diff --git a/src/Microsoft.OData.Core/Json/ODataJsonResourceDeserializer.cs b/src/Microsoft.OData.Core/Json/ODataJsonResourceDeserializer.cs index 36be01404a..72d8cca062 100644 --- a/src/Microsoft.OData.Core/Json/ODataJsonResourceDeserializer.cs +++ b/src/Microsoft.OData.Core/Json/ODataJsonResourceDeserializer.cs @@ -137,49 +137,22 @@ internal ODataDeletedResource ReadDeletedResource() this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); ODataDeletedResource deletedResource = null; + DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; + Uri id = null; // If the current node is the deleted property - read it. if (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.GetPropertyName(); + + // Case 1: removed property first if (string.Equals(ODataJsonConstants.PrefixedODataRemovedPropertyName, propertyName, StringComparison.Ordinal) || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) { - DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; - Uri id = null; - // Read over the property to move to its value. this.JsonReader.Read(); - // Read the removed object and extract the reason, if present - this.AssertJsonCondition(JsonNodeType.StartObject, JsonNodeType.PrimitiveValue /*null*/); - if (this.JsonReader.NodeType != JsonNodeType.PrimitiveValue) - { - while (this.JsonReader.NodeType != JsonNodeType.EndObject && this.JsonReader.Read()) - { - // If the current node is the reason property - read it. - if (this.JsonReader.NodeType == JsonNodeType.Property && - string.Equals(ODataJsonConstants.ODataReasonPropertyName, this.JsonReader.GetPropertyName(), StringComparison.Ordinal)) - { - // Read over the property to move to its value. - this.JsonReader.Read(); - - // Read the reason value. - if (string.Equals(ODataJsonConstants.ODataReasonDeletedValue, this.JsonReader.ReadStringValue(), StringComparison.Ordinal)) - { - reason = DeltaDeletedEntryReason.Deleted; - } - } - } - } - else - { - object value = this.JsonReader.GetValue(); - if (value != null) - { - throw new ODataException(Error.Format(SRResources.ODataJsonResourceDeserializer_DeltaRemovedAnnotationMustBeObject, value)); - } - } + reason = ReadDeletedReason(); // read over end object or null value this.JsonReader.Read(); @@ -204,6 +177,31 @@ internal ODataDeletedResource ReadDeletedResource() deletedResource = ReaderUtils.CreateDeletedResource(id, reason); } + //// Case 2: id property first + //else if (this.JsonReader.NodeType == JsonNodeType.Property && !propertyName.EndsWith(ODataJsonConstants.SimplifiedODataDeltaPropertyName) && !propertyName.Contains(ODataJsonConstants.ODataPropertyAnnotationSeparatorChar)) + //{ + // // read over end object or null value + // this.JsonReader.Read(); + + // ReadOnlyMemory idPropertyName = propertyName.AsMemory(); + // object value = this.JsonReader.ReadPrimitiveValue(); + + // // If the next property is the removed property - read it. + // propertyName = this.JsonReader.GetPropertyName(); + // if (string.Equals(ODataJsonConstants.PrefixedODataRemovedPropertyName, propertyName, StringComparison.Ordinal) + // || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) + // { + // // Read over the property to move to its value. + // this.JsonReader.Read(); + + // reason = ReadDeletedReason(); + + // // Read over end object or null value + // this.JsonReader.Read(); + + // deletedResource = ReaderUtils.CreateDeletedResource(id, reason, KeyValuePair.Create(idPropertyName.Span.ToString(), value)); + // } + //} } this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); @@ -2431,79 +2429,75 @@ internal async Task ReadDeletedResourceAsync() { this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); + if (this.JsonReader.NodeType != JsonNodeType.Property) + { + this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); + return null; + } + ODataDeletedResource deletedResource = null; + DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; + Uri id = null; - // If the current node is the deleted property - read it. - if (this.JsonReader.NodeType == JsonNodeType.Property) + string propertyName = await this.JsonReader.GetPropertyNameAsync().ConfigureAwait(false); + + // Case 1: removed property first + if (string.Equals(ODataJsonConstants.PrefixedODataRemovedPropertyName, propertyName, StringComparison.Ordinal) + || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) { - string propertyName = await this.JsonReader.GetPropertyNameAsync() - .ConfigureAwait(false); - if (string.Equals(ODataJsonConstants.PrefixedODataRemovedPropertyName, propertyName, StringComparison.Ordinal) - || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) + // Read over the property to move to its value. + await this.JsonReader.ReadAsync().ConfigureAwait(false); + + reason = await ReadDeletedReasonAsync(); + + // Read over end object or null value + await this.JsonReader.ReadAsync().ConfigureAwait(false); + + // A deleted object must have at least either the odata id annotation or the key values + if (this.JsonReader.NodeType != JsonNodeType.Property) { - DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; - Uri id = null; + throw new ODataException(SRResources.ODataWriterCore_DeltaResourceWithoutIdOrKeyProperties); + } + // If the next property is the id property - read it. + propertyName = await this.JsonReader.GetPropertyNameAsync().ConfigureAwait(false); + if (string.Equals(ODataJsonConstants.PrefixedODataIdPropertyName, propertyName, StringComparison.Ordinal) + || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataIdPropertyName, propertyName)) + { // Read over the property to move to its value. - await this.JsonReader.ReadAsync() - .ConfigureAwait(false); + await this.JsonReader.ReadAsync().ConfigureAwait(false); - // Read the removed object and extract the reason, if present - this.AssertJsonCondition(JsonNodeType.StartObject, JsonNodeType.PrimitiveValue /*null*/); - object removedValue; - if (this.JsonReader.NodeType != JsonNodeType.PrimitiveValue) - { - while (this.JsonReader.NodeType != JsonNodeType.EndObject && await this.JsonReader.ReadAsync().ConfigureAwait(false)) - { - // If the current node is the reason property - read it. - if (this.JsonReader.NodeType == JsonNodeType.Property - && string.Equals(ODataJsonConstants.ODataReasonPropertyName, await this.JsonReader.GetPropertyNameAsync().ConfigureAwait(false), StringComparison.Ordinal)) - { - // Read over the property to move to its value. - await this.JsonReader.ReadAsync() - .ConfigureAwait(false); + // Read the id value. + id = UriUtils.StringToUri(await this.JsonReader.ReadStringValueAsync().ConfigureAwait(false)); + } - // Read the reason value. - if (string.Equals(ODataJsonConstants.ODataReasonDeletedValue, await this.JsonReader.ReadStringValueAsync().ConfigureAwait(false), StringComparison.Ordinal)) - { - reason = DeltaDeletedEntryReason.Deleted; - } - } - } - } - else if ((removedValue = await this.JsonReader.GetValueAsync().ConfigureAwait(false)) != null) - { - throw new ODataException( - Error.Format(SRResources.ODataJsonResourceDeserializer_DeltaRemovedAnnotationMustBeObject, removedValue)); - } + deletedResource = ReaderUtils.CreateDeletedResource(id, reason); + } + //else if (string.Equals(ODataJsonConstants.ODataIdPropertyName, propertyName, StringComparison.OrdinalIgnoreCase) + // || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) + //{ + // // read over end object or null value + // this.JsonReader.Read(); - // Read over end object or null value - await this.JsonReader.ReadAsync() - .ConfigureAwait(false); + // ReadOnlyMemory idPropertyName = propertyName.AsMemory(); + // object value = this.JsonReader.ReadPrimitiveValue(); - // A deleted object must have at least either the odata id annotation or the key values - if (this.JsonReader.NodeType != JsonNodeType.Property) - { - throw new ODataException(SRResources.ODataWriterCore_DeltaResourceWithoutIdOrKeyProperties); - } + // // If the next property is the removed property - read it. + // propertyName = this.JsonReader.GetPropertyName(); + // if (string.Equals(ODataJsonConstants.PrefixedODataRemovedPropertyName, propertyName, StringComparison.Ordinal) + // || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataRemovedPropertyName, propertyName)) + // { + // // Read over the property to move to its value. + // this.JsonReader.Read(); - // If the next property is the id property - read it. - propertyName = await this.JsonReader.GetPropertyNameAsync() - .ConfigureAwait(false); - if (string.Equals(ODataJsonConstants.PrefixedODataIdPropertyName, propertyName, StringComparison.Ordinal) - || this.CompareSimplifiedODataAnnotation(ODataJsonConstants.SimplifiedODataIdPropertyName, propertyName)) - { - // Read over the property to move to its value. - await this.JsonReader.ReadAsync() - .ConfigureAwait(false); + // reason = ReadDeletedReason(); - // Read the id value. - id = UriUtils.StringToUri(await this.JsonReader.ReadStringValueAsync().ConfigureAwait(false)); - } + // // Read over end object or null value + // this.JsonReader.Read(); - deletedResource = ReaderUtils.CreateDeletedResource(id, reason); - } - } + // deletedResource = ReaderUtils.CreateDeletedResource(id, reason, KeyValuePair.Create(idPropertyName.Span.ToString(), value)); + // } + //} this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); @@ -4332,5 +4326,90 @@ await this.JsonReader.ReadEndArrayAsync() this.JsonReader.AssertNotBuffering(); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); } + + /// + /// Reads the reason for a deleted entry. + /// + /// The reason for the deleted entry. + /// + private DeltaDeletedEntryReason ReadDeletedReason() + { + DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; + + // Read the removed object and extract the reason, if present + this.AssertJsonCondition(JsonNodeType.StartObject, JsonNodeType.PrimitiveValue /*null*/); + + if (this.JsonReader.NodeType != JsonNodeType.PrimitiveValue) + { + while (this.JsonReader.NodeType != JsonNodeType.EndObject && this.JsonReader.Read()) + { + // If the current node is the reason property - read it. + if (this.JsonReader.NodeType == JsonNodeType.Property && + string.Equals(ODataJsonConstants.ODataReasonPropertyName, this.JsonReader.GetPropertyName(), StringComparison.Ordinal)) + { + // Read over the property to move to its value. + this.JsonReader.Read(); + + // Read the reason value. + if (string.Equals(ODataJsonConstants.ODataReasonDeletedValue, this.JsonReader.ReadStringValue(), StringComparison.Ordinal)) + { + reason = DeltaDeletedEntryReason.Deleted; + } + } + } + } + else + { + object value = this.JsonReader.GetValue(); + if (value != null) + { + throw new ODataException(Error.Format(SRResources.ODataJsonResourceDeserializer_DeltaRemovedAnnotationMustBeObject, value)); + } + } + + return reason; + } + + /// + /// Asynchronously reads the reason for a deleted entry. + /// + /// The reason for the deleted entry. + /// + private async Task ReadDeletedReasonAsync() + { + DeltaDeletedEntryReason reason = DeltaDeletedEntryReason.Changed; + + // Read the removed object and extract the reason, if present + this.AssertJsonCondition(JsonNodeType.StartObject, JsonNodeType.PrimitiveValue /*null*/); + + object removedValue; + if (this.JsonReader.NodeType != JsonNodeType.PrimitiveValue) + { + while (this.JsonReader.NodeType != JsonNodeType.EndObject && await this.JsonReader.ReadAsync().ConfigureAwait(false)) + { + // If the current node is the reason property - read it. + if (this.JsonReader.NodeType == JsonNodeType.Property + && string.Equals(ODataJsonConstants.ODataReasonPropertyName, await this.JsonReader.GetPropertyNameAsync().ConfigureAwait(false), StringComparison.Ordinal)) + { + // Read over the property to move to its value. + await this.JsonReader.ReadAsync() + .ConfigureAwait(false); + + // Read the reason value. + if (string.Equals(ODataJsonConstants.ODataReasonDeletedValue, await this.JsonReader.ReadStringValueAsync().ConfigureAwait(false), StringComparison.Ordinal)) + { + reason = DeltaDeletedEntryReason.Deleted; + } + } + } + } + else if ((removedValue = await this.JsonReader.GetValueAsync().ConfigureAwait(false)) != null) + { + throw new ODataException( + Error.Format(SRResources.ODataJsonResourceDeserializer_DeltaRemovedAnnotationMustBeObject, removedValue)); + } + + return reason; + } } } diff --git a/src/Microsoft.OData.Core/ReaderUtils.cs b/src/Microsoft.OData.Core/ReaderUtils.cs index 74dc855636..d81f677af5 100644 --- a/src/Microsoft.OData.Core/ReaderUtils.cs +++ b/src/Microsoft.OData.Core/ReaderUtils.cs @@ -8,10 +8,11 @@ namespace Microsoft.OData { #region Namespaces using System; + using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; - using Microsoft.OData.Metadata; using Microsoft.OData.Edm; + using Microsoft.OData.Metadata; #endregion Namespaces /// @@ -66,13 +67,29 @@ internal static ODataResource CreateNewResource() /// /// The id of the deleted resource, or null if not yet known. /// The for the deleted resource. + /// The key of the deleted resource, or null if not yet known. /// The newly created deleted resource. /// The method populates the Properties property with an empty read only enumeration. - internal static ODataDeletedResource CreateDeletedResource(Uri id, DeltaDeletedEntryReason reason) + internal static ODataDeletedResource CreateDeletedResource(Uri id, DeltaDeletedEntryReason reason, KeyValuePair? keyValue = null) { + if (keyValue == null) + { + return new ODataDeletedResource(id, reason) + { + Properties = new ReadOnlyEnumerable() + }; + } + return new ODataDeletedResource(id, reason) { - Properties = new ReadOnlyEnumerable() + Properties = new ReadOnlyEnumerable(new ODataPropertyInfo[] + { + new ODataProperty() + { + Name = keyValue.Value.Key, + Value = keyValue.Value.Value, + } + }) }; } diff --git a/test/EndToEndTests/Common/Microsoft.OData.E2E.TestCommon/Common/Server/Deltas/DeltaTestsController.cs b/test/EndToEndTests/Common/Microsoft.OData.E2E.TestCommon/Common/Server/Deltas/DeltaTestsController.cs new file mode 100644 index 0000000000..de1d461f91 --- /dev/null +++ b/test/EndToEndTests/Common/Microsoft.OData.E2E.TestCommon/Common/Server/Deltas/DeltaTestsController.cs @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OData.Deltas; +using Microsoft.AspNetCore.OData.Query; +using Microsoft.AspNetCore.OData.Routing.Controllers; +using Microsoft.OData.E2E.TestCommon.Common.Server.Default; + +namespace Microsoft.OData.E2E.TestCommon.Common.Server.Deltas; + +public class DeltaTestsController : ODataController +{ + private static DefaultDataSource _dataSource; + + [EnableQuery] + [HttpGet("odata/Customers({key})")] + public IActionResult GetCustomer([FromRoute] int key) + { + var customer = _dataSource.Customers?.FirstOrDefault(e => e.PersonID == key); + if (customer == null) + { + return NotFound(); + } + return Ok(customer); + } + + [EnableQuery] + [HttpGet("odata/Customers({customerKey})/Orders({orderKey})")] + public IActionResult GetOrder([FromRoute] int customerKey, [FromRoute] int orderKey) + { + var order = _dataSource.Customers?.FirstOrDefault(e => e.PersonID == customerKey)?.Orders?.FirstOrDefault(o => o.OrderID == orderKey); + if (order == null) + { + return NotFound(); + } + + return Ok(order); + } + + [HttpPatch("odata/Customers({key})")] + public IActionResult Patch([FromRoute] int key, [FromBody] Delta delta) + { + var customer = _dataSource.Customers?.FirstOrDefault(e => e.PersonID == key); + if (customer == null) + { + return NotFound(); + } + + if (delta.TryGetPropertyValue("Orders", out var orders)) + { + var orderDeltaSet = orders as DeltaSet; + if (orderDeltaSet != null) + { + foreach (Delta deltaUnit in orderDeltaSet) + { + if (deltaUnit.Kind == DeltaItemKind.Resource) + { + var id = deltaUnit.GetInstance().OrderID; + var order = customer.Orders.FirstOrDefault(o => o.OrderID == id); + if (order != null) + { + deltaUnit.Patch(order); + } + else + { + customer.Orders.Add(deltaUnit.GetInstance()); + } + } + else if (deltaUnit.Kind == DeltaItemKind.DeletedResource) + { + var id = deltaUnit.GetInstance().OrderID; + var order = customer.Orders.FirstOrDefault(o => o.OrderID == id); + if (order != null) + { + customer.Orders.Remove(order); + } + } + } + } + } + + return Ok(); + } + + [HttpPost("odata/deltatests/Default.ResetDefaultDataSource")] + public IActionResult ResetDefaultDataSource() + { + _dataSource = DefaultDataSource.CreateInstance(); + + return Ok(); + } +} diff --git a/test/EndToEndTests/Tests/Core/Microsoft.OData.Core.E2E.Tests/DeltaTests/DeltaTests.cs b/test/EndToEndTests/Tests/Core/Microsoft.OData.Core.E2E.Tests/DeltaTests/DeltaTests.cs index c610a5fe37..e09aa947f8 100644 --- a/test/EndToEndTests/Tests/Core/Microsoft.OData.Core.E2E.Tests/DeltaTests/DeltaTests.cs +++ b/test/EndToEndTests/Tests/Core/Microsoft.OData.Core.E2E.Tests/DeltaTests/DeltaTests.cs @@ -5,22 +5,26 @@ // //------------------------------------------------------------------------------ +using System.Net; +using System.Text; using Microsoft.AspNetCore.OData; using Microsoft.AspNetCore.OData.Routing.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.E2E.TestCommon; using Microsoft.OData.E2E.TestCommon.Common; +using Microsoft.OData.E2E.TestCommon.Common.Client.Default.Default; using Microsoft.OData.E2E.TestCommon.Common.Server.Default; +using Microsoft.OData.E2E.TestCommon.Common.Server.Deltas; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; using Microsoft.Spatial; -using System.Reflection; namespace Microsoft.OData.Core.E2E.Tests.DeltaTests { public class DeltaTests : EndToEndTestBase { private readonly Uri _baseUri; + private readonly Container _context; private readonly IEdmModel _model; private static readonly string NameSpacePrefix = "Microsoft.OData.E2E.TestCommon.Common.Server.Default."; @@ -28,7 +32,7 @@ public class TestsStartup : TestStartupBase { public override void ConfigureServices(IServiceCollection services) { - services.ConfigureControllers(typeof(MetadataController)); + services.ConfigureControllers(typeof(MetadataController), typeof(DeltaTestsController)); services.AddControllers().AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(null) .AddRouteComponents("odata", DefaultEdmModel.GetEdmModel())); @@ -39,7 +43,14 @@ public DeltaTests(TestWebApplicationFactory fixture) : base(fixture) { _baseUri = new Uri(Client.BaseAddress, "odata/"); + _context = new Container(_baseUri) + { + HttpClientFactory = HttpClientFactory + }; + _model = DefaultEdmModel.GetEdmModel(); + + ResetDefaultDataSource(); } [Theory] @@ -827,6 +838,42 @@ public async Task ADeltaResponse_WithSelectedAndExpandedNavProperties_IsWrittenA Assert.Equal(ODataReaderState.Completed, deltaReader.State); } + [Theory] + [InlineData("Customers(1)", "{\"Orders@delta\": [{\"OrderID\": 8, \"@removed\": { \"reason\": \"changed\" }}]}", 8)] + [InlineData("Customers(1)", "{\"FirstName\": \"Jane_1\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 8 }]}", 8)] + [InlineData("Customers(1)", "{\"FirstName\": \"Jane_2\", \"Orders@delta\": [{\"Id\": 8, \"@removed\": { \"reason\": \"changed\" }}]}", 8)] + [InlineData("Customers(2)", "{\"FirstName\": \"Doe_1\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 7 }, {\"OrderID\": 9, \"@removed\": { \"reason\": \"changed\" }}]}", 9)] + [InlineData("Customers(1)", "{\"Orders@delta\": [{\"OrderID\": 8, \"@removed\": { \"reason\": \"deleted\" }}]}", 8)] + [InlineData("Customers(2)", "{\"Orders@delta\": [{\"OrderID\": 9, \"@removed\": { \"reason\": \"changed\" }}, {\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 7 }]}", 9)] + [InlineData("Customers(2)", "{\"Orders@delta\": [{\"OrderID\": 9, \"@removed\": { \"reason\": \"deleted\" }}, {\"@removed\": { \"reason\": \"deleted\" }, \"OrderID\": 7 }]}", 9)] + [InlineData("Customers(2)", "{\"Orders@delta\": [{\"OrderID\": 9, \"@removed\": { \"reason\": \"deleted\" }}, {\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 7 }]}", 7)] + [InlineData("Customers(1)", "{\"FirstName\": \"Jane_3\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"deleted\" }, \"OrderID\": 8 }]}", 8)] + [InlineData("Customers(1)", "{\"FirstName\": \"Jane_4\", \"Orders@delta\": [{\"OrderID\": 8, \"@removed\": { \"reason\": \"deleted\" }}]}", 8)] + [InlineData("Customers(2)", "{\"FirstName\": \"Doe_2\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"deleted\" }, \"OrderID\": 7 }, {\"OrderID\": 9, \"@removed\": { \"reason\": \"deleted\" }}]}", 9)] + [InlineData("Customers(2)", "{\"FirstName\": \"Doe_3\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 7 }, {\"OrderID\": 9, \"@removed\": { \"reason\": \"deleted\" }}]}", 7)] + [InlineData("Customers(2)", "{\"FirstName\": \"Doe_4\", \"Orders@delta\": [{\"@removed\": { \"reason\": \"changed\" }, \"OrderID\": 7 }, {\"@removed\": { \"reason\": \"deleted\" }, \"OrderID\": 9}]}", 7)] + public async Task DeltaDeleteWithOrderedAndUnorderedPayload_WorksAsExpected_Async(string query, string payload, int removedID) + { + // Arrange + var requestUri = new Uri(_baseUri, query); + var orderQuery = requestUri.AbsoluteUri + $"/Orders({removedID})"; + + // Ensure the order to be removed exists + var existOrderResponse = await Client.GetAsync(orderQuery); + Assert.Equal(HttpStatusCode.OK, existOrderResponse.StatusCode); + + var content = new StringContent(payload, Encoding.UTF8, "application/json"); + + // Act + var response = await Client.PatchAsync(requestUri, content); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + // Ensure the order is removed + var removedOrderResponse = await Client.GetAsync(orderQuery); + Assert.Equal(HttpStatusCode.NotFound, removedOrderResponse.StatusCode); + } private ODataMessageWriterSettings CreateODataMessageWriterSettings(Uri requestUri) { @@ -846,5 +893,11 @@ private ODataMessageWriterSettings CreateODataMessageWriterSettings(Uri requestU return settings; } + + private void ResetDefaultDataSource() + { + var actionUri = new Uri(_baseUri + "deltatests/Default.ResetDefaultDataSource", UriKind.Absolute); + _context.Execute(actionUri, "POST"); + } } }