/// <summary>
        /// Converts an instance of <see cref="ODataPath" /> into an OData link.
        /// </summary>
        /// <param name="path">The OData path to convert into a link.</param>
        /// <returns>
        /// The generated OData link.
        /// </returns>
        public virtual string Link(ODataPath path)
        {
            if (path == null)
            {
                throw Error.ArgumentNull("path");
            }

            return path.ToString();
        }
        /// <summary>
        /// Matches the current template with an OData path.
        /// </summary>
        /// <param name="path">The OData path to be matched against.</param>
        /// <param name="values">The dictionary of matches to be updated in case of a match.</param>
        /// <returns><c>true</c> in case of a match; otherwise, <c>false</c>.</returns>
        public bool TryMatch(ODataPath path, IDictionary<string, object> values)
        {
            if (path.Segments.Count != Segments.Count)
            {
                return false;
            }

            for (int index = 0; index < Segments.Count; index++)
            {
                if (!Segments[index].TryMatch(path.Segments[index], values))
                {
                    return false;
                }
            }

            return true;
        }
        private static ODataPathTemplate Templatify(ODataPath path, string pathTemplate)
        {
            if (path == null)
            {
                throw new ODataException(Error.Format(SRResources.InvalidODataPathTemplate, pathTemplate));
            }

            List<ODataPathSegmentTemplate> templateSegments = new List<ODataPathSegmentTemplate>();
            foreach (ODataPathSegment pathSegment in path.Segments)
            {
                switch (pathSegment.SegmentKind)
                {
                    case ODataSegmentKinds._Unresolved:
                        throw new ODataException(
                            Error.Format(SRResources.UnresolvedPathSegmentInTemplate, pathSegment.ToString(), pathTemplate));

                    case ODataSegmentKinds._Key:
                        templateSegments.Add(new KeyValuePathSegmentTemplate((KeyValuePathSegment)pathSegment));
                        break;

                    case ODataSegmentKinds._Function:
                        templateSegments.Add(new BoundFunctionPathSegmentTemplate((BoundFunctionPathSegment)pathSegment));
                        break;

                    case ODataSegmentKinds._UnboundFunction:
                        templateSegments.Add(new UnboundFunctionPathSegmentTemplate((UnboundFunctionPathSegment)pathSegment));
                        break;

                    case ODataSegmentKinds._DynamicProperty:
                        templateSegments.Add(new DynamicPropertyPathSegmentTemplate((DynamicPropertyPathSegment)pathSegment));
                        break;

                    default:
                        templateSegments.Add(pathSegment);
                        break;
                }
            }

            return new ODataPathTemplate(templateSegments);
        }
        private static void CheckNavigableProperty(ODataPath path, IEdmModel model)
        {
            Contract.Assert(path != null);
            Contract.Assert(model != null);

            foreach (ODataPathSegment segment in path.Segments)
            {
                NavigationPathSegment navigationPathSegment = segment as NavigationPathSegment;

                if (navigationPathSegment != null)
                {
                    if (EdmLibHelpers.IsNotNavigable(navigationPathSegment.NavigationProperty, model))
                    {
                        throw new ODataException(Error.Format(
                            SRResources.NotNavigablePropertyUsedInNavigation,
                            navigationPathSegment.NavigationProperty.Name));
                    }
                }
            }
        }
        /// <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 new ArgumentNullException(nameof(httpContext));
            }

            if (values == null)
            {
                throw new ArgumentNullException(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;

                // 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(oDataPathValue as string, requestLeftPart, queryString, () => request.CreateRequestContainer(routeName));
                ODataPath path = new ODataPath();
                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;

                        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));
        }
Exemple #6
0
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw Error.ArgumentNull(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;
                }

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

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

                        MergeRouteValues(translatorContext.UpdatedValues, candidate.Values);

                        // Shall we break the remaining candidates?
                        // So far the answer is no. Because we can use this matcher to obsolete the unmatched endpoint.
                        // break;
                    }
                    else
                    {
                        candidates.SetValidity(i, false);
                    }
                }
                catch (Exception)
                {
                }
            }
Exemple #7
0
        private static ODataPath Parse(
            IEdmModel model,
            string serviceRoot,
            string odataPath,
            ODataUriResolverSetttings resolverSettings,
            bool enableUriTemplateParsing)
        {
            ODataUriParser uriParser;
            Uri            serviceRootUri = null;
            Uri            fullUri        = null;

            // TODO: Replace this type.
            //NameValueCollection queryString = null;

            if (enableUriTemplateParsing)
            {
                uriParser = new ODataUriParser(model, new Uri(odataPath, UriKind.Relative));
                uriParser.EnableUriTemplateParsing = true;
            }
            else
            {
                Contract.Assert(serviceRoot != null);

                serviceRootUri = new Uri(
                    serviceRoot.EndsWith("/", StringComparison.Ordinal) ?
                    serviceRoot :
                    serviceRoot + "/");

                fullUri = new Uri(serviceRootUri, odataPath);
                //queryString = fullUri.ParseQueryString();
                uriParser = new ODataUriParser(model, serviceRootUri, fullUri);
            }

            uriParser.Resolver = resolverSettings.CreateResolver();

            Semantic.ODataPath    path;
            UnresolvedPathSegment unresolvedPathSegment = null;

            Semantic.KeySegment id = null;
            try
            {
                path = uriParser.ParsePath();
            }
            catch (ODataUnrecognizedPathException ex)
            {
                if (ex.ParsedSegments != null &&
                    ex.ParsedSegments.Count() > 0 &&
                    (ex.ParsedSegments.Last().EdmType is IEdmComplexType ||
                     ex.ParsedSegments.Last().EdmType is IEdmEntityType) &&
                    ex.CurrentSegment != ODataSegmentKinds.Count)
                {
                    if (ex.UnparsedSegments.Count() == 0)
                    {
                        path = new Semantic.ODataPath(ex.ParsedSegments);
                        unresolvedPathSegment = new UnresolvedPathSegment(ex.CurrentSegment);
                    }
                    else
                    {
                        // Throw ODataException if there is some segment following the unresolved segment.
                        throw new ODataException(Error.Format(
                                                     SRResources.InvalidPathSegment,
                                                     ex.UnparsedSegments.First(),
                                                     ex.CurrentSegment));
                    }
                }
                else
                {
                    throw;
                }
            }

            if (!enableUriTemplateParsing && path.LastSegment is Semantic.NavigationPropertyLinkSegment)
            {
                IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType;

                if (lastSegmentEdmType != null)
                {
                    Semantic.EntityIdSegment entityIdSegment = null;
                    bool exceptionThrown = false;

                    try
                    {
                        entityIdSegment = uriParser.ParseEntityId();

                        if (entityIdSegment != null)
                        {
                            // Create another ODataUriParser to parse $id, which is absolute or relative.
                            ODataUriParser parser = new ODataUriParser(model, serviceRootUri, entityIdSegment.Id);
                            id = parser.ParsePath().LastSegment as Semantic.KeySegment;
                        }
                    }
                    catch (ODataException)
                    {
                        // Exception was thrown while parsing the $id.
                        // We will throw another exception about the invalid $id.
                        exceptionThrown = true;
                    }

                    if (exceptionThrown ||
                        (entityIdSegment != null &&
                         (id == null ||
                          !(id.EdmType.IsOrInheritsFrom(lastSegmentEdmType.ElementType.Definition) ||
                            lastSegmentEdmType.ElementType.Definition.IsOrInheritsFrom(id.EdmType)))))
                    {
                        throw new ODataException(Error.Format(SRResources.InvalidDollarId, /*queryString.Get("$id")*/ "$id"));
                    }
                }
            }

            ODataPath webAPIPath = ODataPathSegmentTranslator.TranslateODLPathToWebAPIPath(
                path,
                model,
                unresolvedPathSegment,
                id,
                enableUriTemplateParsing,
                uriParser.ParameterAliasNodes);

            CheckNavigableProperty(webAPIPath, model);
            return(webAPIPath);
        }
		private static string GetRootElementName(ODataPath path)
		{
			if (path != null)
			{
				ODataPathSegment lastSegment = path.Segments.LastOrDefault();
				if (lastSegment != null)
				{
					BoundActionPathSegment actionSegment = lastSegment as BoundActionPathSegment;
					if (actionSegment != null)
					{
						return actionSegment.Action.Name;
					}

					PropertyAccessPathSegment propertyAccessSegment = lastSegment as PropertyAccessPathSegment;
					if (propertyAccessSegment != null)
					{
						return propertyAccessSegment.Property.Name;
					}
				}
			}
			return null;
		}
		private static bool IsOperationPath(ODataPath path)
		{
			if (path == null)
			{
				return false;
			}

			foreach (ODataPathSegment segment in path.Segments)
			{
				switch (segment.SegmentKind)
				{
					case ODataSegmentKinds._Action:
					case ODataSegmentKinds._Function:
					case ODataSegmentKinds._UnboundAction:
					case ODataSegmentKinds._UnboundFunction:
						return true;
				}
			}

			return false;
		}
Exemple #10
0
        /// <summary>
        /// Gets the property and structured type from <see cref="ODataPath"/>.
        /// TODO: The logic implenetation is not good and do need refactor it later.
        /// </summary>
        /// <param name="path">The OData path.</param>
        /// <returns>The property, structured type and the name.</returns>
        internal static (IEdmProperty, IEdmStructuredType, string) GetPropertyAndStructuredTypeFromPath(this ODataPath path)
        {
            if (path == null)
            {
                return(null, null, string.Empty);
            }

            IEdmStructuredType             structuredType  = null;
            string                         typeCast        = string.Empty;
            IEnumerable <ODataPathSegment> reverseSegments = path.Reverse();

            foreach (var segment in reverseSegments)
            {
                if (segment is NavigationPropertySegment navigationPathSegment)
                {
                    IEdmProperty property = navigationPathSegment.NavigationProperty;
                    if (structuredType == null)
                    {
                        structuredType = navigationPathSegment.NavigationProperty.ToEntityType();
                    }

                    string name = navigationPathSegment.NavigationProperty.Name + typeCast;
                    return(property, structuredType, name);
                }

                if (segment is OperationSegment operationSegment)
                {
                    if (structuredType == null)
                    {
                        structuredType = operationSegment.EdmType as IEdmStructuredType;
                    }

                    string name = operationSegment.Operations.First().FullName() + typeCast;
                    return(null, structuredType, name);
                }

                if (segment is PropertySegment propertyAccessPathSegment)
                {
                    IEdmProperty property = propertyAccessPathSegment.Property;
                    if (structuredType == null)
                    {
                        structuredType = property.Type.GetElementType() as IEdmStructuredType;
                    }

                    string name = property.Name + typeCast;
                    return(property, structuredType, name);
                }

                if (segment is EntitySetSegment entitySetSegment)
                {
                    if (structuredType == null)
                    {
                        structuredType = entitySetSegment.EntitySet.EntityType();
                    }

                    string name = entitySetSegment.EntitySet.Name + typeCast;
                    return(null, structuredType, name);
                }

                if (segment is SingletonSegment singletonSegment)
                {
                    if (structuredType == null)
                    {
                        structuredType = singletonSegment.Singleton.EntityType();
                    }

                    string name = singletonSegment.Singleton.Name + typeCast;
                    return(null, structuredType, name);
                }

                if (segment is TypeSegment typeSegment)
                {
                    structuredType = typeSegment.EdmType.AsElementType() as IEdmStructuredType;
                    typeCast       = "/" + structuredType;
                }
                else if (segment is KeySegment || segment is CountSegment)
                {
                    // do nothing, just go to next segment, what about if meet OperationSegment?
                }
                else
                {
                    // if we meet any other segments, just return (null, null, string.Empty);
                    break;
                }
            }

            return(null, null, string.Empty);
        }