/// <summary> /// Translate the parameter alias, convert node, returned entity set into OData path segment. /// </summary> /// <param name="model">The EDM model</param> /// <param name="path">The odata path segments</param> /// <param name="parameterAliasNodes">The parameter alias</param> /// <returns>The translated odata path segments.</returns> public static IEnumerable <ODataPathSegment> Translate(IEdmModel model, Semantic.ODataPath path, IDictionary <string, SingleValueNode> parameterAliasNodes) { if (model == null) { throw Error.ArgumentNull("model"); } if (path == null) { throw Error.ArgumentNull("path"); } var translator = new ODataPathSegmentTranslator(model, parameterAliasNodes); return(path.WalkWith(translator)); }
private ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer, bool template) { ODataUriParser uriParser; Uri serviceRootUri = null; Uri fullUri = null; IEdmModel model = requestContainer.GetRequiredService <IEdmModel>(); if (template) { uriParser = new ODataUriParser(model, new Uri(odataPath, UriKind.Relative), requestContainer); uriParser.EnableUriTemplateParsing = true; } else { Contract.Assert(serviceRoot != null); serviceRootUri = new Uri( serviceRoot.EndsWith("/", StringComparison.Ordinal) ? serviceRoot : serviceRoot + "/"); // Concatenate the root and path and create a Uri. Using Uri to build a Uri from // a root and relative path changes the casing on .NetCore. However, odataPath may // be a full Uri. if (!Uri.TryCreate(odataPath, UriKind.Absolute, out fullUri)) { fullUri = new Uri(serviceRootUri + odataPath); } // Due to a bug in the System.Uri some relative paths are rejected if they contain // a ':' symbol on a position greater than 1024. This careful check should mitigate // this problem by encoding these characters before the path is combined with // service roor Uri. // https://github.com/dotnet/corefx/issues/29011 if (!Uri.IsWellFormedUriString(odataPath, UriKind.RelativeOrAbsolute) && odataPath.IndexOf(':') > MaxUriSchemeName) { var odataPathColonEncoded = odataPath.Replace(":", "%3A"); if (Uri.IsWellFormedUriString(odataPathColonEncoded, UriKind.Relative)) { odataPath = odataPathColonEncoded; } } fullUri = new Uri(serviceRootUri, odataPath); uriParser = new ODataUriParser(model, serviceRootUri, fullUri, requestContainer); } if (UrlKeyDelimiter != null) { uriParser.UrlKeyDelimiter = UrlKeyDelimiter; } else { uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Slash; } ODL.ODataPath path; UnresolvedPathSegment unresolvedPathSegment = null; ODL.KeySegment id = null; try { path = uriParser.ParsePath(); } catch (ODataUnrecognizedPathException ex) { if (ex.ParsedSegments != null && ex.ParsedSegments.Any() && (ex.ParsedSegments.Last().EdmType is IEdmComplexType || ex.ParsedSegments.Last().EdmType is IEdmEntityType) && ex.CurrentSegment != ODataSegmentKinds.Count) { if (!ex.UnparsedSegments.Any()) { path = new ODL.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 (!template && path.LastSegment is ODL.NavigationPropertyLinkSegment) { IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType; if (lastSegmentEdmType != null) { ODL.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, requestContainer); id = parser.ParsePath().LastSegment as ODL.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))))) { // System.Net.Http on NetCore does not have the Uri extension method // ParseQueryString(), to avoid a platform-specific call, extract $id manually. string idValue = fullUri.Query; string idParam = "$id="; int start = idValue.IndexOf(idParam, StringComparison.OrdinalIgnoreCase); if (start >= 0) { int end = idValue.IndexOf("&", start, StringComparison.OrdinalIgnoreCase); if (end >= 0) { idValue = idValue.Substring(start + idParam.Length, end - 1); } else { idValue = idValue.Substring(start + idParam.Length); } } throw new ODataException(Error.Format(SRResources.InvalidDollarId, idValue)); } } } // do validation for the odata path path.WalkWith(new DefaultODataPathValidator(model)); // do segment translator (for example parameter alias, key & function parameter template, etc) var segments = ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList(); if (unresolvedPathSegment != null) { segments.Add(unresolvedPathSegment); } if (!template) { AppendIdForRef(segments, id); } return(new ODataPath(segments) { Path = path }); }
/// <summary> /// Parse the odata path. /// </summary> /// <param name="model">The model to use for path parsing.</param> /// <param name="serviceRoot">The service root of the OData path.</param> /// <param name="odataPath">The OData path to parse.</param> /// <param name="template">The flag indicates whether the path is template or not.</param> /// <param name="serviceProvider">The service proivder.</param> /// <param name="httpContext"></param> /// <param name="resolverSettings"></param> /// <returns>A parsed representation of the path, or <c>null</c> if the path does not match the model.</returns> public static ODataPath Parse(IEdmModel model, string serviceRoot, string odataPath, bool template, IServiceProvider serviceProvider, ODataUriResolverSettings resolverSettings) { ODL.ODataUriParser uriParser; Uri serviceRootUri = null; Uri fullUri = null; // TODO: Replace this type. //NameValueCollection queryString = null; ODataOptions options = serviceProvider.GetRequiredService <IOptions <ODataOptions> >().Value; if (template) { uriParser = new ODL.ODataUriParser(model, new Uri(odataPath, UriKind.Relative), serviceProvider); 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 ODL.ODataUriParser(model, serviceRootUri, fullUri, serviceProvider); } uriParser.Resolver = resolverSettings.CreateResolver(); if (options.UrlKeyDelimiter != null) { uriParser.UrlKeyDelimiter = options.UrlKeyDelimiter; } else { // ODL changes to use ODataUrlKeyDelimiter.Slash as default value. // Web API still uses the ODataUrlKeyDelimiter.Parentheses as default value. // Please remove it after fix: https://github.com/OData/odata.net/issues/642 uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Parentheses; } ODL.ODataPath path; UnresolvedPathSegment unresolvedPathSegment = null; ODL.KeySegment id = null; try { path = uriParser.ParsePath(); } catch (ODL.ODataUnrecognizedPathException ex) { if (ex.ParsedSegments != null && ex.ParsedSegments.Any() && (ex.ParsedSegments.Last().EdmType is IEdmComplexType || ex.ParsedSegments.Last().EdmType is IEdmEntityType) && ex.CurrentSegment != ODataSegmentKinds.Count) { if (!ex.UnparsedSegments.Any()) { path = new ODL.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 (!template && path.LastSegment is ODL.NavigationPropertyLinkSegment) { IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType; if (lastSegmentEdmType != null) { ODL.EntityIdSegment entityIdSegment = null; bool exceptionThrown = false; try { entityIdSegment = uriParser.ParseEntityId(); if (entityIdSegment != null) { // Create another ODataUriParser to parse $id, which is absolute or relative. ODL.ODataUriParser parser = new ODL.ODataUriParser(model, serviceRootUri, entityIdSegment.Id); id = parser.ParsePath().LastSegment as ODL.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, "$id" /*queryString.Get("$id")*/)); } } } // do validation for the odata path path.WalkWith(new DefaultODataPathValidator(model)); // do segment translator (for example parameter alias, key & function parameter template, etc) var segments = ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList(); if (unresolvedPathSegment != null) { segments.Add(unresolvedPathSegment); } if (!template) { AppendIdForRef(segments, id); } return(new ODataPath(segments) { ODLPath = path }); }
/// <summary> /// Computes the <see cref="IEdmNavigationSource"/> of the resource identified by this <see cref="ODataPath"/>. /// </summary> /// <param name="path">Path to compute the set for.</param> /// <returns>The <see cref="IEdmNavigationSource"/> of the resource, or null if the path does not identify a /// resource that is part of a set.</returns> public static IEdmNavigationSource NavigationSource(this ODataPath path) { return(path.LastSegment.TranslateWith(new DetermineNavigationSourceTranslator())); }
/// <summary> /// Computes whether or not the resource identified by this <see cref="ODataPath"/> is a collection. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <returns>True if the resource if a resource set or collection of primitive or complex types. False otherwise.</returns> public static bool IsCollection(this ODataPath path) { return(path.LastSegment.TranslateWith(new IsCollectionTranslator())); }
/// <summary> /// Get the string representation of <see cref="ODataPath"/>. /// mainly translate Query Url path. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <param name="urlKeyDelimiter">Mark whether key is segment</param> /// <returns>The string representation of the Query Url path.</returns> public static string ToResourcePathString(this ODataPath path, ODataUrlKeyDelimiter urlKeyDelimiter) { return(string.Concat(path.WalkWith(new PathSegmentToResourcePathTranslator(urlKeyDelimiter)).ToArray()).TrimStart('/')); }
/// <summary> /// Computes the <see cref="IEdmTypeReference"/> of the resource identified by this <see cref="ODataPath"/>. /// </summary> /// <param name="path">Path to compute the type for.</param> /// <returns>The <see cref="IEdmTypeReference"/> of the resource, or null if the path does not identify a /// resource with a type.</returns> public static IEdmTypeReference EdmType(this ODataPath path) { return(path.LastSegment.EdmType.ToTypeReference()); }
/// <summary> /// Computes whether or not the ODataPath targets at an unknown segment. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <returns>True if the the ODataPath targets at an unknown segment. False otherwise.</returns> public static bool IsUndeclared(this ODataPath path) { ODataPathSegment lastNonTypeCastSegment = path.TrimEndingTypeSegment().LastSegment; return(lastNonTypeCastSegment is DynamicPathSegment); }
/// <summary> /// Get the string representation of <see cref="ODataPath"/>. /// mainly translate Context Url path. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <returns>The string representation of the Context Url path.</returns> public static string ToContextUrlPathString(this ODataPath path) { StringBuilder pathString = new StringBuilder(); PathSegmentToContextUrlPathTranslator pathTranslator = PathSegmentToContextUrlPathTranslator.DefaultInstance; ODataPathSegment priorSegment = null; bool foundOperationWithoutPath = false; foreach (ODataPathSegment segment in path) { OperationSegment operationSegment = segment as OperationSegment; OperationImportSegment operationImportSegment = segment as OperationImportSegment; if (operationImportSegment != null) { IEdmOperationImport operationImport = operationImportSegment.OperationImports.FirstOrDefault(); Debug.Assert(operationImport != null); EdmPathExpression pathExpression = operationImport.EntitySet as EdmPathExpression; if (pathExpression != null) { Debug.Assert(priorSegment == null); // operation import is always the first segment? pathString.Append(pathExpression.Path); } else { pathString = operationImport.Operation.ReturnType != null ? new StringBuilder(operationImport.Operation.ReturnType.FullName()) : new StringBuilder("Edm.Untyped"); foundOperationWithoutPath = true; } } else if (operationSegment != null) { IEdmOperation operation = operationSegment.Operations.FirstOrDefault(); Debug.Assert(operation != null); if (operation.IsBound && priorSegment != null && operation.Parameters.First().Type.Definition == priorSegment.EdmType) { if (operation.EntitySetPath != null) { foreach (string pathSegment in operation.EntitySetPath.PathSegments.Skip(1)) { pathString.Append('/'); pathString.Append(pathSegment); } } else if (operationSegment.EntitySet != null) { // Is it correct to check EntitySet? pathString = new StringBuilder(operationSegment.EntitySet.Name); } else { pathString = operation.ReturnType != null ? new StringBuilder(operation.ReturnType.FullName()) : new StringBuilder("Edm.Untyped"); foundOperationWithoutPath = true; } } } else { if (foundOperationWithoutPath) { pathString = new StringBuilder(segment.EdmType.FullTypeName()); foundOperationWithoutPath = false; } else { pathString.Append(segment.TranslateWith(pathTranslator)); } } priorSegment = segment; } return(pathString.ToString().TrimStart('/')); }
/// <summary> /// Constructor for ODataQueryOptionParser /// </summary> /// <param name="model">Model to use for metadata binding.</param> /// <param name="odataPath">The odata path to apply the query option on.</param> /// <param name="queryOptions">The dictionary storing query option key-value pairs.</param> public ODataQueryOptionParser(IEdmModel model, ODataPath odataPath, IDictionary <string, string> queryOptions) : this(model, odataPath, queryOptions, null) { }
/// <summary> /// Computes whether or not the ODataPath targets at an individual property. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <returns>True if the the ODataPath targets at an individual property. False otherwise.</returns> public static bool IsIndividualProperty(this ODataPath path) { ODataPathSegment lastNonTypeCastSegment = path.TrimEndingTypeSegment().LastSegment; return(lastNonTypeCastSegment is PropertySegment || lastNonTypeCastSegment is DynamicPathSegment); }
/// <summary> /// Build a segment representing a property. /// </summary> /// <param name="path">Path to perform the computation on.</param> /// <param name="property">The property this segment represents.</param> /// <returns>>A new ODataPath with property segment appended to the end.</returns> public static ODataPath AddPropertySegment(this ODataPath path, IEdmStructuralProperty property) { PropertySegment propertySegment = new PropertySegment(property); return(path.AddSegment(propertySegment)); }
/// <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 targeted by this navigation property. This can be null.</param> /// <returns>A new ODataPath with navigation property segment appended to the end.</returns> public static ODataPath AddNavigationPropertySegment(this ODataPath path, IEdmNavigationProperty navigationProperty, IEdmNavigationSource navigationSource) { NavigationPropertySegment navigationSegment = new NavigationPropertySegment(navigationProperty, navigationSource); return(path.AddSegment(navigationSegment)); }
private static void AppendLastSegment(SplitEndingSegmentOfTypeHandler <TypeSegment> handler, ODataPath newPath) { newPath.AddRange(handler.LastPart); }
public override ODataPath Parse(string serviceRoot, string odataPath, IServiceProvider requestContainer) { ODL.ODataUriParser uriParser; Uri serviceRootUri = null; Uri fullUri = null; string dataSourceName = odataPath.Split('/')[0]; IEdmModel model = requestContainer.GetService(typeof(IEdmModel)) as IEdmModel; Contract.Assert(serviceRoot != null); serviceRootUri = new Uri( serviceRoot.EndsWith("/", StringComparison.Ordinal) ? serviceRoot : serviceRoot + "/"); // Concatenate the root and path and create a Uri. Using Uri to build a Uri from // a root and relative path changes the casing on .NetCore. However, odataPath may // be a full Uri. if (!Uri.TryCreate(odataPath, UriKind.Absolute, out fullUri)) { fullUri = new Uri(serviceRootUri + odataPath); } serviceRootUri = new Uri(serviceRootUri, dataSourceName); uriParser = new ODL.ODataUriParser(model, serviceRootUri, fullUri, requestContainer); if (UrlKeyDelimiter != null) { uriParser.UrlKeyDelimiter = UrlKeyDelimiter; } else { // ODL changes to use ODataUrlKeyDelimiter.Slash as default value. // Web API still uses the ODataUrlKeyDelimiter.Parentheses as default value. // Please remove it after fix: https://github.com/OData/odata.net/issues/642 uriParser.UrlKeyDelimiter = ODataUrlKeyDelimiter.Parentheses; } ODL.ODataPath path; UnresolvedPathSegment unresolvedPathSegment = null; ODL.KeySegment id = null; try { path = uriParser.ParsePath(); } catch (ODL.ODataUnrecognizedPathException ex) { if (ex.ParsedSegments != null && ex.ParsedSegments.Any() && (ex.ParsedSegments.Last().EdmType is IEdmComplexType || ex.ParsedSegments.Last().EdmType is IEdmEntityType) && ex.CurrentSegment != ODataSegmentKinds.Count) { if (!ex.UnparsedSegments.Any()) { path = new ODL.ODataPath(ex.ParsedSegments); unresolvedPathSegment = new UnresolvedPathSegment(ex.CurrentSegment); } else { // Throw ODataException if there is some segment following the unresolved segment. throw new ODataException(String.Format(CultureInfo.CurrentCulture, "InvalidPathSegment", ex.UnparsedSegments.First(), ex.CurrentSegment)); } } else { throw; } } if (path.LastSegment is ODL.NavigationPropertyLinkSegment) { IEdmCollectionType lastSegmentEdmType = path.LastSegment.EdmType as IEdmCollectionType; if (lastSegmentEdmType != null) { ODL.EntityIdSegment entityIdSegment = null; bool exceptionThrown = false; try { entityIdSegment = uriParser.ParseEntityId(); if (entityIdSegment != null) { // Create another ODataUriParser to parse $id, which is absolute or relative. ODL.ODataUriParser parser = new ODL.ODataUriParser(model, serviceRootUri, entityIdSegment.Id, requestContainer); id = parser.ParsePath().LastSegment as ODL.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))))) { // System.Net.Http on NetCore does not have the Uri extension method // ParseQueryString(), to avoid a platform-specific call, extract $id manually. string idValue = fullUri.Query; string idParam = "$id="; int start = idValue.IndexOf(idParam, StringComparison.OrdinalIgnoreCase); if (start >= 0) { int end = idValue.IndexOf("&", start, StringComparison.OrdinalIgnoreCase); if (end >= 0) { idValue = idValue.Substring(start + idParam.Length, end - 1); } else { idValue = idValue.Substring(start + idParam.Length); } } throw new ODataException(String.Format(CultureInfo.CurrentCulture, "InvalidDollarId", idValue)); } } } // do validation for the odata path path.WalkWith(new DefaultODataPathValidator(model)); // do segment translator (for example parameter alias, key & function parameter template, etc) var segments = ODataPathSegmentTranslator.Translate(model, path, uriParser.ParameterAliasNodes).ToList(); if (unresolvedPathSegment != null) { segments.Add(unresolvedPathSegment); } AppendIdForRef(segments, id); return(new ODataPath(segments)); }
/// <summary> /// Adds a range of segments to the current path /// </summary> /// <param name="oDataPath"></param> internal void AddRange(ODataPath oDataPath) { this.segments.AddRange(oDataPath.segments); }