/// <summary> /// Puts given payload in an operation for a changeset. /// </summary> /// <typeparam name="T"> Must be a PayloadTestDescriptor.</typeparam> /// <param name="payload">Payload to be put into changeset operation.</param> /// <param name="verb">Verb associated with payload.</param> /// <param name="baseUri">Baseuri for operation.</param> /// <param name="requestManager">RequestManager to build the request</param> /// <param name="contentType">The (optional) content type to be used for the operation content.</param> /// <returns>IHttpRequest containing payload with specified verb and uri</returns> public static ODataRequest InRequestOperation <T>( this T payload, HttpVerb verb, ODataUri baseUri, IODataRequestManager requestManager, string contentType = null) where T : PayloadTestDescriptor { ExceptionUtilities.CheckArgumentNotNull(payload, "payload"); ExceptionUtilities.CheckArgumentNotNull(verb, "verb"); ExceptionUtilities.CheckArgumentNotNull(baseUri, "baseUri"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); var headers = new Dictionary <string, string> { { "GivenPayloadRequestHeader", "PayloadHeaderValue" } }; var request = requestManager.BuildRequest(baseUri, verb, headers); if (payload.PayloadElement != null) { if (string.IsNullOrEmpty(contentType)) { contentType = payload.PayloadElement.GetDefaultContentType(); } contentType = HttpUtilities.BuildContentType(contentType, Encoding.UTF8.WebName, null); request.Headers.Add(Microsoft.OData.Core.ODataConstants.ContentTypeHeader, contentType); request.Body = requestManager.BuildBody(contentType, baseUri, payload.PayloadElement); } return(request); }
/// <summary> /// Puts payload in a batch response. /// </summary> /// <typeparam name="T">T must be a PayloadTestDescriptor.</typeparam> /// <param name="originalPayload">Payload to be inserted into batch.</param> /// <param name="statusCode">Status code associated with payload</param> /// <param name="random">Use of random makes this method non deterministic</param> /// <param name="requestManager">Used to construct the response payload.</param> /// <param name="inChangeset">Specifies whether this is in a changeset or an operation.</param> /// <param name="operationsBefore">Number of operations/changesets to go before payload.</param> /// <param name="operationsAfter">Number of operations/changesets to go after payload.</param> /// <param name="version">Maximum version for extra generated payloads</param> /// <returns>Test descriptor for the new BatchResponsePayload.</returns> public static T InBatchResponse <T>( this T originalPayload, int statusCode, IRandomNumberGenerator random, IODataRequestManager requestManager, bool inChangeset = false, int operationsBefore = 0, int operationsAfter = 0, ODataVersion version = ODataVersion.V4 ) where T : PayloadTestDescriptor { ExceptionUtilities.CheckArgumentNotNull(originalPayload, "originalPayload"); ExceptionUtilities.CheckArgumentNotNull(statusCode, "statusCode"); ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); var payload = (T)originalPayload.Clone(); var headers = new Dictionary <string, string> { { "ResponseHeader", "ResponseValue" } }; var baseUri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org/service.svc"))); IMimePart[] extraoperations = BatchUtils.ExtraResponseChangesets(random, (EdmModel)payload.PayloadEdmModel, version); // add extraChangesets methods extraoperations.Concat(BatchUtils.ExtraResponseOperations(random, (EdmModel)payload.PayloadEdmModel, version)); //Work out the operations and changesets to go before the payload var parts = Enumerable.Range(0, operationsBefore).Select(i => random.ChooseFrom(extraoperations)); if (!inChangeset) { extraoperations.ConcatSingle(payload.InResponseOperation(statusCode, requestManager)); } else { extraoperations.ConcatSingle(payload.InResponseChangeset(statusCode, random, requestManager, random.NextFromRange(0, 2), random.NextFromRange(0, 2), version)); } parts.Concat(Enumerable.Range(0, operationsAfter).Select(i => random.ChooseFrom(extraoperations))); var batchResponse = new BatchResponsePayload(); foreach (var part in parts) { HttpResponseData operation = part as HttpResponseData; if (operation != null) { batchResponse.Add(operation.AsBatchFragment()); } BatchResponseChangeset changeset = part as BatchResponseChangeset; if (changeset != null) { batchResponse.Add(changeset); } } //payload.PayloadEdmModel.Fixup(); payload.PayloadElement = batchResponse; return(payload); }
/// <summary> /// Generates extra request changesets. /// </summary> /// <param name="random">For generating arbitrary changesets.</param> /// <param name="requestManager">For building changesets.</param> /// <param name="model">For adding any generated types to.</param> /// <param name="baseUri">Base uri for the changesets.</param> /// <param name="version">Maximum version of any generated types.</param> /// <returns>An array of request changesets.</returns> public static BatchRequestChangeset[] ExtraRequestChangesets( IRandomNumberGenerator random, IODataRequestManager requestManager, EdmModel model, ODataUri baseUri, ODataVersion version = ODataVersion.V4 ) { ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); ExceptionUtilities.CheckArgumentNotNull(baseUri, "baseUri"); var headers = new Dictionary <string, string> { { "RequestHeader", "RequestHeaderValue" } }; string mergeContentType = HttpUtilities.BuildContentType(MimeTypes.ApplicationXml, Encoding.UTF8.WebName, null); var requests = ExtraRequestChangesetOperations(random, requestManager, model, baseUri, version); List <BatchRequestChangeset> changesets = new List <BatchRequestChangeset>(); for (int x = 0; x < 5; ++x) { IEnumerable <IHttpRequest> operations = Enumerable.Range(0, random.Next(10)).Select(i => random.ChooseFrom(requests)); changesets.Add(BatchPayloadBuilder.RequestChangeset("changeset_" + Guid.NewGuid().ToString(), "", operations.ToArray())); } return(changesets.ToArray()); }
/// <summary> /// Puts the specified <paramref name="payload"/> into a changeset. /// </summary> /// <param name="payload">The payload to be used as content for the expanded link.</param> /// <param name="verb">The verb associated with the payload.</param> /// <param name="random">Use of random makes this method non deterministic.</param> /// <param name="requestManager">Used to construct requests</param> /// <param name="operationsBefore">Number of extra operations before payload.</param> /// <param name="operationsAfter">Number of extra operations after payload.</param> /// <param name="version">Highest version of allowed features</param> /// <returns>An entry payload with an expanded link that contains the specified <paramref name="payload"/>.</returns> public static BatchRequestChangeset InRequestChangeset <T>( this T payload, HttpVerb verb, IRandomNumberGenerator random, IODataRequestManager requestManager, int operationsBefore = 0, int operationsAfter = 0, ODataVersion version = ODataVersion.V4 ) where T : PayloadTestDescriptor { ExceptionUtilities.CheckArgumentNotNull(payload, "payload"); ExceptionUtilities.CheckArgumentNotNull(verb, "verb"); ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); var baseUri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org/service.svc"))); var extraOperations = BatchUtils.ExtraRequestChangesetOperations(random, requestManager, (EdmModel)payload.PayloadEdmModel, baseUri, version); // Build the list of all properties IEnumerable <IHttpRequest> operations = Enumerable.Range(0, operationsBefore).Select(i => random.ChooseFrom(extraOperations)); operations.ConcatSingle(payload.InRequestOperation(verb, baseUri, requestManager)); operations = operations.Concat(Enumerable.Range(0, operationsAfter).Select(i => extraOperations[extraOperations.Length - 1 - (i % extraOperations.Length)])); var changeset = BatchPayloadBuilder.RequestChangeset("changeset_" + Guid.NewGuid().ToString(), Encoding.UTF8.WebName, operations.ToArray()); return(changeset); }
/// <summary> /// Generates extra operations to go into a request changeset /// </summary> /// <param name="random">For generating the payloads to go in the extra operations</param> /// <param name="requestManager">For building the operations</param> /// <param name="model">To add any new types to.</param> /// <param name="baseUri">Base uri for the extra operations.</param> /// <param name="version">Maximum version for </param> /// <returns>An array of extra request operations.</returns> public static IHttpRequest[] ExtraRequestChangesetOperations( IRandomNumberGenerator random, IODataRequestManager requestManager, EdmModel model, ODataUri baseUri, ODataVersion version = ODataVersion.V4) { ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); ExceptionUtilities.CheckArgumentNotNull(baseUri, "baseUri"); var headers = new Dictionary <string, string> { { "RequestHeader", "RequestHeaderValue" } }; string mergeContentType = HttpUtilities.BuildContentType(MimeTypes.ApplicationXml, Encoding.UTF8.WebName, null); List <IHttpRequest> requests = new List <IHttpRequest>(); ODataRequest request = null; for (int i = 0; i < 4; i++) { request = requestManager.BuildRequest(baseUri, HttpVerb.Post, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Put, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Patch, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Delete, headers); requests.Add(request); } return(requests.ToArray()); }
/// <summary> /// Generates extra operations to go into a request changeset /// </summary> /// <param name="random">For generating the payloads to go in the extra operations</param> /// <param name="requestManager">For building the operations</param> /// <param name="model">To add any new types to.</param> /// <param name="baseUri">Base uri for the extra operations.</param> /// <param name="version">Maximum version for </param> /// <returns>An array of extra request operations.</returns> public static IHttpRequest[] ExtraRequestChangesetOperations( IRandomNumberGenerator random, IODataRequestManager requestManager, EdmModel model, ODataUri baseUri, ODataVersion version = ODataVersion.V4) { ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); ExceptionUtilities.CheckArgumentNotNull(baseUri, "baseUri"); var headers = new Dictionary<string, string> { { "RequestHeader", "RequestHeaderValue" } }; string mergeContentType = HttpUtilities.BuildContentType(MimeTypes.ApplicationXml, Encoding.UTF8.WebName, null); List<IHttpRequest> requests = new List<IHttpRequest>(); ODataRequest request = null; for (int i = 0; i < 4; i++) { request = requestManager.BuildRequest(baseUri, HttpVerb.Post, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Put, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Patch, headers); request.Body = requestManager.BuildBody(mergeContentType, baseUri, RandomPayloadBuilder.GetRandomPayload(random, model, version)); requests.Add(request); request = requestManager.BuildRequest(baseUri, HttpVerb.Delete, headers); requests.Add(request); } return requests.ToArray(); }
/// <summary> /// Puts payload in a batch request. /// </summary> /// <typeparam name="T">T must be a PayloadTestDescriptor.</typeparam> /// <param name="originalPayload">Payload to put into batch.</param> /// <param name="verb">HttpVerb associated with payload.</param> /// <param name="random">Use of random makes this method non deterministic.</param> /// <param name="requestManager">Used to construct requests.</param> /// <param name="operationsBefore">Number of operations/changesets to go before payload.</param> /// <param name="operationsAfter">Number of operations/changesets to go after payload.</param> /// <param name="version">Maximum version for extra payloads</param> /// <returns>The test descriptor for the new BatchRequestPayload</returns> public static T InBatchRequest <T>( this T originalPayload, HttpVerb verb, IRandomNumberGenerator random, IODataRequestManager requestManager, int operationsBefore = 0, int operationsAfter = 0, ODataVersion version = ODataVersion.V4 ) where T : PayloadTestDescriptor { ExceptionUtilities.CheckArgumentNotNull(originalPayload, "originalPayload"); ExceptionUtilities.CheckArgumentNotNull(verb, "verb"); ExceptionUtilities.CheckArgumentNotNull(random, "random"); ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); var payload = (T)originalPayload.Clone(); var baseUri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org/service.svc"))); IMimePart[] extraOperations = BatchUtils.ExtraRequestChangesets(random, requestManager, (EdmModel)payload.PayloadEdmModel, baseUri, version); // add extraChangesets methods extraOperations.Concat(BatchUtils.ExtraRequestChangesets(random, requestManager, (EdmModel)payload.PayloadEdmModel, baseUri, version)); //Work out the operations and changesets to go before the payload var parts = Enumerable.Range(0, operationsBefore).Select(i => random.ChooseFrom(extraOperations)); if (verb != HttpVerb.Get) { extraOperations.ConcatSingle(payload.InRequestOperation(HttpVerb.Get, baseUri, requestManager)); } else { extraOperations.ConcatSingle(payload.InRequestChangeset(verb, random, requestManager, random.NextFromRange(0, 2), random.NextFromRange(0, 2), version)); } parts.Concat(Enumerable.Range(0, operationsAfter).Select(i => random.ChooseFrom(extraOperations))); var batchRequest = new BatchRequestPayload(); foreach (var part in parts) { IHttpRequest operation = part as IHttpRequest; if (operation != null) { batchRequest.Add(operation.AsBatchFragment()); } BatchRequestChangeset changeset = part as BatchRequestChangeset; if (changeset != null) { batchRequest.Add(changeset); } } //payload.PayloadEdmModel.Fixup(); payload.PayloadElement = batchRequest; return(payload); }
/// <summary> /// Returns the query option values with a strict sorting /// </summary> /// <param name="uri">The current uri</param> /// <returns>The uri's query options after sorting</returns> protected override IEnumerable<KeyValuePair<string, string>> GetSortedQueryOptions(ODataUri uri) { List<KeyValuePair<string, string>> queryOptions = new List<KeyValuePair<string, string>>(); // NOTE: this is set up partly to match the order the product uses for next-links. We may need to make this more customizable. queryOptions.AddRange(uri.CustomQueryOptions); if (uri.Filter != null) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Filter, uri.Filter)); } if (uri.ExpandSegments.Count > 0) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Expand, this.ConcatenateSegments(uri.ExpandSegments))); } if (uri.OrderBy != null) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.OrderBy, uri.OrderBy)); } if (uri.InlineCount != null) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.InlineCount, uri.InlineCount)); } if (uri.SelectSegments.Count > 0) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Select, this.ConcatenateSegments(uri.SelectSegments))); } if (uri.Top.HasValue) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Top, uri.Top.Value.ToString(CultureInfo.InvariantCulture))); } if (uri.Skip.HasValue) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Skip, uri.Skip.Value.ToString(CultureInfo.InvariantCulture))); } if (uri.SkipToken != null) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.SkipToken, uri.SkipToken)); } if (uri.Format != null) { queryOptions.Add(new KeyValuePair<string, string>(QueryOptions.Format, uri.Format)); } return queryOptions; }
/// <summary> /// Converts an ODataBatchOperation to the corresponding IMimePart. /// </summary> /// <param name="operation">The operation to convert.</param> /// <returns>The converted operation.</returns> private IMimePart ConvertBatchOperation(ODataBatchOperation operation) { ODataPayloadElement payloadElement = operation.Payload == null ? null : this.Visit(operation.Payload); if (this.response) { ODataBatchResponseOperation responseOperation = (ODataBatchResponseOperation)operation; HttpResponseData httpResponse = new HttpResponseData { StatusCode = (HttpStatusCode)responseOperation.StatusCode, }; if (responseOperation.Headers != null) { foreach (var kvp in responseOperation.Headers) { httpResponse.Headers.Add(kvp.Key, kvp.Value); } } ; return(new ODataResponse(httpResponse) { RootElement = payloadElement }); } ODataBatchRequestOperation requestOperation = (ODataBatchRequestOperation)operation; // NOTE: this is abusing the ODataUriBuilder but is sufficient for our purposes // We use Unrecognized because the request URI may be relative (in the case of a reference) ODataUri requestUri = new ODataUri(ODataUriBuilder.Unrecognized(requestOperation.Url.OriginalString)); ODataRequest request = this.requestManager.BuildRequest( requestUri, (HttpVerb)Enum.Parse(typeof(HttpVerb), requestOperation.HttpMethod, /*ignoreCase*/ true), requestOperation.Headers); if (payloadElement != null) { string contentType; if (!request.Headers.TryGetValue(Microsoft.OData.ODataConstants.ContentTypeHeader, out contentType)) { throw new InvalidOperationException("ContentType header not found."); } request.Body = this.requestManager.BuildBody(contentType, requestUri, payloadElement); } return(request); }
/// <summary> /// Gets the expected options for the given content type and version /// </summary> /// <param name="contentType">The content type of the payload</param> /// <param name="version">The current version</param> /// <param name="payloadUri">The payload URI</param> /// <returns>The payload options for the given content type and version</returns> public ODataPayloadOptions GetExpectedPayloadOptions(string contentType, DataServiceProtocolVersion version, ODataUri payloadUri) { ExceptionUtilities.CheckArgumentNotNull(contentType, "contentType"); ExceptionUtilities.Assert(version != DataServiceProtocolVersion.Unspecified, "Version cannot be unspecified"); var expected = expectedPayloadOptions; if (contentType.StartsWith(MimeTypes.ApplicationJsonODataLightNonStreaming, System.StringComparison.Ordinal) || contentType.StartsWith(MimeTypes.ApplicationJsonODataLightStreaming, System.StringComparison.Ordinal)) { expected = ODataPayloadOptions.IncludeTypeNames | ODataPayloadOptions.IncludeMediaResourceSourceLinks | ODataPayloadOptions.IncludeMediaResourceEditLinks | ODataPayloadOptions.IncludeNamedMediaResourceSourceLinks | ODataPayloadOptions.IncludeNamedMediaResourceEditLinks | ODataPayloadOptions.IncludeEntityIdentifier | ODataPayloadOptions.ConventionallyProducedNamedStreamSelfLink; var selectedPropertyNames = ODataUtilities.GetSelectedPropertyNamesFromUri(payloadUri, this.UriConverter).ToList(); if (selectedPropertyNames.Any()) { EntitySet payloadEntitySet = null; EntityType payloadEntityType = null; if (payloadUri.TryGetExpectedEntitySetAndType(out payloadEntitySet, out payloadEntityType) && !ODataUtilities.ContainsAllIdentityPropertyNames(selectedPropertyNames, payloadEntityType)) { // JSON Light projections without identity do not contain enough metadata to deduce // id and link values. expected = ODataPayloadOptions.IncludeTypeNames; } } } else if (version < DataServiceProtocolVersion.V4) { // Type names for null values are only supported in V1 and V2. expected = expected | ODataPayloadOptions.IncludeTypeNamesForNullValues; } return expected; }
/// <summary> /// Returns the strategy to use for serializing/deserialzing the given content type /// </summary> /// <param name="contentType">The content type</param> /// <param name="uri">The request uri</param> /// <returns>A serialization strategy</returns> public virtual IProtocolFormatStrategy GetStrategy(string contentType, ODataUri uri) { if (uri != null) { // if its a named stream or an MLE, handle the returned payload as a binary stream if (uri.IsNamedStream() || uri.IsMediaResource()) { return this.BinaryValueStrategy; } // if its a raw $count request, we need to use a different strategy if (uri.IsCount() && IsPlainTextMimeType(contentType)) { return this.CountStrategy; } } if (IsXmlMimeType(contentType)) { return this.XmlStrategy; } if (IsJsonMimeType(contentType)) { return this.JsonStrategy; } if (IsTextBasedMimeType(contentType)) { return this.TextValueStrategy; } if (IsHtmlFormMimeType(contentType)) { return this.HtmlFormStrategy; } return this.BinaryValueStrategy; }
/// <summary> /// Validates the data in the response payload based on the expected query value /// </summary> /// <param name="requestUri">The request uri</param> /// <param name="response">The response</param> /// <param name="expected">The expected query value</param> /// <param name="maxProtocolVersion">The max protocol version of the service</param> public void ValidateResponsePayload(ODataUri requestUri, ODataResponse response, QueryValue expected, DataServiceProtocolVersion maxProtocolVersion) { ExceptionUtilities.CheckObjectNotNull(requestUri, "requestUri"); ExceptionUtilities.CheckObjectNotNull(response, "response"); ExceptionUtilities.CheckObjectNotNull(expected, "expected"); var expectedVersion = response.GetDataServiceVersion(); this.Validator.ExpectedProtocolVersion = expectedVersion; this.Validator.ExpectedPayloadOptions = ODataPayloadOptions.None; string contentType; if (response.Headers.TryGetValue(HttpHeaders.ContentType, out contentType)) { var strategy = this.FormatSelector.GetStrategy(contentType, requestUri); ExceptionUtilities.CheckObjectNotNull(strategy, "Could not get strategy for content type '{0}'", contentType); this.Validator.PrimitiveValueComparer = strategy.GetPrimitiveComparer(); this.Validator.ExpectedPayloadOptions = this.ProtocolImplementationDetails.GetExpectedPayloadOptions(contentType, expectedVersion, requestUri); } this.ValidateAndPrintInfoOnError(requestUri, response.RootElement, expected); }
/// <summary> /// Returns whether or not the uri refers to an entity set (with or without an EntityType Segment) or collection navigation property /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri refers to an entity set or collection navigation property</returns> public static bool IsEntitySet(this ODataUri uri) { if (uri.Segments.Count == 0) { return(false); } if (uri.IsEntityReferenceLink()) { return(false); } if (uri.LastSegment.SegmentType == ODataUriSegmentType.EntitySet) { return(true); } if (uri.LastSegment.SegmentType == ODataUriSegmentType.NavigationProperty) { return(((NavigationSegment)uri.LastSegment).NavigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many); } if (uri.LastSegment.SegmentType == ODataUriSegmentType.EntityType) { return(new ODataUri(uri.Segments.Take(uri.Segments.Count - 1)).IsEntitySet()); } if (uri.LastSegment.SegmentType == ODataUriSegmentType.Function) { var collectionType = ((FunctionSegment)uri.LastSegment).Function.ReturnType as CollectionDataType; if (collectionType != null) { return(collectionType.ElementDataType is EntityDataType); } } return(false); }
internal static ODataPayloadElementType GetExpectedLinkPayloadType(this ODataUri linkUri) { ExceptionUtilities.CheckArgumentNotNull(linkUri, "linkUri"); NavigationSegment lastNavigation = null; bool keyAfterLastNavigation = false; bool hasLinks = false; foreach (var segment in linkUri.Segments) { if (segment.SegmentType == ODataUriSegmentType.NavigationProperty) { lastNavigation = (NavigationSegment)segment; keyAfterLastNavigation = false; } else if (segment.SegmentType == ODataUriSegmentType.Key) { keyAfterLastNavigation = true; } else if (segment.SegmentType == ODataUriSegmentType.EntityReferenceLinks) { hasLinks = true; } } ExceptionUtilities.Assert(hasLinks, "Uri did not contain the '$ref' segment"); ExceptionUtilities.CheckObjectNotNull(lastNavigation, "Uri did not contain any navigation properties"); if (lastNavigation.NavigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many && !keyAfterLastNavigation) { return(ODataPayloadElementType.LinkCollection); } else { return(ODataPayloadElementType.DeferredLink); } }
/// <summary> /// Annotates the given payload based on the metadata in the given uri /// </summary> /// <param name="rootElement">The payload to annotate with metadata information</param> /// <param name="uri">The uri that corresponds to the given payload</param> public void ResolveMetadata(ODataPayloadElement rootElement, ODataUri uri) { ExceptionUtilities.CheckArgumentNotNull(rootElement, "rootElement"); ExceptionUtilities.CheckArgumentNotNull(uri, "uri"); this.InitializeMetadataStack(uri); this.InitialStackSize = this.MetadataStack.Count; rootElement.Add(new ExpectedPayloadElementTypeAnnotation() { ExpectedType = uri.GetExpectedPayloadType() }); // if the uri did not contain any metadata, do nothing if (this.InitialStackSize > 0) { // if this is the result of service operation or action, the root element needs to have the function itself and its return type var serviceOperation = this.MetadataStack.OfType<Function>().FirstOrDefault(); if (serviceOperation != null) { rootElement.AddAnnotationIfNotExist(new FunctionAnnotation() { Function = serviceOperation }); rootElement.AddAnnotationIfNotExist(new DataTypeAnnotation() { DataType = serviceOperation.ReturnType }); } this.Recurse(rootElement); } }
/// <summary> /// Initializes a new instance of the <see cref="NextLinkValidatingVisitor"/> class. /// </summary> /// <param name="requestUri">The request URI.</param> /// <param name="payloadOptions">The payload options.</param> /// <param name="parent">The parent verifier.</param> public NextLinkValidatingVisitor(ODataUri requestUri, ODataPayloadOptions payloadOptions, NextLinkResponseVerifier parent) { ExceptionUtilities.CheckArgumentNotNull(requestUri, "requestUri"); ExceptionUtilities.CheckArgumentNotNull(parent, "parent"); this.requestUri = requestUri; this.payloadOptions = payloadOptions; this.parent = parent; this.queryValueStack.Push(this.parent.UriEvaluator.Evaluate(requestUri, true, true)); }
/// <summary> /// Returns whether or not the uri ends with a primitive or complex property segment /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri ends with a primitive or complex property segment</returns> public static bool IsProperty(this ODataUri uri) { return(uri.Segments.Count > 0 && uri.LastSegment.IsProperty()); }
/// <summary> /// Returns whether or not the uri ends with a '$count' segment /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri ends with a '$count' segment</returns> public static bool IsCount(this ODataUri uri) { return(uri.Segments.Count > 0 && uri.LastSegment.SegmentType == ODataUriSegmentType.Count); }
/// <summary> /// Determines whether ODataUri has an InlineCount or not /// </summary> /// <param name="uri">OData Uri to look at</param> /// <returns>True or false if there is an inlinecount or not</returns> public static bool IncludesInlineCountAllPages(this ODataUri uri) { string value; return(uri.TryGetInlineCountValue(out value) && value == QueryOptions.InlineCountAllPages); }
private static void SetExpectedEntitySetAndTypeFromUri(ODataPayloadContext payloadContext, ODataUri uri) { EntitySet entitySet = null; EntityType entityType = null; uri.TryGetExpectedEntitySetAndType(out entitySet, out entityType); payloadContext.ExpectedEntitySet = entitySet; payloadContext.ExpectedEntityType = entityType; }
internal static string DetermineAcceptType(ODataUri uri, string defaultAcceptType) { if (uri.IsNamedStream() || uri.IsMediaResource()) { return MimeTypes.Any; } if (uri.IsCount()) { return MimeTypes.TextPlain; } if (uri.IsPropertyValue()) { string propertyAcceptType = MimeTypes.Any; var propertySegment = uri.Segments[uri.Segments.Count - 2] as PropertySegment; ExceptionUtilities.CheckObjectNotNull(propertySegment, "Cannot get Property segment from uri"); var mimeTypeAnnotation = propertySegment.Property.Annotations.OfType<MimeTypeAnnotation>().SingleOrDefault(); if (mimeTypeAnnotation != null) { propertyAcceptType = mimeTypeAnnotation.MimeTypeValue; } return propertyAcceptType; } return defaultAcceptType; }
/// <summary> /// Extracts the names of properties specified in the $select option, if any. /// </summary> /// <param name="uri">The URI to parse.</param> /// <param name="uriToStringConverter">The IODataUriToStringConverter implementation.</param> /// <returns>The list of property names.</returns> public static IEnumerable <string> GetSelectedPropertyNamesFromUri(ODataUri uri, IODataUriToStringConverter uriToStringConverter) { return(uri.SelectSegments.Select(s => uriToStringConverter.ConcatenateSegments(s.Where(s2 => !(s2 is EntityTypeSegment))))); }
internal MimePartData<IHttpRequest> BuildRequestFromPart(MimePartData<byte[]> mimePart, Encoding encoding) { var requestData = CreateRequest(mimePart.Body, encoding); var odataUri = new ODataUri(new UnrecognizedSegment(requestData.Uri.OriginalString)); var odataRequest = this.RequestManager.BuildRequest(odataUri, requestData.Verb, requestData.Headers); string contentType; if (requestData.TryGetHeaderValueIgnoreHeaderCase(HttpHeaders.ContentType, out contentType)) { var formatStrategy = this.FormatSelector.GetStrategy(contentType, odataUri); var deserializer = formatStrategy.GetDeserializer(); var rootElement = deserializer.DeserializeFromBinary(requestData.Body, ODataPayloadContext.BuildPayloadContextFromRequest(odataRequest)); odataRequest.Body = new ODataPayloadBody(requestData.Body, rootElement); } else if (requestData.Body != null) { odataRequest.Body = new ODataPayloadBody(requestData.Body, new PrimitiveValue(null, requestData.Body)); } var rebuiltPart = new MimePartData<IHttpRequest>(); rebuiltPart.Headers.AddRange(mimePart.Headers); rebuiltPart.Body = odataRequest; return rebuiltPart; }
public void BatchReaderMixedEncodingTest() { EdmModel model = new EdmModel(); EdmEntityType personType = model.EntityType("Person") .KeyProperty("Id", EdmCoreModel.Instance.GetInt32(false) as EdmTypeReference) .Property("Name", EdmPrimitiveTypeKind.String, isNullable: true); model.Fixup(); EdmEntitySet personSet = model.EntitySet("Person", personType); EntityInstance personInstance = PayloadBuilder.Entity("TestModel.Person") .Property("Id", PayloadBuilder.PrimitiveValue(1)) .Property("Name", PayloadBuilder.PrimitiveValue("Mr Foo Baz")); ODataUriSegment root = ODataUriBuilder.Root(new Uri("http://www.odata.org")); ODataUri testUri = new ODataUri(root, ODataUriBuilder.EntitySet(personSet)); Encoding[] encodings = new Encoding[] { Encoding.UTF8, Encoding.BigEndianUnicode, Encoding.Unicode }; IEnumerable<BatchReaderMixedEncodingTestCase> testCases = encodings.SelectMany(batchEncoding => encodings.Select(changesetEncoding => new BatchReaderMixedEncodingTestCase { BatchEncoding = batchEncoding, Changesets = new[] { new BatchReaderMixedEncodingChangeset { ChangesetEncoding = changesetEncoding, Operations = new[] { new BatchReaderMixedEncodingOperation { OperationEncoding = Encoding.Unicode, PayloadFormat = ODataFormat.Atom, }, new BatchReaderMixedEncodingOperation { // Uses changeset's encoding PayloadFormat = ODataFormat.Atom, }, }, }, new BatchReaderMixedEncodingChangeset { Operations = new[] { new BatchReaderMixedEncodingOperation { // Uses batch's encoding OperationEncoding = batchEncoding, PayloadFormat = ODataFormat.Atom, }, }, }, }, } )); this.CombinatorialEngineProvider.RunCombinations( testCases, this.ReaderTestConfigurationProvider.DefaultFormatConfigurations, (testCase, testConfiguration) => { var testPayload = personInstance.DeepCopy(); if (!testConfiguration.IsRequest) { testPayload.AddAnnotation(new PayloadFormatVersionAnnotation() { Response = true, ResponseWrapper = true }); } var testDescriptor = this.CreateTestDescriptor(testCase, testPayload, testUri, testConfiguration.IsRequest); testDescriptor.PayloadEdmModel = model; testDescriptor.RunTest(testConfiguration); }); }
/// <summary> /// Generates the raw test message with mixed encodings defined by the test case, as well as the expected ODataPayloadElement. /// </summary> /// <param name="testCase">The test case defining the structure and encodings of the batch payload.</param> /// <param name="payload">The payload to use for all generated batch operations.</param> /// <param name="payloadUri">The URI to use for all generated batch operations.</param> /// <param name="isRequest">If true, generates a batch request, otherwise a batch response.</param> /// <returns>The test descriptor for this test case/configuration.</returns> private BatchReaderMixedEncodingTestDescriptor CreateTestDescriptor(BatchReaderMixedEncodingTestCase testCase, ODataPayloadElement payload, ODataUri payloadUri, bool isRequest) { ExceptionUtilities.Assert(testCase.BatchEncoding != null, "Batch encoding has not been specified."); string batchBoundary = "batch_" + Guid.NewGuid().ToString(); string payloadUriString = this.UriConverter.ConvertToString(payloadUri); var batchPayload = isRequest ? (ODataPayloadElement)new BatchRequestPayload() : (ODataPayloadElement)new BatchResponsePayload(); batchPayload.AddAnnotation(new BatchBoundaryAnnotation(batchBoundary)); var rawMessage = new List<byte>(); // TODO: Batch reader does not support multi codepoint encodings Encoding unsupportedEncoding = AsUnsupportedEncoding(testCase.BatchEncoding); foreach (var changeset in testCase.Changesets) { string changesetBoundary = "change_" + Guid.NewGuid().ToString(); Encoding changesetEncoding = changeset.ChangesetEncoding ?? testCase.BatchEncoding; // TODO: Batch reader does not support multi codepoint encodings unsupportedEncoding = unsupportedEncoding ?? AsUnsupportedEncoding(changesetEncoding); string changesetContentType = HttpUtilities.BuildContentType( MimeTypes.MultipartMixed, changeset.ChangesetEncoding == null ? string.Empty : changeset.ChangesetEncoding.WebName, changesetBoundary); rawMessage.AddRange( WriteMessagePart( testCase.BatchEncoding, (writer) => { writer.Write("--"); writer.WriteLine(batchBoundary); writer.WriteLine(HttpHeaders.ContentType + ": " + changesetContentType); writer.WriteLine(); })); var mimeParts = new List<IMimePart>(); int contentId = 0; foreach (var operation in changeset.Operations) { ExceptionUtilities.Assert(operation.PayloadFormat == ODataFormat.Atom || operation.PayloadFormat == ODataFormat.Json, "Payload format must be ATOM or JSON."); string formatType = MimeTypes.ApplicationAtomXml + ";type=entry"; Encoding payloadEncoding = operation.OperationEncoding ?? changesetEncoding; string payloadContentType = HttpUtilities.BuildContentType( formatType, operation.OperationEncoding == null ? string.Empty : operation.OperationEncoding.WebName, string.Empty); string httpStatus = isRequest ? "POST " + payloadUriString + " HTTP/1.1" : "HTTP/1.1 201 Created"; rawMessage.AddRange( WriteMessagePart( changesetEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.WriteLine(changesetBoundary); writer.WriteLine(HttpHeaders.ContentType + ": application/http"); writer.WriteLine(HttpHeaders.ContentTransferEncoding + ": binary"); writer.WriteLine(HttpHeaders.ContentId + ": " + (++contentId).ToString()); writer.WriteLine(); writer.WriteLine(httpStatus); writer.WriteLine(HttpHeaders.ContentType + ": " + payloadContentType); writer.WriteLine(); })); IPayloadSerializer payloadSerializer = operation.PayloadFormat == ODataFormat.Atom ? (IPayloadSerializer)new XmlPayloadSerializer(this.PayloadElementToXmlConverter) : (IPayloadSerializer)new JsonPayloadSerializer(this.PayloadElementToJsonConverter.ConvertToJson); byte[] payloadBytes = payloadSerializer.SerializeToBinary(payload, payloadEncoding.WebName); rawMessage.AddRange(payloadBytes.Skip(payloadEncoding.GetPreamble().Length)); if (isRequest) { var request = this.RequestManager.BuildRequest(payloadUri, HttpVerb.Post, new Dictionary<string, string> { { HttpHeaders.ContentType, payloadContentType } }); request.Body = new ODataPayloadBody(payloadBytes, payload); mimeParts.Add(request); } else { var httpResponseData = new HttpResponseData { StatusCode = HttpStatusCode.Created, }; httpResponseData.Headers.Add(HttpHeaders.ContentType, payloadContentType); var response = new ODataResponse(httpResponseData) { Body = payloadBytes, RootElement = payload }; mimeParts.Add(response); } } rawMessage.AddRange( WriteMessagePart( changesetEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.Write(changesetBoundary); writer.WriteLine("--"); })); if (isRequest) { ((BatchRequestPayload)batchPayload).Add(BatchPayloadBuilder.RequestChangeset(changesetBoundary, changesetEncoding.WebName, mimeParts.ToArray())); } else { ((BatchResponsePayload)batchPayload).Add(BatchPayloadBuilder.ResponseChangeset(changesetBoundary, changesetEncoding.WebName, mimeParts.ToArray())); } } rawMessage.AddRange( WriteMessagePart( testCase.BatchEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.Write(batchBoundary); writer.WriteLine("--"); })); return new BatchReaderMixedEncodingTestDescriptor(this.PayloadReaderSettings) { BatchContentTypeHeader = HttpUtilities.BuildContentType(MimeTypes.MultipartMixed, testCase.BatchEncoding.WebName, batchBoundary), RawMessage = rawMessage.ToArray(), PayloadElement = batchPayload, ExpectedException = unsupportedEncoding == null ? null : ODataExpectedExceptions.ODataException("ODataBatchReaderStream_MultiByteEncodingsNotSupported", unsupportedEncoding.WebName) }; }
/// <summary> /// Verifies the delete succeeded /// </summary> /// <param name="request">The request to verify</param> /// <param name="response">The response to verify</param> public override void Verify(ODataRequest request, ODataResponse response) { base.Verify(request, response); var originalUri = request.Uri; var entityUri = originalUri.ScopeToEntity(); var beforeSync = this.Evaluator.Evaluate(entityUri, false, false) as QueryStructuralValue; ExceptionUtilities.CheckObjectNotNull(beforeSync, "Could not get entity before syncing"); ExceptionUtilities.Assert(!beforeSync.IsNull, "Entity was null before syncing"); var afterSync = this.SynchronizeAndEvaluate(entityUri, beforeSync, originalUri.IsEntity()); QueryValue currentValue = afterSync; string message = null; if (originalUri.IsEntity()) { // DELETE Customers(1) message = "Entity was not deleted"; } else if (originalUri.IsPropertyValue()) { // DELETE Customers(1)/Name/$value message = "Property value was not null"; // drill down by each property in the uri after the entity portion foreach (var propertySegment in originalUri.Segments.Skip(entityUri.Segments.Count).OfType<PropertySegment>()) { currentValue = ((QueryStructuralValue)currentValue).GetValue(propertySegment.Property.Name); } } else if (originalUri.IsEntityReferenceLink()) { // TODO: verify back-links? var navigation = originalUri.Segments.OfType<NavigationSegment>().Last(); if (navigation.NavigationProperty.ToAssociationEnd.Multiplicity == EndMultiplicity.Many) { // DELETE Customers(1)/Orders/$ref?$id=Orders(1) message = "Collection link was not deleted"; var linkUri = new ODataUri(entityUri.Segments.Concat(navigation, originalUri.Segments.OfType<KeyExpressionSegment>().Last())); currentValue = this.Evaluator.Evaluate(linkUri, false, false); } else { // DELETE Customers(1)/BestFriend/$ref message = "Reference link was not deleted"; currentValue = ((QueryStructuralValue)afterSync).GetValue(navigation.NavigationProperty.Name); } } // at this point, the current value should be null if the delete was successful ExceptionUtilities.CheckObjectNotNull(message, "Uri did not represent an entity, value, or link: '{0}'", request.GetRequestUri()); this.Assert(currentValue.IsNull, message, request, response); if (originalUri.IsEntity()) { this.RequeryEntityAndVerifyStatusCode(request, response, entityUri); if (entityUri.Segments.OfType<NavigationSegment>().Any()) { // convert the uri into a top-level access entityUri = GetTopLevelUri(beforeSync); entityUri.Segments.Insert(0, originalUri.Segments.OfType<ServiceRootSegment>().Single()); this.RequeryEntityAndVerifyStatusCode(request, response, entityUri); } } }
private void RequeryEntityAndVerifyStatusCode(ODataRequest request, ODataResponse response, ODataUri entityUri) { var getVerificationRequest = this.RequestManager.BuildRequest(entityUri, HttpVerb.Get, new HttpHeaderCollection()); var getVerificationResponse = this.RequestManager.GetResponse(getVerificationRequest); var expectedStatusCode = HttpStatusCode.NotFound; if (entityUri.LastSegment.SegmentType == ODataUriSegmentType.NavigationProperty) { var navigation = (NavigationSegment)entityUri.LastSegment; if (navigation.NavigationProperty.ToAssociationEnd.Multiplicity != EndMultiplicity.Many) { expectedStatusCode = HttpStatusCode.NoContent; } } string message = null; bool succeeded = getVerificationResponse.StatusCode == expectedStatusCode; if (!succeeded) { message = @"Re-querying the deleted entity did not result in correct status code Expected: {0} Actual: {1}"; message = string.Format(CultureInfo.InvariantCulture, message, expectedStatusCode, getVerificationResponse.StatusCode); this.ReportFailure(request, response); } this.Assert(succeeded, message, getVerificationRequest, getVerificationResponse); }
private QueryValue SynchronizeAndEvaluate(ODataUri entityUri, QueryStructuralValue beforeSync, bool synchronizeEntireSet) { var entityType = beforeSync.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Structural value was not an entity type"); // if an entity was deleted, synchronize the entire set. Otherwise just synchronize the entity if (synchronizeEntireSet) { SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntireEntitySet(c, entityType.EntitySet.Name)); } else { SyncHelpers.ExecuteActionAndWait(c => this.Synchronizer.SynchronizeEntity(c, beforeSync)); } return this.Evaluator.Evaluate(entityUri, false, false); }
private IHttpRequest GenerateRequestOperation(ODataResponse batchResponseOperation, EntityModelSchema model) { ExceptionUtilities.CheckArgumentNotNull(batchResponseOperation, "batchResponseOperation"); ExceptionUtilities.CheckArgumentNotNull(model, "model"); var headers = new Dictionary<string, string> { { "GivenPayloadRequestHeader", "PayloadHeaderValue" } }; string mergeContentType = HttpUtilities.BuildContentType(MimeTypes.ApplicationXml, Encoding.UTF8.WebName, null); ODataUri uri = null; HttpVerb verb = HttpVerb.Get; if (batchResponseOperation.RootElement != null) { var complexInstanceCollection = batchResponseOperation.RootElement as ComplexInstanceCollection; if (complexInstanceCollection != null) { var function = batchResponseOperation.RootElement.GetAnnotation<FunctionAnnotation>(); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.ServiceOperation(function.Function)); } var complexMultivalue = batchResponseOperation.RootElement as ComplexMultiValueProperty; if (complexMultivalue != null) { var entityType = complexMultivalue.GetAnnotation<EntityModelTypeAnnotation>().EntityModelType as EntityDataType; var entitySet = model.GetEntitySet(entityType.Definition.Name); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.EntitySet(entitySet), ODataUriBuilder.Property(entityType.Definition, complexMultivalue.Name)); } var complexProperty = batchResponseOperation.RootElement as ComplexProperty; if (complexProperty != null) { var type = complexProperty.GetAnnotation<EntityModelTypeAnnotation>(); var complexDataType = type.EntityModelType as ComplexDataType; // Using first because we don't need a specific entity just one that contains this type. If there is more than one the first works fine. var entityType = model.EntityTypes.Where(et => et.Properties.Where(p => p.Name == complexProperty.Name).Count() == 1).First(); var complexType = model.ComplexTypes.Where(ct => complexDataType.Definition.Name == ct.Name).Single(); var complexPropertyName = entityType.AllProperties.Where(p => { var complex = p.PropertyType as ComplexDataType; if (complex == null) { return false; } return complex.Definition.Name == complexDataType.Definition.Name; }).Single(); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.Property(entityType, complexPropertyName.Name)); } var deferredLink = batchResponseOperation.RootElement as DeferredLink; if (deferredLink != null) { var navigationProperty = deferredLink.GetAnnotation<NavigationPropertyAnnotation>(); var entityType = model.EntityTypes.Where(et => et.Properties.Where(p => p.Name == navigationProperty.Property.Name).Count() == 1).First(); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.Property(entityType, navigationProperty.Property.Name)); } var linkCollection = batchResponseOperation.RootElement as LinkCollection; if (linkCollection != null) { uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), SystemSegment.EntityReferenceLinks); } var entityInstance = batchResponseOperation.RootElement as EntityInstance; if (entityInstance != null) { var type = entityInstance.GetAnnotation<EntityModelTypeAnnotation>().EntityModelType as EntityDataType; var entitySetType = model.GetEntitySet(type.Definition.Name); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.EntitySet(entitySetType)); } var entitySetInstance = batchResponseOperation.RootElement as EntitySetInstance; if (entitySetInstance != null) { var entitySetType = model.GetEntitySet(entityInstance.FullTypeName); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.EntitySet(entitySetType)); } var primitiveCollection = batchResponseOperation.RootElement as PrimitiveCollection; if (primitiveCollection != null) { var function = batchResponseOperation.RootElement.GetAnnotation<FunctionAnnotation>(); uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.ServiceOperation(function.Function)); } var primitiveMultiValueProperty = batchResponseOperation.RootElement as PrimitiveMultiValueProperty; if (primitiveMultiValueProperty != null) { var type = primitiveMultiValueProperty.GetAnnotation<EntityModelTypeAnnotation>(); var entityType = type.EntityModelType as EntityDataType; uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.EntitySet(model.GetEntitySet(entityType.Definition.Name)), ODataUriBuilder.Property(entityType.Definition, primitiveMultiValueProperty.Name)); } var primitiveProperty = batchResponseOperation.RootElement as PrimitiveProperty; if (primitiveProperty != null) { var type = complexProperty.GetAnnotation<EntityModelTypeAnnotation>(); var entityType = type.EntityModelType as EntityDataType; uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.Property(entityType.Definition, primitiveProperty.Name)); } var primitiveValue = batchResponseOperation.RootElement as PrimitiveValue; if (primitiveValue != null) { var primitiveType = primitiveValue.GetAnnotation<DataTypeAnnotation>(); var entityType = primitiveValue.GetAnnotation<EntityModelTypeAnnotation>().EntityModelType as EntityDataType; var propertyName = entityType.Definition.Properties.Where(p=> p.PropertyType == primitiveType.DataType).First().Name; uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org")), ODataUriBuilder.Property(entityType.Definition, propertyName), SystemSegment.Value); } var odataError = batchResponseOperation.RootElement as ODataErrorPayload; if (odataError != null) { uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org"))); } } else { verb = HttpVerb.Put; uri = new ODataUri(ODataUriBuilder.Root(new Uri("http://www.odata.org"))); } ExceptionUtilities.Assert(uri != null, "The request URI has not been defined."); var request = this.RequestManager.BuildRequest(uri, verb, headers); return request; }
private static bool HasETagOnRetrieve(ODataUri uri) { if (uri.ExpandSegments.Any()) { return false; } if (uri.IsEntity()) { return true; } if (uri.IsProperty()) { return true; } if (uri.IsPropertyValue() || uri.IsMediaResource()) { // this covers media-resources as well return true; } if (uri.IsNamedStream()) { return true; } return false; }
/// <summary> /// Generates the next link for an expanded feed. /// </summary> /// <param name="containingEntity">The containing entity.</param> /// <param name="navigation">The expanded navigation property.</param> /// <param name="lastEntityValue">The last entity value.</param> /// <returns> /// The expected next link /// </returns> public string GenerateExpandedNextLink(EntityInstance containingEntity, NavigationPropertyInstance navigation, QueryStructuralValue lastEntityValue) { ExceptionUtilities.CheckArgumentNotNull(containingEntity, "containingEntity"); ExceptionUtilities.CheckArgumentNotNull(navigation, "navigation"); ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue"); var navigationUriString = ((ExpandedLink)navigation.Value).UriString; if (string.IsNullOrEmpty(navigationUriString)) { navigationUriString = UriHelpers.ConcatenateUriSegments(containingEntity.EditLink, navigation.Name); } var skipTokenValues = new List<object>(); foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey)) { skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value); } var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues); var nextLinkUri = new ODataUri(new UnrecognizedSegment(navigationUriString)); nextLinkUri.SkipToken = skiptoken; return this.UriToStringConverter.ConvertToString(nextLinkUri); }
private DataServiceProtocolVersion CalculateExpectedClientMinRequestVersion(ODataUri odataUri, out ExpectedClientErrorBaseline errorIfTooLow) { ExceptionUtilities.CheckArgumentNotNull(odataUri, "odataUri"); DataServiceProtocolVersion expectedVersion = DataServiceProtocolVersion.V4; errorIfTooLow = null; // Uri specific processing if (odataUri.HasAnyOrAllInFilter()) { expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); string anyOrAll = string.Empty; if (odataUri.Filter.Contains("/any(")) { anyOrAll = "Any"; } else { anyOrAll = "All"; } var errorInformation = new ExpectedErrorMessage("ALinq_MethodNotSupportedForMaxDataServiceVersionLessThanX", anyOrAll, expectedVersion.ConvertToHeaderFormat()); errorIfTooLow = new ExpectedClientErrorBaseline(typeof(NotSupportedException), false, errorInformation); } if (odataUri.HasSignificantTypeSegmentInPath()) { // TODO: this seems like it should be inferred from the query, not the uri expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); var errorInformation = new ExpectedErrorMessage("ALinq_MethodNotSupportedForMaxDataServiceVersionLessThanX", "OfType", expectedVersion.ConvertToHeaderFormat()); errorIfTooLow = new ExpectedClientErrorBaseline(typeof(NotSupportedException), false, errorInformation); } else if (odataUri.HasTypeSegmentInExpandOrSelect()) { // TODO: this seems like it should be inferred from the query, not the uri expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); var errorInformation = new ExpectedErrorMessage("ALinq_TypeAsNotSupportedForMaxDataServiceVersionLessThan3"); errorIfTooLow = new ExpectedClientErrorBaseline(typeof(NotSupportedException), false, errorInformation); } return expectedVersion; }
internal static HttpStatusCode GetExpectedStatusCode(ODataUri uri, IODataUriEvaluator evaluator) { bool specialStatusCodeIfNull = false; specialStatusCodeIfNull |= uri.IsNamedStream(); specialStatusCodeIfNull |= uri.IsMediaResource(); specialStatusCodeIfNull |= uri.IsEntity(); specialStatusCodeIfNull |= uri.IsEntityReferenceLink(); specialStatusCodeIfNull |= uri.IsPropertyValue(); bool uriIsValue = uri.IsNamedStream() || uri.IsMediaResource(); // For an action it is evaluated specially via the actionresponse verifier, skip eval here if (!uri.IsAction() && specialStatusCodeIfNull && evaluator.Evaluate(uri).IsNull) { if (uriIsValue) { return HttpStatusCode.NoContent; } else { return HttpStatusCode.NotFound; } } return HttpStatusCode.OK; }
private void InitializeMetadataStack(ODataUri uri) { this.MetadataStack.Clear(); EntitySet currentSet = null; foreach (var segment in uri.Segments) { var setSegment = segment as EntitySetSegment; if (setSegment != null) { this.MetadataStack.Push(setSegment.EntitySet); currentSet = setSegment.EntitySet; continue; } var navigationSegment = segment as NavigationSegment; if (navigationSegment != null) { this.MetadataStack.Push(navigationSegment.NavigationProperty); this.MetadataStack.Push(this.CurrentEntitySet.GetRelatedEntitySet(navigationSegment.NavigationProperty)); currentSet = currentSet.GetRelatedEntitySet(navigationSegment.NavigationProperty); continue; } var propertySegment = segment as PropertySegment; if (propertySegment != null) { this.MetadataStack.Push(propertySegment.Property); continue; } var functionSegment = segment as FunctionSegment; if (functionSegment != null) { EntitySet returningEntitySet; if (this.InitializeFunctionMetadata(functionSegment.Function, currentSet, out returningEntitySet)) { currentSet = returningEntitySet; } continue; } // special segment types if (segment.SegmentType == ODataUriSegmentType.Count) { this.MetadataStack.Push(DataTypes.Integer); } else if (segment.SegmentType == ODataUriSegmentType.Value) { var property = this.MetadataStack.Peek() as MemberProperty; if (property != null) { this.MetadataStack.Push(property.PropertyType); } } } }
public void BatchReaderMixedEncodingTest() { EdmModel model = new EdmModel(); EdmEntityType personType = model.EntityType("Person") .KeyProperty("Id", EdmCoreModel.Instance.GetInt32(false) as EdmTypeReference) .Property("Name", EdmPrimitiveTypeKind.String, isNullable: true); model.Fixup(); EdmEntitySet personSet = model.EntitySet("Person", personType); EntityInstance personInstance = PayloadBuilder.Entity("TestModel.Person") .Property("Id", PayloadBuilder.PrimitiveValue(1)) .Property("Name", PayloadBuilder.PrimitiveValue("Mr Foo Baz")); ODataUriSegment root = ODataUriBuilder.Root(new Uri("http://www.odata.org")); ODataUri testUri = new ODataUri(root, ODataUriBuilder.EntitySet(personSet)); Encoding[] encodings = new Encoding[] { Encoding.UTF8, Encoding.BigEndianUnicode, Encoding.Unicode }; IEnumerable <BatchReaderMixedEncodingTestCase> testCases = encodings.SelectMany(batchEncoding => encodings.Select(changesetEncoding => new BatchReaderMixedEncodingTestCase { BatchEncoding = batchEncoding, Changesets = new[] { new BatchReaderMixedEncodingChangeset { ChangesetEncoding = changesetEncoding, Operations = new[] { new BatchReaderMixedEncodingOperation { OperationEncoding = Encoding.Unicode, PayloadFormat = ODataFormat.Atom, }, new BatchReaderMixedEncodingOperation { // Uses changeset's encoding PayloadFormat = ODataFormat.Atom, }, }, }, new BatchReaderMixedEncodingChangeset { Operations = new[] { new BatchReaderMixedEncodingOperation { // Uses batch's encoding OperationEncoding = batchEncoding, PayloadFormat = ODataFormat.Atom, }, }, }, }, } )); this.CombinatorialEngineProvider.RunCombinations( testCases, this.ReaderTestConfigurationProvider.DefaultFormatConfigurations, (testCase, testConfiguration) => { var testPayload = personInstance.DeepCopy(); if (!testConfiguration.IsRequest) { testPayload.AddAnnotation(new PayloadFormatVersionAnnotation() { Response = true, ResponseWrapper = true }); } var testDescriptor = this.CreateTestDescriptor(testCase, testPayload, testUri, testConfiguration.IsRequest); testDescriptor.PayloadEdmModel = model; testDescriptor.RunTest(testConfiguration); }); }
private IEnumerable<PayloadReaderTestDescriptor> CreateCrossReferenceTestDescriptors(CrossReferenceTestCase testCase, ReaderTestConfiguration testConfiguration) { ExceptionUtilities.CheckArgumentNotNull(testCase, "testCase"); var emptyPayload = new OData.Common.PayloadTestDescriptor() { PayloadEdmModel = new EdmModel().Fixup() }; IEnumerable<OData.Common.PayloadTestDescriptor> operationPayloads = new[] { emptyPayload }; // One of the operations in the test case may specify a reference link value to use to generate payloads string payloadReferenceLink = testCase.ChangeSets.SelectMany(cset => cset.Operations).Select(o => o.PayloadCrossReferenceLink).SingleOrDefault(s => !string.IsNullOrEmpty(s)); if (payloadReferenceLink != null) { EdmModel testModel = Test.OData.Utils.Metadata.TestModels.BuildTestModel(); operationPayloads = GeneratePayloadElementsWithCrossReferenceLinks(payloadReferenceLink, testConfiguration).Select( p => new OData.Common.PayloadTestDescriptor { PayloadElement = p, PayloadEdmModel = testModel, }); } var testDescriptors = new List<PayloadReaderTestDescriptor>(); foreach (var payload in operationPayloads) { IEnumerable<IMimePart> requestChangesets = testCase.ChangeSets.Select( c => (IMimePart)BatchUtils.GetRequestChangeset( c.Operations.Select(o => { // check whether we need to inject a payload into this operation var operationPayload = string.IsNullOrEmpty(o.PayloadCrossReferenceLink) ? emptyPayload : payload; ODataUri operationUri = new ODataUri(new[] { ODataUriBuilder.Unrecognized(o.Uri.OriginalString) }); var requestOperation = operationPayload.InRequestOperation(HttpVerb.Post, operationUri, this.RequestManager); requestOperation.Headers.Add(HttpHeaders.ContentId, o.ContentId); return (IMimePart)requestOperation; }).ToArray(), this.RequestManager)); var testDescriptor = new PayloadReaderTestDescriptor(this.PayloadReaderSettings) { DebugDescription = testCase.DebugDescription, PayloadElement = PayloadBuilder.BatchRequestPayload(requestChangesets.ToArray()).AddAnnotation(new BatchBoundaryAnnotation("batch_foo")), ExpectedException = testCase.ExpectedException, SkipTestConfiguration = (testConfig) => !testConfig.IsRequest, }; testDescriptors.Add(testDescriptor); } return testDescriptors; }
/// <summary> /// Returns whether or not the uri contains a '$ref' segment /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri contains a '$ref' segment</returns> public static bool IsEntityReferenceLink(this ODataUri uri) { return(uri.Segments.Any(s => s.SegmentType == ODataUriSegmentType.EntityReferenceLinks)); }
private ODataUri GenerateEntityIdUri(EntitySet entitySet, EntityType entityType, IEnumerable<NamedValue> keyValues) { ExceptionUtilities.CheckAllRequiredDependencies(this); // null checks performed by ODataUriBuilder var conventionalId = new ODataUri(); conventionalId.Segments.Add(new ServiceRootSegment(this.Service.ServiceUri)); conventionalId.Segments.Add(ODataUriBuilder.EntitySet(entitySet)); conventionalId.Segments.Add(ODataUriBuilder.Key(entityType, keyValues)); return conventionalId; }
/// <summary> /// Returns whether or not the uri ends with a '$value' segment /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri ends with a '$value' segment</returns> public static bool IsPropertyValue(this ODataUri uri) { return(uri.Segments.Count > 1 && uri.LastSegment.SegmentType == ODataUriSegmentType.Value && uri.Segments[uri.Segments.Count - 2].IsProperty()); }
private static bool CheckLastSegment <TSegment>(this ODataUri uri, Func <TSegment, bool> check) where TSegment : ODataUriSegment { return(uri.LastSegment.CheckSegment(check)); }
/// <summary> /// Helper method for converting an OData uri directly to a system Uri /// </summary> /// <param name="converter">The uri-to-string converter to extend</param> /// <param name="uri">The uri to convert</param> /// <returns>The system uri for the given OData uri</returns> public static Uri ConvertToUri(this IODataUriToStringConverter converter, ODataUri uri) { ExceptionUtilities.CheckArgumentNotNull(converter, "converter"); ExceptionUtilities.CheckArgumentNotNull(uri, "uri"); return(new Uri(converter.ConvertToString(uri), UriKind.RelativeOrAbsolute)); }
internal static ODataUri ConstructODataUriWithoutActionInformation(IEnumerable<EntitySet> entitySets, ODataUri uri) { var segmentsToInclude = new List<ODataUriSegment>(); ServiceOperationAnnotation serviceOperationAnnotation = null; string setName = null; foreach (var segment in uri.Segments) { var functionSegment = segment as FunctionSegment; if (functionSegment != null && functionSegment.Function.IsAction()) { serviceOperationAnnotation = functionSegment.Function.Annotations.OfType<ServiceOperationAnnotation>().Single(); var toggleBooleanAnnotation = functionSegment.Function.Annotations.OfType<ToggleBoolPropertyValueActionAnnotation>().SingleOrDefault(); if (toggleBooleanAnnotation != null) { setName = toggleBooleanAnnotation.SourceEntitySet; } break; } segmentsToInclude.Add(segment); } if (!serviceOperationAnnotation.BindingKind.IsBound()) { ExceptionUtilities.CheckObjectNotNull(setName, "Cannot find the set name that the action starts from"); var sourceEntitySet = entitySets.Single(es => es.Name == setName); segmentsToInclude.Add(new EntitySetSegment(sourceEntitySet)); } var actionlessODataUri = new ODataUri(segmentsToInclude); actionlessODataUri.CustomQueryOptions = uri.CustomQueryOptions; actionlessODataUri.Filter = uri.Filter; actionlessODataUri.InlineCount = uri.InlineCount; actionlessODataUri.OrderBy = uri.OrderBy; actionlessODataUri.Skip = uri.Skip; actionlessODataUri.SkipToken = uri.SkipToken; actionlessODataUri.Top = uri.Top; EntitySet expectedEntitySet = null; ExceptionUtilities.Assert(actionlessODataUri.TryGetExpectedEntitySet(out expectedEntitySet), "Expected entity set not found"); // expand all navigations for actionlessODataUri so that we do not need to send additional request when calculating expected action result foreach (NavigationProperty navigation in expectedEntitySet.EntityType.NavigationProperties) { actionlessODataUri.ExpandSegments.Add(new List<ODataUriSegment>() { new NavigationSegment(navigation) }); } return actionlessODataUri; }
/// <summary> /// Uses the content type and uri to get a deserializer, and asserts that the root element is of the expected type /// </summary> /// <typeparam name="TElement">The expected root element type</typeparam> /// <param name="selector">The protocol format selector</param> /// <param name="uri">The uri to use for getting a format strategy</param> /// <param name="contentType">The content type to use for getting a format strategy</param> /// <param name="body">The body to deserialize</param> /// <returns>The root element of the body</returns> public static TElement DeserializeAndCast <TElement>(this IProtocolFormatStrategySelector selector, ODataUri uri, string contentType, byte[] body) where TElement : ODataPayloadElement { ExceptionUtilities.CheckArgumentNotNull(selector, "selector"); ExceptionUtilities.CheckStringArgumentIsNotNullOrEmpty(contentType, "contentType"); ExceptionUtilities.CheckArgumentNotNull(body, "body"); // TODO: force uri to be non-null var strategy = selector.GetStrategy(contentType, uri); ExceptionUtilities.CheckObjectNotNull(strategy, "Could not find protocol strategy for content-type '{0}'.", contentType); var deserializer = strategy.GetDeserializer(); ExceptionUtilities.CheckObjectNotNull(deserializer, "Strategy returned null deserializer"); string charset = HttpUtilities.GetContentTypeCharsetOrNull(contentType); var rootElement = deserializer.DeserializeFromBinary(body, new ODataPayloadContext { EncodingName = charset }); ExceptionUtilities.CheckObjectNotNull(rootElement, "Deserializer returned null element"); var afterCast = rootElement as TElement; ExceptionUtilities.CheckObjectNotNull(afterCast, "Root element was of unexpected type '{0}'. Expected '{1}'", rootElement.ElementType, typeof(TElement).Name); return(afterCast); }
/// <summary> /// Find out whether to expect action descriptor with projection in request uri /// </summary> /// <param name="requestUri">The request uri</param> /// <param name="action">The action</param> /// <param name="isTopLevelElement">Whether the entity being verified is top level payload element</param> /// <returns>Whether to expect action descriptor</returns> private bool ExpectActionWithProjection(ODataUri requestUri, Function action, bool isTopLevelElement) { ODataUriSegmentPathCollection selectSegments = requestUri.SelectSegments; ODataUriSegmentPathCollection expandSegments = requestUri.ExpandSegments; // handle single level $select path, expect action descriptor if $select=ActionName or $select=Container.* foreach (var selectSegmentPath in selectSegments.Where(ss => ss.Count == 1)) { ODataUriSegment selectSegment = selectSegmentPath.Single(); if (isTopLevelElement && this.FuctionMatchWithSelectFunctionSegment(action, selectSegment)) { return true; } if (this.IsSelectAllFunctionSegment(selectSegment)) { return true; } } // handle multiple level $select path, expect action descriptor for $expand=Rating if: $select=Rating or $select=Rating/ActionName or $Select=Rating/Container.* foreach (var expandSegmentPath in expandSegments) { List<ODataUriSegment> expandSegmentList = expandSegmentPath.ToList(); foreach (var selectSegmentPath in selectSegments.Where(ss => ss.Count == expandSegmentPath.Count || ss.Count == expandSegmentPath.Count + 1)) { List<ODataUriSegment> selectSegmentList = selectSegmentPath.ToList(); if (this.FunctionMatchWithExpandSegmentList(selectSegmentList, expandSegmentList, action)) { return true; } } } return false; }
/// <summary> /// Returns whether or not the uri refers to the root of the service /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri refers to the root of the service</returns> public static bool IsServiceDocument(this ODataUri uri) { return(uri.Segments.Count == 1 && uri.LastSegment.SegmentType == ODataUriSegmentType.ServiceRoot); }
/// <summary> /// Generates extra request operations /// </summary> /// <param name="requestManager">RequestManager to build the operations.</param> /// <param name="baseUri">Base uri for the operations</param> /// <returns>An array of request operations.</returns> public static IMimePart[] ExtraRequestOperations(IODataRequestManager requestManager, ODataUri baseUri) { ExceptionUtilities.CheckArgumentNotNull(requestManager, "requestManager"); ExceptionUtilities.CheckArgumentNotNull(baseUri, "baseUri"); var headers = new Dictionary <string, string> { { "RequestHeader", "RequestHeaderValue" } }; string mergeContentType = HttpUtilities.BuildContentType(MimeTypes.ApplicationXml, Encoding.UTF8.WebName, null); List <IHttpRequest> requests = new List <IHttpRequest>(); requests.Add(requestManager.BuildRequest(baseUri, HttpVerb.Get, headers)); var segments = baseUri.Segments; ODataUriSegment[] segmentstoadd = { ODataUriBuilder.EntitySet(new EntitySet("Set1")), ODataUriBuilder.EntityType(new EntityType("EntityType")), ODataUriBuilder.EntitySet(new EntitySet("Set2")), ODataUriBuilder.EntityType(new EntityType("EntityType2")), }; foreach (var segment in segmentstoadd) { requests.Add(requestManager.BuildRequest(new ODataUri(segments.ConcatSingle(segment)), HttpVerb.Get, headers)); } return(requests.ToArray()); }
/// <summary> /// Processes the given uri and produces an expected value according to the conventions of an OData server implementation /// </summary> /// <param name="evaluator">The evaluator</param> /// <param name="uri">The uri to process</param> /// <returns>The value resulting from processing the uri</returns> public static QueryValue Evaluate(this IODataUriEvaluator evaluator, ODataUri uri) { ExceptionUtilities.CheckArgumentNotNull(evaluator, "evaluator"); return evaluator.Evaluate(uri, true, true); }
/// <summary> /// Calculates the protocol version based on the ODataUri provided /// </summary> /// <param name="uri">OData Uri to analyze</param> /// <param name="contentType">Content Type</param> /// <param name="maxProtocolVersion">The max protocol version</param> /// <param name="dataServiceVersion">The data service version of the request</param> /// <param name="maxDataServiceVersion">The max data service version of the request</param> /// <returns>Data Service Protocol Version</returns> public DataServiceProtocolVersion CalculateProtocolVersion(ODataUri uri, string contentType, DataServiceProtocolVersion maxProtocolVersion, DataServiceProtocolVersion dataServiceVersion, DataServiceProtocolVersion maxDataServiceVersion) { ExceptionUtilities.CheckArgumentNotNull(uri, "uri"); ExceptionUtilities.Assert(maxProtocolVersion != DataServiceProtocolVersion.Unspecified, "Max protocol version cannot be unspecified"); if (uri.IsMetadata()) { throw new TaupoNotSupportedException("Context uri should be processed by Entity Model Version Calculator instead"); } DataServiceProtocolVersion expectedVersion = DataServiceProtocolVersion.V4; if (uri.IncludesInlineCountAllPages()) { expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); } if (uri.IsCount()) { expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); } else if (uri.IsEntityReferenceLink() || uri.IsNamedStream() || uri.IsPropertyValue()) { // If the uri points to a Link, $value or a named stream their response DSV's are V1 because their payloads are understood by V1 expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, DataServiceProtocolVersion.V4); } else if (uri.HasOpenProperties()) { expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, VersionHelper.GetMinimumVersion(maxProtocolVersion, maxDataServiceVersion)); } else if (uri.IsProperty()) { var propertySegment = (PropertySegment)uri.LastSegment; expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, VersionHelper.CalculateProtocolVersion(propertySegment.Property)); } else if (uri.IsAction()) { var action = (FunctionSegment)uri.LastSegment; if (action.Function.ReturnType != null) { var collectionReturnType = action.Function.ReturnType as CollectionDataType; // If its a collection this doesn't bump things to V3 as this is normal behavior from ServiceOperations V1, so only determining version based on non data type DataType versionType = action.Function.ReturnType; var entityVersionType = action.Function.ReturnType as EntityDataType; if (collectionReturnType != null) { entityVersionType = collectionReturnType.ElementDataType as EntityDataType; if (entityVersionType != null) { versionType = entityVersionType; } } // Bump if its an open type if (entityVersionType != null && entityVersionType.Definition.IsOpen) { expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, VersionHelper.GetMinimumVersion(maxProtocolVersion, maxDataServiceVersion)); } expectedVersion = VersionHelper.IncreaseVersionIfRequired(expectedVersion, VersionHelper.CalculateDataTypeVersion(versionType)); } } else { ExceptionUtilities.CheckArgumentNotNull(contentType, "contentType"); List<DataServiceProtocolVersion> dataServiceProtocolVersions = uri.GetAllEntitySetsIncludingExpands().Select(es => es.CalculateEntitySetProtocolVersion(contentType, VersionCalculationType.Response, maxProtocolVersion, maxDataServiceVersion)).ToList(); dataServiceProtocolVersions.Add(expectedVersion); expectedVersion = VersionHelper.GetMaximumVersion(dataServiceProtocolVersions.ToArray()); } // determine if the uri MIGHT result in there being a next link, and increase version if so expectedVersion = VersionHelper.IncreaseVersionIfTrue(ResponseMightIncludeNextLink(uri), expectedVersion, DataServiceProtocolVersion.V4); return expectedVersion; }
private string CalculateExpectedETagForEntityOrStream(ODataUri uri, QueryStructuralValue entity) { if (uri.IsMediaResource()) { return entity.GetDefaultStreamValue().GetExpectedETag(); } if (uri.IsNamedStream()) { var streamSegment = uri.Segments.OfType<NamedStreamSegment>().Last(); return entity.GetStreamValue(streamSegment.Name).GetExpectedETag(); } return this.LiteralConverter.ConstructWeakETag(entity); }
/// <summary> /// Returns whether or not the uri refers to a function call /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri refers to a function call</returns> public static bool IsFunction(this ODataUri uri) { return(uri.CheckLastSegment <FunctionSegment>(f => f.Function.IsFunction()) || uri.CheckLastTwoSegments <FunctionSegment, ParametersExpressionSegment>(f => f.Function.IsFunction(), p => true)); }
/// <summary> /// Generates the expected next link for a top-level feed /// </summary> /// <param name="requestUri">The request URI.</param> /// <param name="pageSize">The page size.</param> /// <param name="lastEntityValue">The last entity value.</param> /// <returns> /// The expected next link /// </returns> public string GenerateNextLink(ODataUri requestUri, int pageSize, QueryStructuralValue lastEntityValue) { ExceptionUtilities.CheckArgumentNotNull(requestUri, "requestUri"); ExceptionUtilities.CheckArgumentNotNull(lastEntityValue, "lastEntityValue"); var queryBasedUri = requestUri as QueryBasedODataUri; ExceptionUtilities.CheckObjectNotNull(queryBasedUri, "Only uris which were generated from queries are supported"); var skipTokenValues = new List<object>(); foreach (var keySelector in queryBasedUri.OrderByExpressions.SelectMany(o => o.KeySelectors)) { skipTokenValues.Add(this.EvaluateLambdaForEntity(lastEntityValue, keySelector)); } foreach (var keyProperty in lastEntityValue.Type.Properties.Where(p => p.IsPrimaryKey)) { skipTokenValues.Add(lastEntityValue.GetScalarValue(keyProperty.Name).Value); } var skiptoken = this.BuildSkipTokenFromValues(skipTokenValues); // copy request uri segments exactly var nextLinkUri = new ODataUri(requestUri.Segments); // copy over $orderby, $filter, $expand, $select, and $inlinecount nextLinkUri.OrderBy = ModifyQueryOptionToMatchProduct(requestUri.OrderBy); nextLinkUri.Filter = ModifyQueryOptionToMatchProduct(requestUri.Filter); nextLinkUri.ExpandSegments = requestUri.ExpandSegments; nextLinkUri.SelectSegments = requestUri.SelectSegments; string inlineCountValue; if (requestUri.TryGetInlineCountValue(out inlineCountValue)) { nextLinkUri.InlineCount = inlineCountValue; } // add the $skiptoken generated above nextLinkUri.SkipToken = skiptoken; // generate a new $top value if (requestUri.Top.HasValue) { var top = requestUri.Top.Value - pageSize; if (top > 0) { nextLinkUri.Top = top; } } if (requestUri.IsServiceOperation()) { var functionSegment = requestUri.Segments.OfType<FunctionSegment>().Last(); foreach (var paramName in functionSegment.Function.Parameters.Select(p => p.Name)) { string paramValue; if (requestUri.CustomQueryOptions.TryGetValue(paramName, out paramValue)) { nextLinkUri.CustomQueryOptions[paramName] = paramValue; } } } return this.UriToStringConverter.ConvertToString(nextLinkUri); }
/// <summary> /// Returns whether or not the uri refers to a named stream /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri refers to a named stream</returns> public static bool IsNamedStream(this ODataUri uri) { return(uri.Segments.Count > 0 && uri.LastSegment.SegmentType == ODataUriSegmentType.NamedStream); }
internal static bool ResponseMightIncludeNextLink(ODataUri uri) { var payloadsWithNextLinks = new[] { ODataPayloadElementType.EntitySetInstance, ODataPayloadElementType.LinkCollection }; var expectedPayloadType = uri.GetExpectedPayloadType(); if (!payloadsWithNextLinks.Contains(expectedPayloadType)) { return false; } EntitySet expectedSet; ExceptionUtilities.Assert(uri.TryGetExpectedEntitySet(out expectedSet), "Could not get expected entity set"); var pageSize = expectedSet.GetEffectivePageSize(); if (!pageSize.HasValue) { return false; } // if the value of $top is less than 1 page size, no next link will be included return !uri.Top.HasValue || uri.Top.Value > pageSize.Value; }
/// <summary> /// Returns whether or not the uri contains one or more open properties /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri contains one or more open properties</returns> public static bool HasOpenProperties(this ODataUri uri) { EntitySet currentSet = null; NamedStructuralType currentType = null; foreach (var segment in uri.Segments) { var setSegment = segment as EntitySetSegment; if (setSegment != null) { currentSet = setSegment.EntitySet; currentType = currentSet.EntityType; continue; } var serviceOperationSegment = segment as FunctionSegment; if (serviceOperationSegment != null) { EntitySet svcOpEntitySet; if (serviceOperationSegment.Function.TryGetExpectedEntitySet(currentSet, out svcOpEntitySet)) { currentSet = svcOpEntitySet; currentType = currentSet.EntityType; continue; } } var navigationSegment = segment as NavigationSegment; if (navigationSegment != null) { ExceptionUtilities.CheckObjectNotNull(currentSet, "Cannot have navigation before a root set or function"); currentSet = currentSet.GetRelatedEntitySet(navigationSegment.NavigationProperty); currentType = currentSet.EntityType; continue; } var typeSegment = segment as EntityTypeSegment; if (typeSegment != null) { currentType = typeSegment.EntityType; continue; } var propertySegment = segment as PropertySegment; if (propertySegment != null) { ExceptionUtilities.CheckObjectNotNull(currentType, "Cannot have property before a root set or function"); if (currentType.IsOpen && !propertySegment.Property.IsMetadataDeclaredProperty()) { return(true); } } var unrecognizedSegment = segment as UnrecognizedSegment; if (unrecognizedSegment != null && currentType != null) { ExceptionUtilities.CheckObjectNotNull(currentType, "Cannot have property before a root set or function"); if (currentType.IsOpen) { return(true); } } } return(false); }
internal static void SetupProtocolRequest(QueryExpression expression, IODataRequestManager requestManager, IQueryToODataPayloadConverter queryToPayloadConverter, ODataUri uri, HttpHeaderCollection headers, string actionContentType, out Contracts.OData.ODataRequest request) { HttpVerb requestVerb = HttpVerb.Get; if (uri.IsAction()) { requestVerb = HttpVerb.Post; } if (uri.IsWebInvokeServiceOperation()) { requestVerb = HttpVerb.Post; } request = requestManager.BuildRequest(uri, requestVerb, headers); if (uri.IsAction()) { var procedurePayload = queryToPayloadConverter.ComputePayload(expression) as ComplexInstance; if (procedurePayload != null) { request.Headers.Add(HttpHeaders.ContentType, actionContentType); FixupAddingResultWrappers(actionContentType, procedurePayload); // TODO: Need to understand if product allow an Html form even if no parameters specified request.Body = requestManager.BuildBody(actionContentType, uri, procedurePayload); } } }
/// <summary> /// Gets the metadata for the set expected for the given uri. If it would return an entity or single property, it will return the set the entity belongs to /// </summary> /// <param name="uri">The uri to get the set for</param> /// <param name="expectedEntitySet">The expected entity set if appropriate, otherwise null</param> /// <returns>Whether an expected entity set could be inferred</returns> public static bool TryGetExpectedEntitySet(this ODataUri uri, out EntitySet expectedEntitySet) { EntityType entityType; return(uri.TryGetExpectedEntitySetAndType(out expectedEntitySet, out entityType)); }
/// <summary> /// Generates the raw test message with mixed encodings defined by the test case, as well as the expected ODataPayloadElement. /// </summary> /// <param name="testCase">The test case defining the structure and encodings of the batch payload.</param> /// <param name="payload">The payload to use for all generated batch operations.</param> /// <param name="payloadUri">The URI to use for all generated batch operations.</param> /// <param name="isRequest">If true, generates a batch request, otherwise a batch response.</param> /// <returns>The test descriptor for this test case/configuration.</returns> private BatchReaderMixedEncodingTestDescriptor CreateTestDescriptor(BatchReaderMixedEncodingTestCase testCase, ODataPayloadElement payload, ODataUri payloadUri, bool isRequest) { ExceptionUtilities.Assert(testCase.BatchEncoding != null, "Batch encoding has not been specified."); string batchBoundary = "batch_" + Guid.NewGuid().ToString(); string payloadUriString = this.UriConverter.ConvertToString(payloadUri); var batchPayload = isRequest ? (ODataPayloadElement) new BatchRequestPayload() : (ODataPayloadElement) new BatchResponsePayload(); batchPayload.AddAnnotation(new BatchBoundaryAnnotation(batchBoundary)); var rawMessage = new List <byte>(); // TODO: Batch reader does not support multi codepoint encodings Encoding unsupportedEncoding = AsUnsupportedEncoding(testCase.BatchEncoding); foreach (var changeset in testCase.Changesets) { string changesetBoundary = "change_" + Guid.NewGuid().ToString(); Encoding changesetEncoding = changeset.ChangesetEncoding ?? testCase.BatchEncoding; // TODO: Batch reader does not support multi codepoint encodings unsupportedEncoding = unsupportedEncoding ?? AsUnsupportedEncoding(changesetEncoding); string changesetContentType = HttpUtilities.BuildContentType( MimeTypes.MultipartMixed, changeset.ChangesetEncoding == null ? string.Empty : changeset.ChangesetEncoding.WebName, changesetBoundary); rawMessage.AddRange( WriteMessagePart( testCase.BatchEncoding, (writer) => { writer.Write("--"); writer.WriteLine(batchBoundary); writer.WriteLine(HttpHeaders.ContentType + ": " + changesetContentType); writer.WriteLine(); })); var mimeParts = new List <IMimePart>(); int contentId = 0; foreach (var operation in changeset.Operations) { ExceptionUtilities.Assert(operation.PayloadFormat == ODataFormat.Atom || operation.PayloadFormat == ODataFormat.Json, "Payload format must be ATOM or JSON."); string formatType = MimeTypes.ApplicationAtomXml + ";type=entry"; Encoding payloadEncoding = operation.OperationEncoding ?? changesetEncoding; string payloadContentType = HttpUtilities.BuildContentType( formatType, operation.OperationEncoding == null ? string.Empty : operation.OperationEncoding.WebName, string.Empty); string httpStatus = isRequest ? "POST " + payloadUriString + " HTTP/1.1" : "HTTP/1.1 201 Created"; rawMessage.AddRange( WriteMessagePart( changesetEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.WriteLine(changesetBoundary); writer.WriteLine(HttpHeaders.ContentType + ": application/http"); writer.WriteLine(HttpHeaders.ContentTransferEncoding + ": binary"); writer.WriteLine(HttpHeaders.ContentId + ": " + (++contentId).ToString()); writer.WriteLine(); writer.WriteLine(httpStatus); writer.WriteLine(HttpHeaders.ContentType + ": " + payloadContentType); writer.WriteLine(); })); IPayloadSerializer payloadSerializer = operation.PayloadFormat == ODataFormat.Atom ? (IPayloadSerializer) new XmlPayloadSerializer(this.PayloadElementToXmlConverter) : (IPayloadSerializer) new JsonPayloadSerializer(this.PayloadElementToJsonConverter.ConvertToJson); byte[] payloadBytes = payloadSerializer.SerializeToBinary(payload, payloadEncoding.WebName); rawMessage.AddRange(payloadBytes.Skip(payloadEncoding.GetPreamble().Length)); if (isRequest) { var request = this.RequestManager.BuildRequest(payloadUri, HttpVerb.Post, new Dictionary <string, string> { { HttpHeaders.ContentType, payloadContentType } }); request.Body = new ODataPayloadBody(payloadBytes, payload); mimeParts.Add(request); } else { var httpResponseData = new HttpResponseData { StatusCode = HttpStatusCode.Created, }; httpResponseData.Headers.Add(HttpHeaders.ContentType, payloadContentType); var response = new ODataResponse(httpResponseData) { Body = payloadBytes, RootElement = payload }; mimeParts.Add(response); } } rawMessage.AddRange( WriteMessagePart( changesetEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.Write(changesetBoundary); writer.WriteLine("--"); })); if (isRequest) { ((BatchRequestPayload)batchPayload).Add(BatchPayloadBuilder.RequestChangeset(changesetBoundary, changesetEncoding.WebName, mimeParts.ToArray())); } else { ((BatchResponsePayload)batchPayload).Add(BatchPayloadBuilder.ResponseChangeset(changesetBoundary, changesetEncoding.WebName, mimeParts.ToArray())); } } rawMessage.AddRange( WriteMessagePart( testCase.BatchEncoding, (writer) => { writer.WriteLine(); writer.Write("--"); writer.Write(batchBoundary); writer.WriteLine("--"); })); return(new BatchReaderMixedEncodingTestDescriptor(this.PayloadReaderSettings) { BatchContentTypeHeader = HttpUtilities.BuildContentType(MimeTypes.MultipartMixed, testCase.BatchEncoding.WebName, batchBoundary), RawMessage = rawMessage.ToArray(), PayloadElement = batchPayload, ExpectedException = unsupportedEncoding == null ? null : ODataExpectedExceptions.ODataException("ODataBatchReaderStream_MultiByteEncodingsNotSupported", unsupportedEncoding.WebName) }); }
public static bool TryGetExpectedEntitySetAndType(this ODataUri uri, out EntitySet expectedEntitySet, out EntityType expectedEntityType) { EntitySet expectedSet = null; EntityType expectedType = null; foreach (var segment in uri.Segments) { var setSegment = segment as EntitySetSegment; if (setSegment != null) { expectedSet = setSegment.EntitySet; expectedType = expectedSet.EntityType; continue; } var navigationSegment = segment as NavigationSegment; if (navigationSegment != null) { expectedSet = expectedSet.GetRelatedEntitySet(navigationSegment.NavigationProperty); expectedType = navigationSegment.NavigationProperty.ToAssociationEnd.EntityType; continue; } var functionSegment = segment as FunctionSegment; if (functionSegment != null) { EntitySet svcOpEntitySet; if (!functionSegment.Function.TryGetExpectedEntitySet(expectedSet, out svcOpEntitySet)) { expectedSet = null; continue; } expectedSet = svcOpEntitySet; EntityDataType entityDataType; var returnType = functionSegment.Function.ReturnType; var collectionReturnType = returnType as CollectionDataType; if (collectionReturnType != null) { entityDataType = collectionReturnType.ElementDataType as EntityDataType; } else { entityDataType = returnType as EntityDataType; } if (entityDataType != null) { expectedType = entityDataType.Definition; } else { expectedType = expectedSet.EntityType; } continue; } var typeSegment = segment as EntityTypeSegment; if (typeSegment != null) { expectedType = typeSegment.EntityType; continue; } } expectedEntitySet = expectedSet; expectedEntityType = expectedType; return(expectedSet != null); }
/// <summary> /// Returns whether or not the uri refers to a procedure call /// </summary> /// <param name="uri">The uri to extend</param> /// <returns>whether or not the uri refers to a procedure call</returns> public static bool IsAction(this ODataUri uri) { return(uri.CheckLastSegment <FunctionSegment>(f => f.Function.IsAction())); }
/// <summary> /// Builds the ODataPayloadContext context from OODataUri. /// </summary> /// <param name="uri">The URI.</param> /// <returns>A OData Payload Context</returns> public static ODataPayloadContext BuildODataPayloadContextFromODataUri(ODataUri uri) { var payloadContext = new ODataPayloadContext(); payloadContext.PayloadUri = uri; payloadContext.ExpectedPayloadKind = uri.GetExpectedPayloadType(); switch (payloadContext.ExpectedPayloadKind) { case ODataPayloadElementType.EntitySetInstance: case ODataPayloadElementType.EntityInstance: SetExpectedEntitySetAndTypeFromUri(payloadContext, uri); break; case ODataPayloadElementType.PrimitiveProperty: case ODataPayloadElementType.PrimitiveMultiValueProperty: case ODataPayloadElementType.ComplexProperty: case ODataPayloadElementType.ComplexMultiValueProperty: case ODataPayloadElementType.EmptyCollectionProperty: var propertySegment = uri.Segments.OfType <PropertySegment>().LastOrDefault(); if (propertySegment != null) { payloadContext.ExpectedMemberProperty = propertySegment.Property; SetExpectedEntitySetAndTypeFromUri(payloadContext, uri); var parentPropertySegment = uri.Segments.GetSecondToLastOrDefault <PropertySegment>(); if (parentPropertySegment != null) { var collectionDataType = parentPropertySegment.Property.PropertyType as CollectionDataType; if (collectionDataType != null) { var elementComplexDataType = collectionDataType.ElementDataType as ComplexDataType; ExceptionUtilities.CheckObjectNotNull(elementComplexDataType, "Expected non complex DataType"); payloadContext.ExpectedPropertyDeclaringParentType = elementComplexDataType.Definition; } else { var complexDataType = parentPropertySegment.Property.PropertyType as ComplexDataType; ExceptionUtilities.CheckObjectNotNull(complexDataType, "Expected non null complex DataType"); payloadContext.ExpectedPropertyDeclaringParentType = complexDataType.Definition; } } else { payloadContext.ExpectedPropertyDeclaringParentType = payloadContext.ExpectedEntityType; } } else { ExceptionUtilities.Assert( uri.IsFunction() || uri.IsServiceOperation() || uri.IsAction(), "Property uri contains no property segments and is not a function/service operation/action."); var functionSegment = uri.Segments.Last() as FunctionSegment ?? uri.Segments[uri.Segments.Count - 2] as FunctionSegment; ExceptionUtilities.CheckObjectNotNull(functionSegment, "Failed to find function segment in finction/service operation uri"); payloadContext.ExpectedFunction = functionSegment.Function; } break; case ODataPayloadElementType.DeferredLink: case ODataPayloadElementType.LinkCollection: var navigationSegment = uri.Segments.OfType <NavigationSegment>().Last(); ExceptionUtilities.CheckObjectNotNull(navigationSegment, "No navigation segments found for expected navigation property uri"); payloadContext.ExpectedNavigationProperty = navigationSegment.NavigationProperty; SetExpectedEntitySetAndTypeFromUri(payloadContext, uri); break; } return(payloadContext); }