/// <summary> /// Creates a list of path segments for the specified URI query. /// </summary> /// <param name="queryUri">The absolute URI of the request.</param> /// <param name="serviceBaseUri">The service base URI for the request.</param> /// <returns>The <see cref="QueryToken"/> representing the query path.</returns> private QueryToken CreatePathSegments(Uri queryUri, Uri serviceBaseUri) { Debug.Assert(queryUri != null, "queryUri != null"); Debug.Assert(serviceBaseUri != null, "serviceBaseUri != null"); //// This method is a no-metadata, lexical only copy of RequestUriProcessor.CreateSegments List <string> uriSegments = this.EnumerateSegments(queryUri, serviceBaseUri); Debug.Assert(uriSegments.Count <= this.maxDepth, "uriSegments.Count <= this.maxDepth"); // The segment before the one being processed SegmentQueryToken parentNode = null; foreach (string uriSegment in uriSegments) { Debug.Assert(uriSegment != null, "uriSegment != null"); string segmentIdentifier; bool hasKeyValues = ExtractSegmentIdentifier(uriSegment, out segmentIdentifier); string keyValuesString = hasKeyValues ? uriSegment.Substring(segmentIdentifier.Length) : null; SegmentQueryToken segment = new SegmentQueryToken() { Name = segmentIdentifier, Parent = parentNode, NamedValues = hasKeyValues ? ParseKeyValues(keyValuesString) : null }; parentNode = segment; } return(parentNode); }
/// <summary> /// Create a new SegmentQueryToken given the name and parent and namedValues if any /// </summary> /// <param name="name">The name of the segment, the identifier.</param> /// <param name="parent">The parent segment, or null if this is the root segment.</param> /// <param name="namedValues">The named values in the key lookup for this segment.</param> public SegmentQueryToken(string name, SegmentQueryToken parent, IEnumerable<NamedValue> namedValues) { // We allow an "empty" name segment so we can create one for the case of a service-document URL (which has no path) ExceptionUtils.CheckArgumentNotNull(name, "name"); this.name = name; this.parent = parent; this.namedValues = namedValues == null ? null : new ReadOnlyEnumerable<NamedValue>(namedValues); }
/// <summary> /// Parses the <paramref name="queryUri"/> and returns a new instance of <see cref="QueryToken"/> /// describing the query path specified by the URI. /// </summary> /// <param name="queryUri">The absolute URI which holds the query to parse. This must be a path relative to the <paramref name="serviceBaseUri"/>.</param> /// <param name="serviceBaseUri">The base URI of the service.</param> /// <returns>A new instance of <see cref="QueryToken"/> which represents the query path specified in the <paramref name="queryUri"/>. /// The result QueryToken is just a lexical representation.</returns> internal QueryToken ParseUri(Uri queryUri, Uri serviceBaseUri) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(queryUri != null, "queryUri != null"); Debug.Assert(serviceBaseUri != null, "serviceBaseUri != null"); QueryToken path = this.CreatePathSegments(queryUri, serviceBaseUri); if (path == null) { // No segments were found - the service document query // The lexical representation of the service document is an identifier with an empty name path = new SegmentQueryToken(string.Empty, null, null); } return path; }
/// <summary> /// Parses the <paramref name="queryUri"/> and returns a new instance of <see cref="QueryToken"/> /// describing the query path specified by the URI. /// </summary> /// <param name="queryUri">The absolute URI which holds the query to parse. This must be a path relative to the <paramref name="serviceBaseUri"/>.</param> /// <param name="serviceBaseUri">The base URI of the service.</param> /// <returns>A new instance of <see cref="QueryToken"/> which represents the query path specified in the <paramref name="queryUri"/>. /// The result QueryToken is just a lexical representation.</returns> internal QueryToken ParseUri(Uri queryUri, Uri serviceBaseUri) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(queryUri != null, "queryUri != null"); Debug.Assert(serviceBaseUri != null, "serviceBaseUri != null"); QueryToken path = this.CreatePathSegments(queryUri, serviceBaseUri); if (path == null) { // No segments were found - the service document query // The lexical representation of the service document is an identifier with an empty name path = new SegmentQueryToken(string.Empty, null, null); } return(path); }
public static string GetOperationName(Uri requestUri, Uri baseAddress) { try { // remove the query part as they are irrelevant here and we dont want to fail parsing them. Uri requestUriWithoutQuerypart = new Uri(requestUri.GetLeftPart(UriPartial.Path)); SyntacticTree syntacticTree = SyntacticTree.ParseUri(requestUriWithoutQuerypart, baseAddress); SegmentQueryToken lastSegment = syntacticTree.Path as SegmentQueryToken; if (lastSegment != null && !String.IsNullOrEmpty(lastSegment.Name)) { return(lastSegment.Name); } } catch (Exception) { } return(null); }
/// <summary> /// Write the descriptor path token as URI part to this builder. /// </summary> /// <param name="segment">To write as URI part.</param> protected virtual void WriteSegment(SegmentQueryToken segment) { ExceptionUtils.CheckArgumentNotNull(segment, "segment"); // If this desriptor have no path, it is a service-document Url, so just quit if (string.IsNullOrEmpty(segment.Name)) { return; } if (segment.Parent != null) { this.WriteSegment(segment.Parent); this.builder.Append(ExpressionConstants.SymbolForwardSlash); } this.builder.Append(segment.Name); if (segment.NamedValues != null) { this.builder.Append(ExpressionConstants.SymbolOpenParen); bool needComma = false; foreach (NamedValue nv in segment.NamedValues) { if (needComma) { this.builder.Append(ExpressionConstants.SymbolComma); } this.builder.Append(nv.Name); this.builder.Append(ExpressionConstants.SymbolEqual); this.WriteLiteral(nv.Value); needComma = true; } this.builder.Append(ExpressionConstants.SymbolClosedParen); } }
/// <summary> /// Create a new StarQueryToken given the parent (if any). /// </summary> /// <param name="keyword">The keyword kind of the segment.</param> /// <param name="parent">The parent segment, or null if this is the root segment.</param> public KeywordSegmentQueryToken(KeywordKind keyword, SegmentQueryToken parent) : base(QueryTokenUtils.GetNameFromKeywordKind(keyword), parent, null) { this.keyword = keyword; }
protected virtual QueryNode BindNonRootSegment(SegmentQueryToken segmentToken) { var parent = Bind(segmentToken.Parent); if (parent is SingleValueQueryNode) { var parentType = (parent as SingleValueQueryNode).TypeReference.Definition; if (parentType is IEdmEntityType) { var parentEntityType = parentType as IEdmEntityType; var targetProperty = parentEntityType.Properties().FirstOrDefault(p => p.Name.Equals(segmentToken.Name)); if (targetProperty != null) { if (targetProperty is IEdmNavigationProperty) { var targetNavProperty = targetProperty as IEdmNavigationProperty; return new NavigationPropertyNode { NavigationProperty = targetNavProperty, Source = parent }; } if (targetProperty is IEdmStructuralProperty) { return new PropertyAccessQueryNode { Source = parent as SingleValueQueryNode, Property = targetProperty }; } } } } throw new NotImplementedException(); }
/// <summary> /// Need to revert this. /// </summary> /// <param name="segmentToken">Need to revert this.</param> /// <returns>Need to revert this.</returns> protected virtual QueryNode BindSegment(SegmentQueryToken segmentToken) { ExceptionUtils.CheckArgumentNotNull(segmentToken, "segmentToken"); ExceptionUtils.CheckArgumentNotNull(segmentToken.Name, "segmentToken.Name"); if (segmentToken.Parent == null) { return this.BindRootSegment(segmentToken); } else { return this.BindNonRootSegment(segmentToken); // TODO: return this.BindNonRootSegment(segmentToken); throw new NotImplementedException(); } }
/// <summary> /// Binds a service operation segment. /// </summary> /// <param name="segmentToken">The segment which represents a service operation.</param> /// <param name="serviceOperation">The service operation to bind.</param> /// <returns>The bound node.</returns> private QueryNode BindServiceOperation(SegmentQueryToken segmentToken, IEdmFunctionImport serviceOperation) { Debug.Assert(segmentToken != null, "segmentToken != null"); Debug.Assert(serviceOperation != null, "serviceOperation != null"); Debug.Assert(segmentToken.Name == serviceOperation.Name, "The segment represents a different service operation."); //// This is a metadata copy of the RequestUriProcessor.CreateSegmentForServiceOperation //// The WCF DS checks the verb in this place, we can't do that here // All service operations other than those returning IQueryable MUST NOT have () appended to the URI // V1/V2 behavior: if it's IEnumerable<T>, we do not allow () either, because it's not further composable. ODataServiceOperationResultKind? resultKind = serviceOperation.GetServiceOperationResultKind(this.model); if (resultKind != ODataServiceOperationResultKind.QueryWithMultipleResults && segmentToken.NamedValues != null) { throw new ODataException(Strings.MetadataBinder_NonQueryableServiceOperationWithKeyLookup(segmentToken.Name)); } if (!resultKind.HasValue) { throw new ODataException(Strings.MetadataBinder_ServiceOperationWithoutResultKind(serviceOperation.Name)); } IEnumerable<QueryNode> serviceOperationParameters = this.BindServiceOperationParameters(serviceOperation); switch (resultKind.Value) { case ODataServiceOperationResultKind.QueryWithMultipleResults: if (serviceOperation.ReturnType.TypeKind() != EdmTypeKind.Entity) { throw new ODataException(Strings.MetadataBinder_QueryServiceOperationOfNonEntityType(serviceOperation.Name, resultKind.Value.ToString(), serviceOperation.ReturnType.ODataFullName())); } CollectionServiceOperationQueryNode collectionServiceOperationQueryNode = new CollectionServiceOperationQueryNode() { ServiceOperation = serviceOperation, Parameters = serviceOperationParameters }; if (segmentToken.NamedValues != null) { return this.BindKeyValues(collectionServiceOperationQueryNode, segmentToken.NamedValues); } else { return collectionServiceOperationQueryNode; } case ODataServiceOperationResultKind.QueryWithSingleResult: if (!serviceOperation.ReturnType.IsODataEntityTypeKind()) { throw new ODataException(Strings.MetadataBinder_QueryServiceOperationOfNonEntityType(serviceOperation.Name, resultKind.Value.ToString(), serviceOperation.ReturnType.ODataFullName())); } return new SingleValueServiceOperationQueryNode() { ServiceOperation = serviceOperation, Parameters = serviceOperationParameters }; case ODataServiceOperationResultKind.DirectValue: if (serviceOperation.ReturnType.IsODataPrimitiveTypeKind()) { // Direct primitive values are composable, $value is allowed on them. return new SingleValueServiceOperationQueryNode() { ServiceOperation = serviceOperation, Parameters = serviceOperationParameters }; } else { // Direct non-primitive values are not composable at all return new UncomposableServiceOperationQueryNode() { ServiceOperation = serviceOperation, Parameters = serviceOperationParameters }; } case ODataServiceOperationResultKind.Enumeration: case ODataServiceOperationResultKind.Void: // Enumeration and void service operations are not composable return new UncomposableServiceOperationQueryNode() { ServiceOperation = serviceOperation, Parameters = serviceOperationParameters }; default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.MetadataBinder_BindServiceOperation)); } }
/// <summary> /// Binds a root path segment. /// </summary> /// <param name="segmentToken">The segment to bind.</param> /// <returns>The bound node.</returns> private QueryNode BindRootSegment(SegmentQueryToken segmentToken) { Debug.Assert(segmentToken != null, "segmentToken != null"); Debug.Assert(segmentToken.Parent == null, "Only root segments should be allowed here."); Debug.Assert(!string.IsNullOrEmpty(segmentToken.Name), "!string.IsNullOrEmpty(segmentToken.Name)"); //// This is a metadata-only version of the RequestUriProcessor.CreateFirstSegment. if (segmentToken.Name == UriQueryConstants.MetadataSegment) { // TODO: $metadata segment parsing - no key values are allowed. throw new NotImplementedException(); } if (segmentToken.Name == UriQueryConstants.BatchSegment) { // TODO: $batch segment parsing - no key values are allowed. throw new NotImplementedException(); } // TODO: WCF DS checks for $count here first and fails. But not for the other $ segments. // which means other $segments get to SO resolution. On the other hand the WCF DS would eventually fail if an SO started with $. // Look for a service operation IEdmFunctionImport serviceOperation = this.model.TryResolveServiceOperation(segmentToken.Name); if (serviceOperation != null) { return this.BindServiceOperation(segmentToken, serviceOperation); } // TODO: Content-ID reference resolution. Do we actually do anything here or do we perform this through extending the metadata binder? // Look for an entity set. IEdmEntitySet entitySet = this.model.TryResolveEntitySet(segmentToken.Name); if (entitySet == null) { throw new ODataException(Strings.MetadataBinder_RootSegmentResourceNotFound(segmentToken.Name)); } EntitySetQueryNode entitySetQueryNode = new EntitySetQueryNode() { EntitySet = entitySet }; if (segmentToken.NamedValues != null) { return this.BindKeyValues(entitySetQueryNode, segmentToken.NamedValues); } return entitySetQueryNode; }
/// <summary> /// Binds a <see cref="SegmentQueryToken"/>. /// </summary> /// <param name="segmentToken">The segment token to bind.</param> /// <returns>The bound node.</returns> protected virtual QueryNode BindSegment(SegmentQueryToken segmentToken) { ExceptionUtils.CheckArgumentNotNull(segmentToken, "segmentToken"); ExceptionUtils.CheckArgumentStringNotNullOrEmpty(segmentToken.Name, "segmentToken.Name"); if (segmentToken.Parent == null) { return this.BindRootSegment(segmentToken); } else { return BindNonRootSegment(segmentToken); } }
/// <summary> /// Creates a list of path segments for the specified URI query. /// </summary> /// <param name="queryUri">The absolute URI of the request.</param> /// <param name="serviceBaseUri">The service base URI for the request.</param> /// <returns>The <see cref="QueryToken"/> representing the query path.</returns> private QueryToken CreatePathSegments(Uri queryUri, Uri serviceBaseUri) { Debug.Assert(queryUri != null, "queryUri != null"); Debug.Assert(serviceBaseUri != null, "serviceBaseUri != null"); //// This method is a no-metadata, lexical only copy of RequestUriProcessor.CreateSegments List<string> uriSegments = this.EnumerateSegments(queryUri, serviceBaseUri); Debug.Assert(uriSegments.Count <= this.maxDepth, "uriSegments.Count <= this.maxDepth"); // The segment before the one being processed SegmentQueryToken parentNode = null; foreach (string uriSegment in uriSegments) { Debug.Assert(uriSegment != null, "uriSegment != null"); string segmentIdentifier; bool hasKeyValues = ExtractSegmentIdentifier(uriSegment, out segmentIdentifier); string keyValuesString = hasKeyValues ? uriSegment.Substring(segmentIdentifier.Length) : null; SegmentQueryToken segment = new SegmentQueryToken() { Name = segmentIdentifier, Parent = parentNode, NamedValues = hasKeyValues ? ParseKeyValues(keyValuesString) : null }; parentNode = segment; } return parentNode; }