/// <summary> /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation /// in a batch request. /// </summary> /// <returns>The message that can be used to read the content of the batch request operation from.</returns> protected override ODataBatchOperationResponseMessage CreateOperationResponseMessageImplementation() { string responseLine = this.batchStream.ReadFirstNonEmptyLine(); int statusCode = this.ParseResponseLine(responseLine); // Read all headers and create the response message ODataBatchOperationHeaders headers = this.batchStream.ReadHeaders(); if (this.currentContentId == null) { headers.TryGetValue(ODataConstants.ContentIdHeader, out this.currentContentId); } // In responses we don't need to use our batch URL resolver, since there are no cross referencing URLs // so use the URL resolver from the batch message instead. // We don't have correlation of changeset boundary between request and response messages in // changesets, so use null value for groupId. ODataBatchOperationResponseMessage responseMessage = BuildOperationResponseMessage( () => ODataBatchUtils.CreateBatchOperationReadStream(this.batchStream, headers, this), statusCode, headers, this.currentContentId, /*groupId*/ null); //// NOTE: Content-IDs for cross referencing are only supported in request messages; in responses //// we allow a Content-ID header but don't process it (i.e., don't add the content ID to the URL resolver). this.currentContentId = null; return(responseMessage); }
/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="context">The message context.</param> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpContext context) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (context == null) { throw Error.ArgumentNull("context"); } string contentId = (context.Request != null) ? context.Request.GetODataContentId() : String.Empty; ODataBatchOperationResponseMessage batchResponse = writer.CreateOperationResponseMessage(contentId); batchResponse.StatusCode = context.Response.StatusCode; foreach (KeyValuePair <string, StringValues> header in context.Response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value.ToArray())); } if (context.Response.Body != null && context.Response.Body.Length != 0) { using (Stream stream = batchResponse.GetStream()) { context.RequestAborted.ThrowIfCancellationRequested(); context.Response.Body.Seek(0L, SeekOrigin.Begin); await context.Response.Body.CopyToAsync(stream); } } }
/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="response">The response message.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task object representing writing the given batch response using the given writer.</returns> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpResponseMessage response, CancellationToken cancellationToken) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (response == null) { throw Error.ArgumentNull("response"); } ODataBatchOperationResponseMessage batchResponse = writer.CreateOperationResponseMessage(); batchResponse.StatusCode = (int)response.StatusCode; foreach (KeyValuePair <string, IEnumerable <string> > header in response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value)); } if (response.Content != null) { foreach (KeyValuePair <string, IEnumerable <string> > header in response.Content.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value)); } using (Stream stream = batchResponse.GetStream()) { cancellationToken.ThrowIfCancellationRequested(); await response.Content.CopyToAsync(stream); } } }
private static BatchWriterStatesTestSetupResult GetOperationStream(object message, WriterTestConfiguration testConfiguration) { BatchWriterStatesTestSetupResult result = new BatchWriterStatesTestSetupResult { Message = message }; ODataBatchOperationRequestMessage requestMessage = message as ODataBatchOperationRequestMessage; if (requestMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = requestMessage.GetStream(); return(result); } else { // TODO: 191417: Enable async Tests on Phone and Silverlight when Product Supports them #if SILVERLIGHT || WINDOWS_PHONE throw new TaupoNotSupportedException("This test is not supported in aSynchronous mode in Silverlight or Phone"); #else var t = requestMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); #endif } } ODataBatchOperationResponseMessage responseMessage = message as ODataBatchOperationResponseMessage; if (responseMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = responseMessage.GetStream(); return(result); } else { // TODO: Enable async Tests on Phone and Silverlight when Product Supports them #if SILVERLIGHT || WINDOWS_PHONE throw new TaupoNotSupportedException("This test is not supported in aSynchronous mode in Silverlight or Phone"); #else var t = responseMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); #endif } } return(null); }
private static Stream GetMessageStream(ODataBatchOperationResponseMessage responseMessage, WriterTestConfiguration testConfiguration) { if (testConfiguration.Synchronous) { return(responseMessage.GetStream()); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); return(t.Result); } }
private void WriteOperation(ODataBatchWriter writer, OeOperationMessage operation) { ODataBatchOperationResponseMessage operationMessage = writer.CreateOperationResponseMessage(operation.ContentId); operationMessage.SetHeader("Location", operation.RequestUrl.AbsoluteUri); operationMessage.SetHeader(ODataConstants.ContentTypeHeader, operation.ContentType); operationMessage.StatusCode = (int)operation.StatusCode; if (operation.StatusCode != HttpStatusCode.NoContent) { using (Stream stream = operationMessage.GetStream()) WriteEntity(stream, operation.EntityItem); } }
private async Task WriteOperation(ODataBatchWriter writer, OeOperationMessage operation) { ODataBatchOperationResponseMessage operationMessage = await writer.CreateOperationResponseMessageAsync(operation.ContentId); operationMessage.SetHeader("Location", operation.RequestUrl.AbsoluteUri); operationMessage.SetHeader(ODataConstants.ContentTypeHeader, operation.ContentType); operationMessage.StatusCode = (int)operation.StatusCode; if (operation.StatusCode != HttpStatusCode.NoContent) { using (Stream stream = await operationMessage.GetStreamAsync()) await WriteEntity(operation.EntitySet, operation.Entry, stream); } }
/// <summary> /// Writes a single OData batch response. /// </summary> /// <param name="writer">The <see cref="ODataBatchWriter"/>.</param> /// <param name="context">The message context.</param> /// <param name="asyncWriter">Whether or not the writer is in async mode. </param> public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpContext context, bool asyncWriter) { if (writer == null) { throw Error.ArgumentNull("writer"); } if (context == null) { throw Error.ArgumentNull("context"); } string contentId = (context.Request != null) ? context.Request.GetODataContentId() : String.Empty; ODataBatchOperationResponseMessage batchResponse = asyncWriter ? await writer.CreateOperationResponseMessageAsync(contentId) : writer.CreateOperationResponseMessage(contentId); batchResponse.StatusCode = context.Response.StatusCode; foreach (KeyValuePair <string, StringValues> header in context.Response.Headers) { batchResponse.SetHeader(header.Key, String.Join(",", header.Value.ToArray())); } if (context.Response.Body != null && context.Response.Body.Length != 0) { using (Stream stream = asyncWriter ? await batchResponse.GetStreamAsync() : batchResponse.GetStream()) { context.RequestAborted.ThrowIfCancellationRequested(); context.Response.Body.Seek(0L, SeekOrigin.Begin); await context.Response.Body.CopyToAsync(stream); // Close and release the stream for the individual response ODataBatchStream batchStream = context.Response.Body as ODataBatchStream; if (batchStream != null) { if (asyncWriter) { await batchStream.InternalDisposeAsync(); } else { batchStream.InternalDispose(); } } } } }
private void ClientReadBatchResponse(byte[] responsePayload, string expectedAtomicGroupId, string expectedAtomicGroupAId) { IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader("Content-Type", batchContentTypeApplicationJson); using (var messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), this.userModel)) { var batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: ODataBatchOperationResponseMessage operationMessage = batchReader.CreateOperationResponseMessage(); string responseId = operationMessage.ContentId; if (responseId.Equals("1") || responseId.Equals("2")) { // Verify the group id of the responses is correlated to the group id from the data modification requests. Assert.Equal(operationMessage.GroupId, expectedAtomicGroupId); Assert.True(operationMessage.StatusCode == 201 || operationMessage.StatusCode == 204); } else if (responseId.Equals("1A") || responseId.Equals("2A")) { // Verify the group id of the responses is correlated to the group id from the data modification requests. Assert.Equal(operationMessage.GroupId, expectedAtomicGroupAId); Assert.True(operationMessage.StatusCode == 201 || operationMessage.StatusCode == 204); } else if (responseId.Equals("3") || responseId.Equals("3A")) { // Verify the group id of the query response. Assert.Null(operationMessage.GroupId); } else { Assert.True(false, "Unexpected response id received: " + responseId); } break; default: break; } } } }
private void ClientReadSingletonBatchResponse(byte[] responsePayload, string batchContentType) { IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader("Content-Type", batchContentType); using (ODataMessageReader messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationResponseMessage operationMessage = batchReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == 200) { using (ODataMessageReader innerMessageReader = new ODataMessageReader(operationMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataReader reader = innerMessageReader.CreateODataResourceReader(); while (reader.Read()) { if (reader.State == ODataReaderState.ResourceEnd) { ODataResource entry = reader.Item as ODataResource; Assert.Equal(10, entry.Properties.Single(p => p.Name == "WebId").Value); Assert.Equal("WebSingleton", entry.Properties.Single(p => p.Name == "Name").Value); } } } // The only two messages with HTTP-200 response codes are the two GET requests with content id value of null. // Verify that: for multipart batch the content id of the response is matching that of the request; // for Json batch the content id of the response is not null. Assert.True( (batchReader is ODataJsonLightBatchReader && operationMessage.ContentId != null) || (batchReader is ODataMultipartMixedBatchReader && operationMessage.ContentId == null)); } break; } } } }
/// <summary> /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation /// in a batch request. /// </summary> /// <returns>The message that can be used to read the content of the batch request operation from.</returns> private ODataBatchOperationResponseMessage CreateOperationResponseMessageImplementation() { this.operationState = OperationState.MessageCreated; string responseLine = this.batchStream.ReadFirstNonEmptyLine(); int statusCode = this.ParseResponseLine(responseLine); // Read all headers and create the response message ODataBatchOperationHeaders headers = this.batchStream.ReadHeaders(); if (this.batchStream.ChangeSetBoundary != null) { if (this.allowLegacyContentIdBehaviour) { // Add a potential Content-ID header to the URL resolver so that it will be available // to subsequent operations. string contentId; if (this.contentIdToAddOnNextRead == null && headers.TryGetValue(ODataConstants.ContentIdHeader, out contentId)) { if (contentId != null && this.urlResolver.ContainsContentId(contentId)) { throw new ODataException(Strings.ODataBatchReader_DuplicateContentIDsNotAllowed(contentId)); } this.contentIdToAddOnNextRead = contentId; } } } // In responses we don't need to use our batch URL resolver, since there are no cross referencing URLs // so use the URL resolver from the batch message instead. ODataBatchOperationResponseMessage responseMessage = ODataBatchOperationResponseMessage.CreateReadMessage( this.batchStream, statusCode, headers, this.contentIdToAddOnNextRead, /*operationListener*/ this, this.urlResolver.BatchMessageUrlResolver); //// NOTE: Content-IDs for cross referencing are only supported in request messages; in responses //// we allow a Content-ID header but don't process it (i.e., don't add the content ID to the URL resolver). return(responseMessage); }
/// <summary> /// Creates an operation response message that can be used to read the operation content from. /// </summary> /// <param name="batchReaderStream">The batch stream underyling the operation response message.</param> /// <param name="statusCode">The status code to use for the operation response message.</param> /// <param name="headers">The headers to use for the operation response message.</param> /// <param name="contentId">The content-ID for the operation response message.</param> /// <param name="operationListener">The operation listener.</param> /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param> /// <returns>An <see cref="ODataBatchOperationResponseMessage"/> that can be used to read the operation content.</returns> internal static ODataBatchOperationResponseMessage CreateReadMessage( ODataBatchReaderStream batchReaderStream, int statusCode, ODataBatchOperationHeaders headers, string contentId, IODataBatchOperationListener operationListener, IODataUrlResolver urlResolver) { Debug.Assert(batchReaderStream != null, "batchReaderStream != null"); Debug.Assert(operationListener != null, "operationListener != null"); Func <Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationReadStream(batchReaderStream, headers, operationListener); ODataBatchOperationResponseMessage responseMessage = new ODataBatchOperationResponseMessage(streamCreatorFunc, headers, operationListener, contentId, urlResolver, /*writing*/ false); responseMessage.statusCode = statusCode; return(responseMessage); }
private static BatchWriterStatesTestSetupResult GetOperationStream(object message, WriterTestConfiguration testConfiguration) { BatchWriterStatesTestSetupResult result = new BatchWriterStatesTestSetupResult { Message = message }; ODataBatchOperationRequestMessage requestMessage = message as ODataBatchOperationRequestMessage; if (requestMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = requestMessage.GetStream(); return(result); } else { var t = requestMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); } } ODataBatchOperationResponseMessage responseMessage = message as ODataBatchOperationResponseMessage; if (responseMessage != null) { if (testConfiguration.Synchronous) { result.MessageStream = responseMessage.GetStream(); return(result); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); result.MessageStream = t.Result; return(result); } } return(null); }
/// <summary> /// Set the response stream. /// </summary> /// <param name ="message">The message that we are setting the stream for.</param> /// <param name="stream">Stream to which the response needs to be written.</param> internal static void SetStream(this IODataResponseMessage message, Stream stream) { AstoriaResponseMessage astoriaResponseMessage = message as AstoriaResponseMessage; ODataBatchOperationResponseMessage batchResponseMessage = message as ODataBatchOperationResponseMessage; if (astoriaResponseMessage != null) { Debug.Assert(stream != null, "When we call SetStream on a non-batch response message, the stream shouldn't be null."); astoriaResponseMessage.SetStream(stream); } else if (batchResponseMessage != null) { Debug.Assert(stream == null, "When we call SetStream, if we are in a batch operation, then the stream should be null."); } else { Debug.Fail("SetStream called on an unknown message type."); } }
private void ClientReadBatchResponse(byte[] responsePayload, BodyContentType bodyContentType) { IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader(ODataConstants.ContentTypeHeader, batchContentTypeApplicationJson); using (ODataMessageReader messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), null)) { ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationResponseMessage operationMessage = batchReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == 200) { using (Stream operationMessageBody = operationMessage.GetStream()) { // Verify the bytes in the response body. byte[] sampleBytes = bodyContentType == BodyContentType.Textual ? Encoding.UTF8.GetBytes("\"" + this.textualSampleStringB + "\"") : this.binarySampleBytesB; Assert.Equal(operationMessageBody.Length, sampleBytes.Length); foreach (byte samplebyte in sampleBytes) { Assert.Equal(samplebyte, operationMessageBody.ReadByte()); } } } else { Assert.True(201 == operationMessage.StatusCode); } break; } } } }
internal static void SetResponseHeadersForBatchRequests(ODataBatchOperationResponseMessage operationResponseMessage, BatchServiceHost batchHost) { IDataServiceHost2 host = batchHost; operationResponseMessage.StatusCode = host.ResponseStatusCode; if (batchHost.ContentId != null) { operationResponseMessage.SetHeader("Content-ID", batchHost.ContentId); } WebHeaderCollection responseHeaders = host.ResponseHeaders; foreach (string str in responseHeaders.AllKeys) { string str2 = responseHeaders[str]; if (!string.IsNullOrEmpty(str2)) { operationResponseMessage.SetHeader(str, str2); } } }
/// <summary> /// Returns the cached <see cref="ODataBatchOperationResponseMessage"/> for reading the content of a /// batch response. /// </summary> /// <returns>The message that can be used to read the content of the batch response from.</returns> protected override ODataBatchOperationResponseMessage CreateOperationResponseMessageImplementation() { Debug.Assert(this.mode == ReaderMode.Responses, "this.mode == ReaderMode.Responses"); Debug.Assert(this.messagePropertiesCache != null, "this.responsePropertiesCache != null"); // body. Use empty stream when request body is not present. Stream bodyContentStream = (Stream)this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameBody) ?? new ODataJsonLightBatchBodyContentReaderStream(this); int statusCode = (int) this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameStatus); string contentId = (string)this.messagePropertiesCache.GetPropertyValue( ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId); string groupId = (string)this.messagePropertiesCache.GetPropertyValue( ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup); ODataBatchOperationHeaders headers = (ODataBatchOperationHeaders) this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameHeaders); // Reset the response property cache since all data in cache has been processed. // So that new instance can be created during subsequent read in operation state. this.messagePropertiesCache = null; // In responses we don't need to use our batch URL resolver, since there are no cross referencing URLs // so use the URL resolver from the batch message instead. ODataBatchOperationResponseMessage responseMessage = BuildOperationResponseMessage( () => bodyContentStream, statusCode, headers, contentId, groupId); //// NOTE: Content-IDs for cross referencing are only supported in request messages; in responses //// we allow a Content-ID header but don't process it (i.e., don't add the content ID to the URL resolver). return(responseMessage); }
private void ClientReadBatchResponse(string batchContentType, byte[] responsePayload) { Debug.Assert(!string.IsNullOrEmpty(batchContentType), "!string.IsNullOrEmpty(batchContentType)"); IODataResponseMessage responseMessage = new InMemoryMessage() { Stream = new MemoryStream(responsePayload) }; responseMessage.SetHeader("Content-Type", batchContentType); using (var messageReader = new ODataMessageReader(responseMessage, new ODataMessageReaderSettings(), this.userModel)) { var batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: ODataBatchOperationResponseMessage operationMessage = batchReader.CreateOperationResponseMessage(); if (operationMessage.StatusCode == 200) { using (ODataMessageReader innerMessageReader = new ODataMessageReader( operationMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataReader reader = innerMessageReader.CreateODataResourceReader(); while (reader.Read()) { } } } break; default: break; } } } }
internal static IList <TableResult> TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpWebResponse resp, OperationContext ctx, TableRequestOptions options, string accountName) { ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings(); readerSettings.MessageQuotas = new ODataMessageQuotas() { MaxPartsPerBatch = TableConstants.TableServiceMaxResults, MaxReceivedMessageSize = TableConstants.TableServiceMaxPayload }; using (ODataMessageReader responseReader = new ODataMessageReader(new HttpResponseAdapterMessage(resp, cmd.ResponseStream), readerSettings)) { // create a reader ODataBatchReader reader = responseReader.CreateODataBatchReader(); // Initial => changesetstart if (reader.State == ODataBatchReaderState.Initial) { reader.Read(); } if (reader.State == ODataBatchReaderState.ChangesetStart) { // ChangeSetStart => Operation reader.Read(); } int index = 0; bool failError = false; bool failUnexpected = false; while (reader.State == ODataBatchReaderState.Operation) { TableOperation currentOperation = batch[index]; TableResult currentResult = new TableResult() { Result = currentOperation.Entity }; result.Add(currentResult); ODataBatchOperationResponseMessage mimePartResponseMessage = reader.CreateOperationResponseMessage(); string contentType = mimePartResponseMessage.GetHeader(Constants.ContentTypeElement); currentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; // Validate Status Code. if (currentOperation.OperationType == TableOperationType.Insert) { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.Conflict; if (currentOperation.EchoContent) { failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.Created; } else { failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } } else if (currentOperation.OperationType == TableOperationType.Retrieve) { if (mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound) { index++; // Operation => next reader.Read(); continue; } failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.OK; } else { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } if (failError) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(mimePartResponseMessage.GetStream(), resp, contentType); } cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = mimePartResponseMessage.StatusCode.ToString(CultureInfo.InvariantCulture); } throw new StorageException( cmd.CurrentResult, cmd.CurrentResult.ExtendedErrorInformation != null ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : SR.ExtendedErrorUnavailable, null) { IsRetryable = false }; } if (failUnexpected) { // If the parse error is null, then don't get the extended error information and the StorageException will contain SR.ExtendedErrorUnavailable message. if (cmd.ParseError != null) { cmd.CurrentResult.ExtendedErrorInformation = cmd.ParseError(mimePartResponseMessage.GetStream(), resp, contentType); } cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; if (!string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string msg = cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage; cmd.CurrentResult.HttpStatusMessage = msg.Substring(0, msg.IndexOf("\n", StringComparison.Ordinal)); } else { cmd.CurrentResult.HttpStatusMessage = mimePartResponseMessage.StatusCode.ToString(CultureInfo.InvariantCulture); } string indexString = Convert.ToString(index, CultureInfo.InvariantCulture); // Attempt to extract index of failing entity from extended error info if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string tempIndex = TableRequest.ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(tempIndex)) { indexString = tempIndex; } } throw new StorageException(cmd.CurrentResult, string.Format(CultureInfo.CurrentCulture, SR.BatchErrorInOperation, indexString), null) { IsRetryable = true }; } // Update etag if (!string.IsNullOrEmpty(mimePartResponseMessage.GetHeader(Constants.HeaderConstants.EtagHeader))) { currentResult.Etag = mimePartResponseMessage.GetHeader(Constants.HeaderConstants.EtagHeader); if (currentOperation.Entity != null) { currentOperation.Entity.ETag = currentResult.Etag; } } // Parse Entity if needed if (currentOperation.OperationType == TableOperationType.Retrieve || (currentOperation.OperationType == TableOperationType.Insert && currentOperation.EchoContent)) { if (mimePartResponseMessage.GetHeader(Constants.ContentTypeElement).Contains(Constants.JsonNoMetadataAcceptHeaderValue)) { ReadEntityUsingJsonParser(currentResult, currentOperation, mimePartResponseMessage.GetStream(), ctx, options); } else { ReadOdataEntity(currentResult, currentOperation, mimePartResponseMessage, ctx, readerSettings, accountName, options); } } else if (currentOperation.OperationType == TableOperationType.Insert) { currentOperation.Entity.Timestamp = ParseETagForTimestamp(currentResult.Etag); } index++; // Operation => reader.Read(); } } return(result); }
public void AsyncBatchRequestTest() { var writerSettings = new ODataMessageWriterSettings(); writerSettings.BaseUri = ServiceBaseUri; ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings() { BaseUri = ServiceBaseUri }; #region send a batch request with respond-async preference var accountType = Model.FindDeclaredType(NameSpacePrefix + "Account") as IEdmEntityType; var accountSet = Model.EntityContainer.FindEntitySet("Accounts"); var paymentInstrumentType = Model.FindDeclaredType(NameSpacePrefix + "PaymentInstrument") as IEdmEntityType; IEdmNavigationProperty navProp = accountType.FindProperty("MyPaymentInstruments") as IEdmNavigationProperty; var myPaymentInstrumentSet = accountSet.FindNavigationTarget(navProp); var requestMessage = new HttpWebRequestMessage(new Uri(ServiceBaseUri + "$batch")); requestMessage.SetHeader("Content-Type", "multipart/mixed;boundary=batch_01AD6766-4A45-47CC-9463-94D4591D8DA9"); requestMessage.SetHeader("OData-Version", "4.0"); requestMessage.PreferHeader().RespondAsync = true; //Request the service to process asynchronously. requestMessage.Method = "POST"; using (var messageWriter = new ODataMessageWriter(requestMessage, writerSettings, Model)) { var batchWriter = messageWriter.CreateODataBatchWriter(); //Batch start. batchWriter.WriteStartBatch(); //A Get request. var batchOperation1 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(101)/MyPaymentInstruments"), null); batchOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); //Get request ends. //Changeset start. batchWriter.WriteStartChangeset(); //The first operation in changeset is a Create request. ODataBatchOperationRequestMessage batchChangesetOperation1 = batchWriter.CreateOperationRequestMessage("POST", new Uri(ServiceBaseUri + "Accounts(102)/MyPaymentInstruments"), "1"); batchChangesetOperation1.SetHeader("Content-Type", "application/json;odata.metadata=full"); batchChangesetOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); var paymentInstrumentEntry = new ODataResource() { TypeName = NameSpacePrefix + "PaymentInstrument" }; var paymentInstrumentEntryP1 = new ODataProperty { Name = "PaymentInstrumentID", Value = 102910 }; var paymentInstrumentEntryP2 = new ODataProperty { Name = "FriendlyName", Value = "102 batch new PI" }; var paymentInstrumentEntryP3 = new ODataProperty { Name = "CreatedDate", Value = new DateTimeOffset(new DateTime(2013, 12, 29, 11, 11, 57)) }; paymentInstrumentEntry.Properties = new[] { paymentInstrumentEntryP1, paymentInstrumentEntryP2, paymentInstrumentEntryP3 }; using (var entryMessageWriter = new ODataMessageWriter(batchChangesetOperation1, writerSettings, Model)) { var odataEntryWriter = entryMessageWriter.CreateODataResourceWriter(myPaymentInstrumentSet, paymentInstrumentType); odataEntryWriter.WriteStart(paymentInstrumentEntry); odataEntryWriter.WriteEnd(); } //Changeset end. batchWriter.WriteEndChangeset(); //Another Get request. var batchOperation2 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(103)/MyPaymentInstruments(103901)/BillingStatements(103901001)"), null); batchOperation2.SetHeader("Accept", "application/json;odata.metadata=full"); //Batch end. batchWriter.WriteEndBatch(); } var responseMessage = requestMessage.GetResponse(); #endregion #region request the status monitor resource Assert.Equal(202, responseMessage.StatusCode); string monitorResource = responseMessage.GetHeader("Location"); Assert.False(string.IsNullOrWhiteSpace(monitorResource)); var statusCheckRequest1 = new HttpWebRequestMessage(new Uri(monitorResource)); var statusCheckResponse1 = statusCheckRequest1.GetResponse(); Assert.Equal(202, statusCheckResponse1.StatusCode); monitorResource = statusCheckResponse1.GetHeader("Location"); Assert.False(string.IsNullOrWhiteSpace(monitorResource)); //The request takes 5 seconds to finish in service, so we wait for 6 seconds. Thread.Sleep(6000); var statusCheckRequest2 = new HttpWebRequestMessage(new Uri(monitorResource)); var statusCheckResponse2 = statusCheckRequest2.GetResponse(); Assert.Equal(200, statusCheckResponse2.StatusCode); #endregion #region read and verify the response using (var messageReader = new ODataMessageReader(statusCheckResponse2, readerSettings, Model)) { var asyncReader = messageReader.CreateODataAsynchronousReader(); var innerMessage = asyncReader.CreateResponseMessage(); Assert.Equal(200, innerMessage.StatusCode); using (var innerMessageReader = new ODataMessageReader(innerMessage, readerSettings, Model)) { var batchReader = innerMessageReader.CreateODataBatchReader(); int batchOperationId = 0; while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Initial: break; case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.ChangesetEnd: break; case ODataBatchReaderState.Operation: ODataBatchOperationResponseMessage operationResponse = batchReader.CreateOperationResponseMessage(); using (var operationResponseReader = new ODataMessageReader(operationResponse, readerSettings, Model)) { if (batchOperationId == 0) { // the first response message is a feed var feedReader = operationResponseReader.CreateODataResourceSetReader(); Assert.Equal(200, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (feedReader.Read()) { switch (feedReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = feedReader.Item as ODataResource; Assert.NotNull(entry); pis.Add(entry); break; } } Assert.Equal(ODataReaderState.Completed, feedReader.State); Assert.Equal(3, pis.Count); } else if (batchOperationId == 1) { // the second response message is a creation response var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.Equal(201, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.NotNull(entry); pis.Add(entry); break; } } Assert.Equal(ODataReaderState.Completed, entryReader.State); Assert.Single(pis); Assert.Equal(102910, pis[0].Properties.Single(p => p.Name == "PaymentInstrumentID").Value); } else if (batchOperationId == 2) { // the third response message is an entry var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.Equal(200, operationResponse.StatusCode); List <ODataResource> statements = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.NotNull(entry); statements.Add(entry); break; } } Assert.Equal(ODataReaderState.Completed, entryReader.State); Assert.Single(statements); Assert.Equal(103901001, statements[0].Properties.Single(p => p.Name == "StatementID").Value); } } batchOperationId++; break; } } Assert.Equal(ODataBatchReaderState.Completed, batchReader.State); } } #endregion }
private byte[] ServiceReadRequestAndWriterResponseForMultipartBatchVerifyDependsOnIds(string requestPayload, ODataVersion maxVersion) { byte[] responseBytes = null; IODataRequestMessage requestMessage = new InMemoryMessage() { Stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(requestPayload)) }; requestMessage.SetHeader("Content-Type", batchContentTypeMultipartMime); ODataMessageReaderSettings settings = new ODataMessageReaderSettings { Version = maxVersion }; using (ODataMessageReader messageReader = new ODataMessageReader(requestMessage, settings, this.userModel)) { MemoryStream responseStream = new MemoryStream(); IODataResponseMessage responseMessage = new InMemoryMessage { Stream = responseStream }; // Client is expected to receive the response message in the same format as that is used in the request sent. responseMessage.SetHeader("Content-Type", batchContentTypeMultipartMime); using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage)) { ODataBatchWriter batchWriter = messageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationRequestMessage operationMessage = batchReader.CreateOperationRequestMessage(); // Verify DependsOnIds are set correctly IEnumerable <string> dependsOnIds = operationMessage.DependsOnIds; switch (operationMessage.ContentId) { case "1": case "2A": Assert.True(dependsOnIds.Count() == 0); break; case "2B": Assert.True(dependsOnIds.SequenceEqual(new List <string> { "2A" })); break; case "2C": Assert.True(dependsOnIds.SequenceEqual(new List <string> { "2A", "2B" })); break; case "3": Assert.True(dependsOnIds.SequenceEqual(new List <string> { "1" })); break; default: break; } ODataBatchOperationResponseMessage response = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); if (operationMessage.Method == "PATCH") { response.StatusCode = 204; response.SetHeader("Content-Type", "application/json;odata.metadata=none"); } else if (operationMessage.Method == "PUT") { response.StatusCode = 201; response.SetHeader("Content-Type", "application/json;"); } break; case ODataBatchReaderState.ChangesetStart: batchWriter.WriteStartChangeset(); break; case ODataBatchReaderState.ChangesetEnd: batchWriter.WriteEndChangeset(); break; } } batchWriter.WriteEndBatch(); responseStream.Position = 0; responseBytes = responseStream.ToArray(); } return(responseBytes); } }
internal static IList <TableResult> TableBatchOperationPostProcess(IList <TableResult> result, TableBatchOperation batch, RESTCommand <IList <TableResult> > cmd, HttpWebResponse resp, OperationContext ctx) { ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings(); readerSettings.MessageQuotas = new ODataMessageQuotas() { MaxPartsPerBatch = TableConstants.TableServiceMaxResults, MaxReceivedMessageSize = TableConstants.TableServiceMaxPayload }; using (ODataMessageReader responseReader = new ODataMessageReader(new HttpResponseAdapterMessage(resp, cmd.ResponseStream), readerSettings)) { // create a reader ODataBatchReader reader = responseReader.CreateODataBatchReader(); // Initial => changesetstart if (reader.State == ODataBatchReaderState.Initial) { reader.Read(); } if (reader.State == ODataBatchReaderState.ChangesetStart) { // ChangeSetStart => Operation reader.Read(); } int index = 0; bool failError = false; bool failUnexpected = false; while (reader.State == ODataBatchReaderState.Operation) { TableOperation currentOperation = batch[index]; TableResult currentResult = new TableResult() { Result = currentOperation.Entity }; result.Add(currentResult); ODataBatchOperationResponseMessage mimePartResponseMessage = reader.CreateOperationResponseMessage(); currentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; // Validate Status Code if (currentOperation.OperationType == TableOperationType.Insert) { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.Conflict; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.Created; } else if (currentOperation.OperationType == TableOperationType.Retrieve) { if (mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound) { index++; // Operation => next reader.Read(); continue; } failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.OK; } else { failError = mimePartResponseMessage.StatusCode == (int)HttpStatusCode.NotFound; failUnexpected = mimePartResponseMessage.StatusCode != (int)HttpStatusCode.NoContent; } if (failError) { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(mimePartResponseMessage.GetStream()); cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; throw new StorageException( cmd.CurrentResult, cmd.CurrentResult.ExtendedErrorInformation != null ? cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage : SR.ExtendedErrorUnavailable, null) { IsRetryable = false }; } if (failUnexpected) { cmd.CurrentResult.ExtendedErrorInformation = StorageExtendedErrorInformation.ReadFromStream(mimePartResponseMessage.GetStream()); cmd.CurrentResult.HttpStatusCode = mimePartResponseMessage.StatusCode; string indexString = Convert.ToString(index); // Attempt to extract index of failing entity from extended error info if (cmd.CurrentResult.ExtendedErrorInformation != null && !string.IsNullOrEmpty(cmd.CurrentResult.ExtendedErrorInformation.ErrorMessage)) { string tempIndex = TableRequest.ExtractEntityIndexFromExtendedErrorInformation(cmd.CurrentResult); if (!string.IsNullOrEmpty(tempIndex)) { indexString = tempIndex; } } throw new StorageException(cmd.CurrentResult, SR.UnexpectedResponseCodeForOperation + indexString, null) { IsRetryable = true }; } // Update etag if (!string.IsNullOrEmpty(mimePartResponseMessage.GetHeader("ETag"))) { currentResult.Etag = mimePartResponseMessage.GetHeader("ETag"); if (currentOperation.Entity != null) { currentOperation.Entity.ETag = currentResult.Etag; } } // Parse Entity if needed if (currentOperation.OperationType == TableOperationType.Retrieve || currentOperation.OperationType == TableOperationType.Insert) { ReadOdataEntity(currentResult, currentOperation, mimePartResponseMessage, ctx, readerSettings); } index++; // Operation => reader.Read(); } } return(result); }
private byte[] ServiceReadReferenceUriBatchRequestAndWriteResponse(byte[] requestPayload) { IODataRequestMessage requestMessage = new InMemoryMessage() { Stream = new MemoryStream(requestPayload) }; requestMessage.SetHeader("Content-Type", batchContentTypeApplicationJson); ODataMessageReaderSettings settings = new ODataMessageReaderSettings(); settings.BaseUri = new Uri(serviceDocumentUri); byte[] responseBytes = null; using (ODataMessageReader messageReader = new ODataMessageReader(requestMessage, settings, this.userModel)) { MemoryStream responseStream = new MemoryStream(); IODataResponseMessage responseMessage = new InMemoryMessage { Stream = responseStream }; // Client is expected to receive the response message in the same format as that is used in the request sent. responseMessage.SetHeader("Content-Type", batchContentTypeApplicationJson); using (ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage)) { ODataBatchWriter batchWriter = messageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationRequestMessage operationMessage = batchReader.CreateOperationRequestMessage(); ODataBatchOperationResponseMessage response = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); if (operationMessage.Method == "PUT") { using (ODataMessageReader operationMessageReader = new ODataMessageReader( operationMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataReader reader = operationMessageReader.CreateODataResourceReader(); Assert.NotNull(reader); } response.StatusCode = 201; response.SetHeader("Content-Type", "application/json;odata.metadata=none"); } else if (operationMessage.Method == "PATCH") { using (ODataMessageReader operationMessageReader = new ODataMessageReader( operationMessage, new ODataMessageReaderSettings(), this.userModel)) { ODataReader reader = operationMessageReader.CreateODataResourceReader(); Assert.NotNull(reader); } response.StatusCode = 204; } else if (operationMessage.Method == "GET") { response.StatusCode = 200; response.SetHeader("Content-Type", "application/json;"); ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings(); writerSettings.ODataUri.ServiceRoot = new Uri(serviceDocumentUri); using ( ODataMessageWriter operationMessageWriter = new ODataMessageWriter(response, writerSettings, this.userModel)) { ODataWriter entryWriter = operationMessageWriter.CreateODataResourceWriter(this.singleton, this.webType); ODataResource entry = new ODataResource() { TypeName = "NS.Web", Properties = new[] { new ODataProperty() { Name = "WebId", Value = -1 }, new ODataProperty() { Name = "Name", Value = aVeryLongString } } }; entryWriter.WriteStart(entry); entryWriter.WriteEnd(); } } break; case ODataBatchReaderState.ChangesetStart: // Set the group Id on the writer side to correlate with request. string atomicGroupId = batchReader.CurrentGroupId; batchWriter.WriteStartChangeset(atomicGroupId); break; case ODataBatchReaderState.ChangesetEnd: batchWriter.WriteEndChangeset(); break; } } batchWriter.WriteEndBatch(); responseStream.Position = 0; responseBytes = responseStream.ToArray(); } return(responseBytes); } }
private byte[] ServiceReadBatchRequestAndWriterBatchResponse(byte[] requestPayload, BodyContentType bodyContentType) { IODataRequestMessage requestMessage = new InMemoryMessage() { Stream = new MemoryStream(requestPayload) }; requestMessage.SetHeader(ODataConstants.ContentTypeHeader, batchContentTypeApplicationJson); using (ODataMessageReader messageReader = new ODataMessageReader(requestMessage, new ODataMessageReaderSettings(), null)) { MemoryStream responseStream = new MemoryStream(); IODataResponseMessage responseMessage = new InMemoryMessage { Stream = responseStream }; // Client is expected to receive the response message in the same format as that is used in the request sent. responseMessage.SetHeader(ODataConstants.ContentTypeHeader, batchContentTypeApplicationJson); ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage); ODataBatchWriter batchWriter = messageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: ODataBatchOperationRequestMessage operationMessage = batchReader.CreateOperationRequestMessage(); if (operationMessage.Method == "PUT") { using (Stream operationMessageBodyStream = operationMessage.GetStream()) { // Verify the bytes in the request operation stream. byte[] sampleBytes = bodyContentType == BodyContentType.Textual ? Encoding.UTF8.GetBytes("\"" + this.textualSampleStringA + "\"") : binarySampleBytesA; Assert.Equal(operationMessageBodyStream.Length, sampleBytes.Length); foreach (byte samplebyte in sampleBytes) { Assert.Equal(samplebyte, operationMessageBodyStream.ReadByte()); } } // Create the response. ODataBatchOperationResponseMessage operationResponse = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); operationResponse.StatusCode = 201; operationResponse.SetHeader("CoNtEnT-TyPe", "application/json;odata.metadata=none"); } else if (operationMessage.Method == "GET") { ODataBatchOperationResponseMessage operationResponse = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); operationResponse.StatusCode = 200; operationResponse.SetHeader("CoNtEnT-TyPe", GetContentType(bodyContentType)); ODataMessageWriterSettings settings = new ODataMessageWriterSettings(); settings.SetServiceDocumentUri(new Uri(serviceDocumentUri)); using (ODataMessageWriter operationMessageWriter = new ODataMessageWriter(operationResponse, settings, null)) { operationMessageWriter.WriteValue(GetEncodedContentObject(bodyContentType, false)); } } break; case ODataBatchReaderState.ChangesetStart: batchWriter.WriteStartChangeset(); break; case ODataBatchReaderState.ChangesetEnd: batchWriter.WriteEndChangeset(); break; } } batchWriter.WriteEndBatch(); responseStream.Position = 0; return(responseStream.ToArray()); } }
private byte[] ServiceReadSingletonBatchRequestAndWriterBatchResponse(ODataJsonBatchPayloadTestCase testCase, ODataVersion version) { string requestPayload = testCase.RequestPayload; Action <ODataBatchOperationRequestMessage, IList <string> > requestOpMessageVerifier = testCase.RequestMessageDependsOnIdVerifier; IODataRequestMessage requestMessage = new InMemoryMessage() { Stream = new MemoryStream(Encoding.ASCII.GetBytes(requestPayload)) }; requestMessage.SetHeader("Content-Type", batchContentTypeApplicationJson); MemoryStream responseStream = new MemoryStream(); using (ODataMessageReader messageReader = new ODataMessageReader(requestMessage, new ODataMessageReaderSettings() { Version = version }, this.edmModel)) { IODataResponseMessage responseMessage = new InMemoryMessage { Stream = responseStream }; // Client is expected to receive the response message in the same format as that is used in the request sent. responseMessage.SetHeader("Content-Type", batchContentTypeApplicationJson); ODataMessageWriterSettings settings = new ODataMessageWriterSettings() { Version = version }; settings.SetServiceDocumentUri(new Uri(serviceDocumentUri)); ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, settings, null); int operationIdx = 0; ODataBatchWriter batchWriter = messageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); ODataBatchReader batchReader = messageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Operation: // Encountered an operation (either top-level or in a change set) ODataBatchOperationRequestMessage operationMessage = batchReader.CreateOperationRequestMessage(); // Verify operation message if applicable. requestOpMessageVerifier?.Invoke(operationMessage, testCase.ListOfDependsOnIds.ElementAt(operationIdx)); if (operationMessage.Method == "POST") { ODataBatchOperationResponseMessage response = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); response.StatusCode = 201; response.SetHeader("Content-Type", batchContentTypeApplicationJson); } else if (operationMessage.Method == "GET") { ODataBatchOperationResponseMessage response = batchWriter.CreateOperationResponseMessage(operationMessage.ContentId); response.StatusCode = 200; response.SetHeader("Content-Type", batchContentTypeApplicationJson); using (ODataMessageWriter operationMessageWriter = new ODataMessageWriter(response, settings, this.edmModel)) { ODataWriter entryWriter = operationMessageWriter.CreateODataResourceWriter(this.singleton, this.userType); ODataResource entry = new ODataResource() { TypeName = "NS.User", Properties = new[] { new ODataProperty() { Name = "UserPrincipalName", Value = "*****@*****.**" }, new ODataProperty() { Name = "GivenName", Value = "Jon" } } }; entryWriter.WriteStart(entry); entryWriter.WriteEnd(); } } operationIdx++; break; case ODataBatchReaderState.ChangesetStart: batchWriter.WriteStartChangeset(); break; case ODataBatchReaderState.ChangesetEnd: batchWriter.WriteEndChangeset(); break; } } batchWriter.WriteEndBatch(); } responseStream.Position = 0; return(responseStream.ToArray()); }
private static Stream GetMessageStream(ODataBatchOperationResponseMessage responseMessage, WriterTestConfiguration testConfiguration) { if (testConfiguration.Synchronous) { return responseMessage.GetStream(); } else { var t = responseMessage.GetStreamAsync(); t.Wait(); return t.Result; } }
internal ODataBatchOperationResponseMessage GetOperationResponseMessage() { return(this.operationMessage ?? (this.operationMessage = this.writer.CreateOperationResponseMessage())); }
/// <summary> /// Creates an operation response message that can be used to read the operation content from. /// </summary> /// <param name="batchReaderStream">The batch stream underyling the operation response message.</param> /// <param name="statusCode">The status code to use for the operation response message.</param> /// <param name="headers">The headers to use for the operation response message.</param> /// <param name="contentId">The content-ID for the operation response message.</param> /// <param name="operationListener">The operation listener.</param> /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param> /// <returns>An <see cref="ODataBatchOperationResponseMessage"/> that can be used to read the operation content.</returns> internal static ODataBatchOperationResponseMessage CreateReadMessage( ODataBatchReaderStream batchReaderStream, int statusCode, ODataBatchOperationHeaders headers, string contentId, IODataBatchOperationListener operationListener, IODataUrlResolver urlResolver) { Debug.Assert(batchReaderStream != null, "batchReaderStream != null"); Debug.Assert(operationListener != null, "operationListener != null"); Func<Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationReadStream(batchReaderStream, headers, operationListener); ODataBatchOperationResponseMessage responseMessage = new ODataBatchOperationResponseMessage(streamCreatorFunc, headers, operationListener, contentId, urlResolver, /*writing*/ false); responseMessage.statusCode = statusCode; return responseMessage; }
private void BatchRequestWithPayloadUriWritingOption(BatchPayloadUriOption option) { var writerSettings = new ODataMessageWriterSettings(); writerSettings.BaseUri = ServiceBaseUri; ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings() { BaseUri = ServiceBaseUri }; var accountType = Model.FindDeclaredType(NameSpacePrefix + "Account") as IEdmEntityType; var accountSet = Model.EntityContainer.FindEntitySet("Accounts"); var paymentInstrumentType = Model.FindDeclaredType(NameSpacePrefix + "PaymentInstrument") as IEdmEntityType; IEdmNavigationProperty navProp = accountType.FindProperty("MyPaymentInstruments") as IEdmNavigationProperty; var myPaymentInstrumentSet = accountSet.FindNavigationTarget(navProp); var requestMessage = new HttpWebRequestMessage(new Uri(ServiceBaseUri + "$batch")); requestMessage.SetHeader("Content-Type", "multipart/mixed;boundary=batch_01AD6766-4A45-47CC-9463-94D4591D8DA9"); requestMessage.SetHeader("OData-Version", "4.0"); requestMessage.Method = "POST"; using (var messageWriter = new ODataMessageWriter(requestMessage, writerSettings, Model)) { var batchWriter = messageWriter.CreateODataBatchWriter(); //Batch start. batchWriter.WriteStartBatch(); //A Get request. var batchOperation1 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(101)/MyPaymentInstruments"), null, option); batchOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); //Get request ends. //Changeset start. batchWriter.WriteStartChangeset(); //The first operation in changeset is a Create request. ODataBatchOperationRequestMessage batchChangesetOperation1 = batchWriter.CreateOperationRequestMessage("POST", new Uri(ServiceBaseUri + "Accounts(102)/MyPaymentInstruments"), "1", option); batchChangesetOperation1.SetHeader("Content-Type", "application/json;odata.metadata=full"); batchChangesetOperation1.SetHeader("Accept", "application/json;odata.metadata=full"); var paymentInstrumentEntry = new ODataResource() { TypeName = NameSpacePrefix + "PaymentInstrument" }; var paymentInstrumentEntryP1 = new ODataProperty { Name = "PaymentInstrumentID", Value = 102910 }; var paymentInstrumentEntryP2 = new ODataProperty { Name = "FriendlyName", Value = "102 batch new PI" }; var paymentInstrumentEntryP3 = new ODataProperty { Name = "CreatedDate", Value = new DateTimeOffset(new DateTime(2013, 12, 29, 11, 11, 57)) }; paymentInstrumentEntry.Properties = new[] { paymentInstrumentEntryP1, paymentInstrumentEntryP2, paymentInstrumentEntryP3 }; using (var entryMessageWriter = new ODataMessageWriter(batchChangesetOperation1, writerSettings, Model)) { var odataEntryWriter = entryMessageWriter.CreateODataResourceWriter(myPaymentInstrumentSet, paymentInstrumentType); odataEntryWriter.WriteStart(paymentInstrumentEntry); odataEntryWriter.WriteEnd(); } //Changeset end. batchWriter.WriteEndChangeset(); //Another Get request. var batchOperation2 = batchWriter.CreateOperationRequestMessage("GET", new Uri(ServiceBaseUri + "Accounts(103)/MyPaymentInstruments(103901)/BillingStatements(103901001)"), null, option); batchOperation2.SetHeader("Accept", "application/json;odata.metadata=full"); //Batch end. batchWriter.WriteEndBatch(); } var responseMessage = requestMessage.GetResponse(); Assert.AreEqual(200, responseMessage.StatusCode); using (var innerMessageReader = new ODataMessageReader(responseMessage, readerSettings, Model)) { var batchReader = innerMessageReader.CreateODataBatchReader(); int batchOperationId = 0; while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Initial: break; case ODataBatchReaderState.ChangesetStart: break; case ODataBatchReaderState.ChangesetEnd: break; case ODataBatchReaderState.Operation: ODataBatchOperationResponseMessage operationResponse = batchReader.CreateOperationResponseMessage(); using (var operationResponseReader = new ODataMessageReader(operationResponse, readerSettings, Model)) { if (batchOperationId == 0) { // the first response message is a feed var feedReader = operationResponseReader.CreateODataResourceSetReader(); Assert.AreEqual(200, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (feedReader.Read()) { switch (feedReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = feedReader.Item as ODataResource; Assert.IsNotNull(entry); pis.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, feedReader.State); Assert.AreEqual(3, pis.Count); } else if (batchOperationId == 1) { // the second response message is a creation response var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.AreEqual(201, operationResponse.StatusCode); List <ODataResource> pis = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.IsNotNull(entry); pis.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, entryReader.State); Assert.AreEqual(1, pis.Count); Assert.AreEqual(102910, pis[0].Properties.Single(p => p.Name == "PaymentInstrumentID").Value); } else if (batchOperationId == 2) { // the third response message is an entry var entryReader = operationResponseReader.CreateODataResourceReader(); Assert.AreEqual(200, operationResponse.StatusCode); List <ODataResource> statements = new List <ODataResource>(); while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceEnd: ODataResource entry = entryReader.Item as ODataResource; Assert.IsNotNull(entry); statements.Add(entry); break; } } Assert.AreEqual(ODataReaderState.Completed, entryReader.State); Assert.AreEqual(1, statements.Count); Assert.AreEqual(103901001, statements[0].Properties.Single(p => p.Name == "StatementID").Value); } } batchOperationId++; break; } } Assert.AreEqual(ODataBatchReaderState.Completed, batchReader.State); } }
public override void Process(IODataRequestMessage requestMessage, IODataResponseMessage responseMessage) { responseMessage.SetStatusCode(HttpStatusCode.OK); using (var batchRequestMessageWriter = this.CreateMessageWriter(responseMessage)) { var batchWriter = batchRequestMessageWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); using (var batchRequestMessageReader = this.CreateMessageReader(requestMessage)) { var batchReader = batchRequestMessageReader.CreateODataBatchReader(); while (batchReader.Read()) { switch (batchReader.State) { case ODataBatchReaderState.Initial: break; case ODataBatchReaderState.ChangesetStart: batchWriter.WriteStartChangeset(); break; case ODataBatchReaderState.ChangesetEnd: batchWriter.WriteEndChangeset(); break; case ODataBatchReaderState.Operation: ODataBatchOperationRequestMessage operationRequestMessage = batchReader.CreateOperationRequestMessage(); ODataBatchOperationResponseMessage operationResponseMessage = batchWriter.CreateOperationResponseMessage(operationRequestMessage.ContentId); HttpMethod method = Utility.CreateHttpMethod(operationRequestMessage.Method); switch (method) { case HttpMethod.GET: new QueryHandler(this, operationRequestMessage.Url, operationRequestMessage.Headers).Process(operationRequestMessage, operationResponseMessage); break; case HttpMethod.POST: new CreateHandler(this, operationRequestMessage.Url, operationRequestMessage.Headers).Process(operationRequestMessage, operationResponseMessage); break; case HttpMethod.DELETE: new DeleteHandler(this, operationRequestMessage.Url, operationRequestMessage.Headers).Process(operationRequestMessage, operationResponseMessage); break; case HttpMethod.PATCH: case HttpMethod.PUT: new UpdateHandler(this, method, operationRequestMessage.Url, operationRequestMessage.Headers).Process(operationRequestMessage, operationResponseMessage); break; default: new ErrorHandler(this, Utility.BuildException(HttpStatusCode.MethodNotAllowed)).Process(operationRequestMessage, operationResponseMessage); break; } break; } } } batchWriter.WriteEndBatch(); } }
/// <summary> /// Processed the operation response reported by the batch reader. /// This is a side-effecting method that is tied deeply to how it is used in the batch processing pipeline. /// </summary> /// <param name="batchReader">The batch reader to get the operation response from.</param> /// <param name="isChangesetOperation">True if the current operation is inside a changeset (implying CUD, not query)</param> /// <returns>An exception if the operation response is an error response, null for success response.</returns> private Exception ProcessCurrentOperationResponse(ODataBatchReader batchReader, bool isChangesetOperation) { Debug.Assert(batchReader != null, "batchReader != null"); Debug.Assert(batchReader.State == ODataBatchReaderState.Operation, "This method requires the batch reader to be on an operation."); ODataBatchOperationResponseMessage operationResponseMessage = batchReader.CreateOperationResponseMessage(); Descriptor descriptor = null; if (isChangesetOperation) { // We need to peek at the content-Id before handing the response to the user, so we can expose the Descriptor them. // We're OK with this exception to our general rule of not using them before ReceivingResponse event is fired. this.entryIndex = this.ValidateContentID(operationResponseMessage.ContentId); descriptor = this.ChangedEntries[entryIndex]; } // If we hit en error inside a batch, we will never expose a descriptor since we don't know which one to return. // The descriptor we fetched above based on the content-ID is bogus because the server returns an errounous content-id when // it hits an error inside batch. if (!WebUtil.SuccessStatusCode((HttpStatusCode)operationResponseMessage.StatusCode)) { descriptor = null; } this.RequestInfo.Context.FireReceivingResponseEvent(new ReceivingResponseEventArgs(operationResponseMessage, descriptor, true)); // We need to know if the content of the operation response is empty or not. // We also need to cache the entire content, since in case of GET response the response itself will be parsed // lazily and so it can happen that we will move the batch reader after this operation before we actually read // the content of the operation. Stream originalOperationResponseContentStream = operationResponseMessage.GetStream(); if (originalOperationResponseContentStream == null) { Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream); } MemoryStream operationResponseContentStream; try { operationResponseContentStream = new MemoryStream(); WebUtil.CopyStream(originalOperationResponseContentStream, operationResponseContentStream, ref this.streamCopyBuffer); operationResponseContentStream.Position = 0; } finally { originalOperationResponseContentStream.Dispose(); } this.currentOperationResponse = new CurrentOperationResponse( (HttpStatusCode)operationResponseMessage.StatusCode, operationResponseMessage.Headers, operationResponseContentStream); Version responseVersion; string headerName = XmlConstants.HttpODataVersion; return(BaseSaveResult.HandleResponse( this.RequestInfo, this.currentOperationResponse.StatusCode, this.currentOperationResponse.Headers.GetHeader(headerName), () => this.currentOperationResponse.ContentStream, false, out responseVersion)); }
/// <summary> /// Return the response message for this operation. /// </summary> /// <returns>ODataBatchOperationResponseMessage instance for this batch operation.</returns> internal ODataBatchOperationResponseMessage GetOperationResponseMessage() { return this.operationMessage ?? (this.operationMessage = this.writer.CreateOperationResponseMessage(this.contentId)); }