private ExpectedClientRequest CreateEntityUpdateRequest(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { var request = new ExpectedClientRequest() { Verb = GetUpdateVerb(options), Uri = entityDescriptorData.EditLink, }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); var dsv = GetDataServiceVersion(request.Verb, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); request.Body = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); string hintString = @"Entity update\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return(request); }
private ExpectedClientRequest CreateSaveStreamRequest(StreamDescriptorData streamDescriptorData, Uri requestUri) { HttpVerb verb; if (streamDescriptorData.State == EntityStates.Added) { verb = HttpVerb.Post; } else { ExceptionUtilities.Assert(streamDescriptorData.State == EntityStates.Modified, "Stream descriptor state should only be added or modified. State was: '{0}'", streamDescriptorData.State); verb = HttpVerb.Put; } var request = new ExpectedClientRequest() { Verb = verb, Uri = requestUri }; foreach (var header in streamDescriptorData.SaveStream.Headers) { request.Headers.Add(header); } request.Body = new PrimitiveValue(null, streamDescriptorData.SaveStream.StreamLogger.GetAllBytesRead()); return(request); }
private ExpectedClientRequest CreateEntityInsertRequest(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { ExceptionUtilities.Assert(!entityDescriptorData.IsMediaLinkEntry, "Can only be used for non media-link-entries"); var insertUri = GetEntityInsertUri(contextData, entityDescriptorData); ExpectedClientRequest request = new ExpectedClientRequest() { Verb = HttpVerb.Post, Uri = insertUri }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); DataServiceProtocolVersion dsv = GetDataServiceVersion(HttpVerb.Post, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); var payload = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); request.Body = payload; this.AddFoldedLinksToEntityInsertPayload(contextData, entityDescriptorData, payload); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); string hintString = @"Entity insert\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return(request); }
private void SetDefaultAcceptHeader(ExpectedClientRequest request, SaveChangesOptions options) { request.Headers[HttpHeaders.Accept] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? DefaultAccept : this.ClientRequestAcceptHeader; #if !SILVERLIGHT request.Headers[HttpHeaders.AcceptCharset] = DefaultEncoding; #else // Silverlight does not specify AcceptCharset at the top level "because the http stack does not support it" request.Headers[HttpHeaders.AcceptCharset] = (options == SaveChangesOptions.Batch) ? DefaultEncoding : null; #endif }
internal static void SetVerbTunnelling(ExpectedClientRequest request, bool useTunnelling) { if (useTunnelling) { request.Headers[HttpHeaders.HttpMethod] = request.Verb.ToHttpMethod(); request.Verb = HttpVerb.Post; } else { request.Headers[HttpHeaders.HttpMethod] = null; } }
private static void SetContentTypeHeaderForStream(ExpectedClientRequest request, StreamDescriptorData streamDescriptorData) { request.Headers[HttpHeaders.ContentType] = streamDescriptorData.ContentType; if (streamDescriptorData.ContentType == null) { request.Headers[HttpHeaders.ContentType] = MimeTypes.Any; } foreach (var header in streamDescriptorData.SaveStream.Headers) { request.Headers[header.Key] = header.Value; } request.Headers[HttpHeaders.Accept] = DefaultAccept; }
private static void SetContentIDHeader(DescriptorData descriptor, ExpectedClientRequest expectedRequest, bool isBatch) { if (isBatch) { expectedRequest.Headers[HttpHeaders.ContentId] = descriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture); var entityDescriptorData = descriptor as EntityDescriptorData; if (entityDescriptorData != null && entityDescriptorData.Identity == null) { entityDescriptorData.Identity = new Uri("$" + descriptor.ChangeOrder, UriKind.Relative); entityDescriptorData.EditLink = entityDescriptorData.Identity; } } else { expectedRequest.Headers[HttpHeaders.ContentId] = null; } }
private ExpectedClientRequest CreateEntityDeleteRequest(EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { var request = new ExpectedClientRequest() { Verb = HttpVerb.Delete, Uri = entityDescriptorData.EditLink, }; request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag; request.Headers[HttpHeaders.Prefer] = null; request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4); this.SetDefaultAcceptHeader(request, options); request.Headers[HttpHeaders.ContentType] = null; string hintString = @"Entity delete\r\n{{\r\n Descriptor = {0}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData); return(request); }
private ExpectedClientRequest CreateLinkRequest(LinkDescriptorData linkDescriptorData, SaveChangesOptions options) { var info = linkDescriptorData.SourceDescriptor.LinkInfos.SingleOrDefault(l => l.Name == linkDescriptorData.SourcePropertyName); ExpectedClientRequest request = new ExpectedClientRequest() { Uri = BuildLinkUri(linkDescriptorData, info) }; if (linkDescriptorData.State == EntityStates.Added) { request.Verb = HttpVerb.Post; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } else if (linkDescriptorData.State == EntityStates.Modified) { if (linkDescriptorData.TargetDescriptor == null) { request.Verb = HttpVerb.Delete; } else { request.Verb = HttpVerb.Put; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } } else { ExceptionUtilities.Assert(linkDescriptorData.State == EntityStates.Deleted, "Link descriptor was in unexpected state '{0}'", linkDescriptorData.State); string keyString = this.EntityDescriptorValueCalculator.CalculateEntityKey(linkDescriptorData.TargetDescriptor.Entity); request.Uri = new Uri(request.Uri.OriginalString + keyString); request.Verb = HttpVerb.Delete; } request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = null; request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4); this.SetDefaultAcceptHeader(request, options); if (request.Verb != HttpVerb.Delete) { request.Headers[HttpHeaders.ContentType] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? MimeTypes.ApplicationXml : this.ClientRequestAcceptHeader; } else { request.Headers[HttpHeaders.ContentType] = null; } string hintString = @"Link\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, linkDescriptorData, options); return request; }
private void SetContentTypeHeaderForEntity(ExpectedClientRequest request) { request.Headers[HttpHeaders.ContentType] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? MimeTypes.ApplicationAtomXml : this.ClientRequestAcceptHeader; }
private void CompareRequest(ExpectedClientRequest expected, IHttpRequest actual) { this.parent.Assert.AreEqual(expected.Verb, actual.Verb, "Request verb did not match"); // The headers are quite different when using XmlHttp if (!this.contextData.UsesXmlHttpStack()) { this.CompareHeaders(expected.Headers, actual.Headers); } if (expected.Body == null) { int actualLength = 0; var actualBody = actual.GetRequestBody(); if (actualBody != null) { actualLength = actualBody.Length; } this.parent.Assert.AreEqual(0, actualLength, "Request should not have had a body"); } else { ODataPayloadElement actualPayload; if (expected.Body.ElementType == ODataPayloadElementType.EntityInstance) { actualPayload = actual.DeserializeAndCast <EntityInstance>(this.parent.FormatSelector); } else if (expected.Body.ElementType == ODataPayloadElementType.DeferredLink) { actualPayload = actual.DeserializeAndCast <DeferredLink>(this.parent.FormatSelector); } else { ExceptionUtilities.Assert(expected.Body.ElementType == ODataPayloadElementType.PrimitiveValue, "Expected payload element was neither an entity, a link, nor a stream"); actualPayload = new PrimitiveValue(null, actual.GetRequestBody()); } try { this.parent.PayloadComparer.Compare(expected.Body, actualPayload); } catch (TestFailedException e) { this.parent.Log.WriteLine(LogLevel.Error, "Expected client request payload did not match actual."); var strategy = this.parent.FormatSelector.GetStrategy(actual.GetHeaderValueIfExists(HttpHeaders.ContentType), null); var expectedBinary = strategy.GetSerializer().SerializeToBinary(expected.Body); this.parent.Log.WriteLine(LogLevel.Verbose, "Expected request:"); var expectedToLog = new HttpRequestData() { Verb = expected.Verb, Uri = expected.Uri, Body = expectedBinary }; expectedToLog.Headers.AddRange(expected.Headers); expectedToLog.WriteToLog(this.parent.Log, LogLevel.Verbose); this.parent.Log.WriteLine(LogLevel.Verbose, "Actual request:"); actual.WriteToLog(this.parent.Log, LogLevel.Verbose); // wrap to preserve call stack throw new AssertionFailedException("Expected client request payload did not match actual.", e); } } }
private ExpectedClientRequest CreateEntityUpdateRequest(DataServiceContextData contextData, IDictionary<object, IEnumerable<NamedValue>> propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { var request = new ExpectedClientRequest() { Verb = GetUpdateVerb(options), Uri = entityDescriptorData.EditLink, }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); var dsv = GetDataServiceVersion(request.Verb, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); request.Body = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); string hintString = @"Entity update\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return request; }
private ExpectedClientRequest CreateLinkRequest(LinkDescriptorData linkDescriptorData, SaveChangesOptions options) { var info = linkDescriptorData.SourceDescriptor.LinkInfos.SingleOrDefault(l => l.Name == linkDescriptorData.SourcePropertyName); ExpectedClientRequest request = new ExpectedClientRequest() { Uri = BuildLinkUri(linkDescriptorData, info) }; if (linkDescriptorData.State == EntityStates.Added) { request.Verb = HttpVerb.Post; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } else if (linkDescriptorData.State == EntityStates.Modified) { if (linkDescriptorData.TargetDescriptor == null) { request.Verb = HttpVerb.Delete; } else { request.Verb = HttpVerb.Put; // note: the edit-link is used rather than identity because the server needs to be able to query for the target entity // and the identity may not be an actual uri request.Body = new DeferredLink() { UriString = linkDescriptorData.TargetDescriptor.EditLink.OriginalString }; } } else { ExceptionUtilities.Assert(linkDescriptorData.State == EntityStates.Deleted, "Link descriptor was in unexpected state '{0}'", linkDescriptorData.State); string keyString = this.EntityDescriptorValueCalculator.CalculateEntityKey(linkDescriptorData.TargetDescriptor.Entity); request.Uri = new Uri(request.Uri.OriginalString + keyString); request.Verb = HttpVerb.Delete; } request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = null; request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4); this.SetDefaultAcceptHeader(request, options); if (request.Verb != HttpVerb.Delete) { request.Headers[HttpHeaders.ContentType] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? MimeTypes.ApplicationXml : this.ClientRequestAcceptHeader; } else { request.Headers[HttpHeaders.ContentType] = null; } string hintString = @"Link\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, linkDescriptorData, options); return(request); }
private void SetDefaultAcceptHeader(ExpectedClientRequest request, SaveChangesOptions options) { request.Headers[HttpHeaders.Accept] = string.IsNullOrWhiteSpace(this.ClientRequestAcceptHeader) ? DefaultAccept : this.ClientRequestAcceptHeader; request.Headers[HttpHeaders.AcceptCharset] = DefaultEncoding; }
/// <summary> /// Calculates expected data for a request during DataServiceContext.SaveChanges for a particular descriptor. /// </summary> /// <param name="contextData">The context data</param> /// <param name="propertyValuesBeforeSave">The property values of the tracked client objects before the call to SaveChanges</param> /// <param name="descriptorData">The descriptor data</param> /// <param name="options">The save changes options</param> /// <returns>The expected client request</returns> public ExpectedClientRequest CalculateRequest(DataServiceContextData contextData, IDictionary <object, IEnumerable <NamedValue> > propertyValuesBeforeSave, DescriptorData descriptorData, SaveChangesOptions options) { ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(descriptorData, "descriptorData"); ExceptionUtilities.CheckArgumentNotNull(propertyValuesBeforeSave, "propertyValuesBeforeSave"); var linkDescriptorData = descriptorData as LinkDescriptorData; var entityDescriptorData = descriptorData as EntityDescriptorData; var streamDescriptorData = descriptorData as StreamDescriptorData; ExpectedClientRequest request = null; if (linkDescriptorData != null) { if (linkDescriptorData.WillTriggerSeparateRequest()) { request = this.CreateLinkRequest(linkDescriptorData, options); } } else if (entityDescriptorData != null) { if (entityDescriptorData.State == EntityStates.Added) { request = this.CreateEntityInsertRequest(contextData, propertyValuesBeforeSave, entityDescriptorData, options); } else if (entityDescriptorData.State == EntityStates.Modified) { request = this.CreateEntityUpdateRequest(contextData, propertyValuesBeforeSave, entityDescriptorData, options); } else if (entityDescriptorData.State == EntityStates.Deleted) { request = this.CreateEntityDeleteRequest(entityDescriptorData, options); } } else if (streamDescriptorData != null) { if (streamDescriptorData.State == EntityStates.Added) { request = this.CreateStreamInsertRequest(contextData, streamDescriptorData, options); } else if (streamDescriptorData.State == EntityStates.Modified) { request = this.CreateStreamUpdateRequest(streamDescriptorData, options); } } if (request != null) { request.Headers[HttpHeaders.MaxDataServiceVersion] = ToClientHeaderFormat(contextData.MaxProtocolVersion); // perform sanity checks var missingHeaders = headersThatWillBeGenerated.Where(h => !request.Headers.ContainsKey(h)).ToArray(); ExceptionUtilities.Assert(missingHeaders.Length == 0, "Generated request was missing headers: {0}", string.Join(", ", missingHeaders)); ExceptionUtilities.CheckObjectNotNull(request.Uri, "Generated request was missing a Uri"); // sanity check: Client sends content-type header for delete request if (request.GetEffectiveVerb() == HttpVerb.Delete) { ExceptionUtilities.Assert( request.Headers[HttpHeaders.ContentType] == null, "Incorrect expectation: client should never send ContentType header for DELETE requests."); } } return(request); }
private ExpectedClientRequest CreateEntityInsertRequest(DataServiceContextData contextData, IDictionary<object, IEnumerable<NamedValue>> propertyValuesBeforeSave, EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { ExceptionUtilities.Assert(!entityDescriptorData.IsMediaLinkEntry, "Can only be used for non media-link-entries"); var insertUri = GetEntityInsertUri(contextData, entityDescriptorData); ExpectedClientRequest request = new ExpectedClientRequest() { Verb = HttpVerb.Post, Uri = insertUri }; string preference = contextData.AddAndUpdateResponsePreference.ToHeaderValue(); DataServiceProtocolVersion dsv = GetDataServiceVersion(HttpVerb.Post, preference); dsv = dsv.IncreaseVersionIfRequired(this.VersionCalculator.CalculateDataServiceVersion(entityDescriptorData, contextData.MaxProtocolVersion)); var payload = this.BuildEntityPayload(contextData, propertyValuesBeforeSave, entityDescriptorData, dsv); request.Body = payload; this.AddFoldedLinksToEntityInsertPayload(contextData, entityDescriptorData, payload); request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(dsv); request.Headers[HttpHeaders.IfMatch] = null; request.Headers[HttpHeaders.Prefer] = preference; this.SetDefaultAcceptHeader(request, options); this.SetContentTypeHeaderForEntity(request); string hintString = @"Entity insert\r\n{{\r\n Descriptor = {0}\r\n Options = {1}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData, options); return request; }
private ExpectedClientRequest CreateEntityDeleteRequest(EntityDescriptorData entityDescriptorData, SaveChangesOptions options) { var request = new ExpectedClientRequest() { Verb = HttpVerb.Delete, Uri = entityDescriptorData.EditLink, }; request.Headers[HttpHeaders.IfMatch] = entityDescriptorData.ETag; request.Headers[HttpHeaders.Prefer] = null; request.Headers[HttpHeaders.DataServiceVersion] = ToClientHeaderFormat(DataServiceProtocolVersion.V4); this.SetDefaultAcceptHeader(request, options); request.Headers[HttpHeaders.ContentType] = null; string hintString = @"Entity delete\r\n{{\r\n Descriptor = {0}\r\n}}"; request.DebugHintString = string.Format(CultureInfo.InvariantCulture, hintString, entityDescriptorData); return request; }
private ExpectedClientRequest CreateSaveStreamRequest(StreamDescriptorData streamDescriptorData, Uri requestUri) { HttpVerb verb; if (streamDescriptorData.State == EntityStates.Added) { verb = HttpVerb.Post; } else { ExceptionUtilities.Assert(streamDescriptorData.State == EntityStates.Modified, "Stream descriptor state should only be added or modified. State was: '{0}'", streamDescriptorData.State); verb = HttpVerb.Put; } var request = new ExpectedClientRequest() { Verb = verb, Uri = requestUri }; foreach (var header in streamDescriptorData.SaveStream.Headers) { request.Headers.Add(header); } request.Body = new PrimitiveValue(null, streamDescriptorData.SaveStream.StreamLogger.GetAllBytesRead()); return request; }
private void CompareRequest(ExpectedClientRequest expected, IHttpRequest actual) { this.parent.Assert.AreEqual(expected.Verb, actual.Verb, "Request verb did not match"); // The headers are quite different when using XmlHttp if (!this.contextData.UsesXmlHttpStack()) { this.CompareHeaders(expected.Headers, actual.Headers); } if (expected.Body == null) { int actualLength = 0; var actualBody = actual.GetRequestBody(); if (actualBody != null) { actualLength = actualBody.Length; } this.parent.Assert.AreEqual(0, actualLength, "Request should not have had a body"); } else { ODataPayloadElement actualPayload; if (expected.Body.ElementType == ODataPayloadElementType.EntityInstance) { actualPayload = actual.DeserializeAndCast<EntityInstance>(this.parent.FormatSelector); } else if (expected.Body.ElementType == ODataPayloadElementType.DeferredLink) { actualPayload = actual.DeserializeAndCast<DeferredLink>(this.parent.FormatSelector); } else { ExceptionUtilities.Assert(expected.Body.ElementType == ODataPayloadElementType.PrimitiveValue, "Expected payload element was neither an entity, a link, nor a stream"); actualPayload = new PrimitiveValue(null, actual.GetRequestBody()); } try { this.parent.PayloadComparer.Compare(expected.Body, actualPayload); } catch (TestFailedException e) { this.parent.Log.WriteLine(LogLevel.Error, "Expected client request payload did not match actual."); var strategy = this.parent.FormatSelector.GetStrategy(actual.GetHeaderValueIfExists(HttpHeaders.ContentType), null); var expectedBinary = strategy.GetSerializer().SerializeToBinary(expected.Body); this.parent.Log.WriteLine(LogLevel.Verbose, "Expected request:"); var expectedToLog = new HttpRequestData() { Verb = expected.Verb, Uri = expected.Uri, Body = expectedBinary }; expectedToLog.Headers.AddRange(expected.Headers); expectedToLog.WriteToLog(this.parent.Log, LogLevel.Verbose); this.parent.Log.WriteLine(LogLevel.Verbose, "Actual request:"); actual.WriteToLog(this.parent.Log, LogLevel.Verbose); // wrap to preserve call stack throw new AssertionFailedException("Expected client request payload did not match actual.", e); } } }