Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement asynchronous support in ODataJsonLightDeltaWriter #2082

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/Microsoft.OData.Core/JsonLight/ODataJsonLightDeltaWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public override void WriteStart(ODataDeltaResourceSet deltaResourceSet)
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteStartAsync(ODataDeltaResourceSet deltaResourceSet)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteStart(deltaResourceSet));
return this.resourceWriter.WriteStartAsync(deltaResourceSet);
}

/// <summary>
Expand All @@ -140,7 +140,7 @@ public override void WriteEnd()
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteEndAsync()
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteEnd());
return this.resourceWriter.WriteEndAsync();
}

/// <summary>
Expand All @@ -159,7 +159,7 @@ public override void WriteStart(ODataNestedResourceInfo nestedResourceInfo)
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteStartAsync(ODataNestedResourceInfo nestedResourceInfo)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteStart(nestedResourceInfo));
return this.resourceWriter.WriteStartAsync(nestedResourceInfo);
}

/// <summary>
Expand All @@ -178,7 +178,7 @@ public override void WriteStart(ODataResourceSet expandedResourceSet)
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteStartAsync(ODataResourceSet expandedResourceSet)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteStart(expandedResourceSet));
return this.resourceWriter.WriteStartAsync(expandedResourceSet);
}

/// <summary>
Expand All @@ -197,7 +197,7 @@ public override void WriteStart(ODataResource deltaResource)
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteStartAsync(ODataResource deltaResource)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteStart(deltaResource));
return this.resourceWriter.WriteStartAsync(deltaResource);
}

/// <summary>
Expand All @@ -217,7 +217,7 @@ public override void WriteDeltaDeletedEntry(ODataDeltaDeletedEntry deltaDeletedE
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteDeltaDeletedEntryAsync(ODataDeltaDeletedEntry deltaDeletedEntry)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteStart(ODataDeltaDeletedEntry.GetDeletedResource(deltaDeletedEntry)));
return this.resourceWriter.WriteStartAsync(ODataDeltaDeletedEntry.GetDeletedResource(deltaDeletedEntry));
}

/// <summary>
Expand All @@ -236,7 +236,7 @@ public override void WriteDeltaLink(ODataDeltaLink deltaLink)
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteDeltaLinkAsync(ODataDeltaLink deltaLink)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteDeltaLink(deltaLink));
return this.resourceWriter.WriteDeltaLinkAsync(deltaLink);
}

/// <summary>
Expand All @@ -255,7 +255,7 @@ public override void WriteDeltaDeletedLink(ODataDeltaDeletedLink deltaDeletedLin
/// <returns>A task instance that represents the asynchronous write operation.</returns>
public override Task WriteDeltaDeletedLinkAsync(ODataDeltaDeletedLink deltaDeletedLink)
{
return TaskUtils.GetTaskForSynchronousOperation(() => this.resourceWriter.WriteDeltaDeletedLink(deltaDeletedLink));
return this.resourceWriter.WriteDeltaDeletedLinkAsync(deltaDeletedLink);
}

/// <summary>
Expand Down Expand Up @@ -290,7 +290,7 @@ void IODataOutputInStreamErrorListener.OnInStreamError()
/// <inheritdoc/>
Task IODataOutputInStreamErrorListener.OnInStreamErrorAsync()
{
throw new NotImplementedException();
return this.inStreamErrorListener.OnInStreamErrorAsync();
marabooy marked this conversation as resolved.
Show resolved Hide resolved
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.OData.Edm;
using Microsoft.OData.JsonLight;
using Microsoft.OData.UriParser;
using Microsoft.OData.Edm;
using Xunit;

namespace Microsoft.OData.Tests.JsonLight
Expand Down Expand Up @@ -2684,6 +2685,145 @@ public void WriteDeltaDeletedLinkToNestedEntitySetShouldFail(bool isResponse)

#endregion 4.01 Tests

#region Async Tests

public static IEnumerable<object[]> GetWriteDeltaPayloadTestData()
{
var valueAsJson = "\"value\":[" +
"{\"@odata.id\":\"Customers('BOTTM')\",\"ContactName\":\"Susan Halvenstern\"}," +
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$deletedLink\"," +
"\"source\":\"Customers('ALFKI')\",\"relationship\":\"Orders\",\"target\":\"Orders('10643')\"}," +
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$link\"," +
"\"source\":\"Customers('BOTTM')\",\"relationship\":\"Orders\",\"target\":\"Orders('10645')\"}," +
"{\"@odata.context\":\"http://host/service/$metadata#Orders/$entity\"," +
"\"@odata.id\":\"Orders(10643)\"," +
"\"ShippingAddress\":{\"Street\":\"23 Tsawassen Blvd.\",\"City\":\"Tsawassen\",\"Region\":\"BC\",\"PostalCode\":\"T2F 8M4\"}},";

yield return new object[]
{
new ODataDeltaResourceSet
{
Count = 5,
DeltaLink = new Uri("Customers?$expand=Orders&$deltatoken=8015", UriKind.Relative)
},
false, // Writing request
ODataVersion.V4,
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$delta\"," +
"\"@odata.count\":5," +
"\"@odata.deltaLink\":\"Customers?$expand=Orders&$deltatoken=8015\"," +
valueAsJson +
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$deletedEntity\",\"id\":\"Customers('ANTON')\",\"reason\":\"deleted\"}"
};

yield return new object[]
{
new ODataDeltaResourceSet(),
true, // Writing request
ODataVersion.V4,
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$delta\"," +
valueAsJson +
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$deletedEntity\",\"id\":\"Customers('ANTON')\",\"reason\":\"deleted\"}"
};

yield return new object[]
{
new ODataDeltaResourceSet
{
Count = 5,
DeltaLink = new Uri("Customers?$expand=Orders&$deltatoken=8015", UriKind.Relative)
},
false, // Writing request
ODataVersion.V401,
"{\"@context\":\"http://host/service/$metadata#Customers/$delta\"," +
"\"@count\":5," +
"\"@deltaLink\":\"Customers?$expand=Orders&$deltatoken=8015\"," +
valueAsJson.Replace("@odata.", "@") +
"{\"@removed\":{\"reason\":\"deleted\"},\"@id\":\"Customers('ANTON')\"}"
};

yield return new object[]
{
new ODataDeltaResourceSet(),
true, // Writing request
ODataVersion.V401,
"{\"@context\":\"http://host/service/$metadata#Customers/$delta\"," +
valueAsJson.Replace("@odata.", "@") +
"{\"@removed\":{\"reason\":\"deleted\"},\"@id\":\"Customers('ANTON')\"}"
};
}

[Theory]
[MemberData(nameof(GetWriteDeltaPayloadTestData))]
public async Task WriteDeltaPayloadAsync(ODataDeltaResourceSet deltaResourceSet, bool writingRequest, ODataVersion odataVersion, string expected)
{
var result = await SetupJsonLightDeltaWriterAndRunTestAsync(
async (jsonLightDeltaWriter) =>
{
await jsonLightDeltaWriter.WriteStartAsync(deltaResourceSet);
await jsonLightDeltaWriter.WriteStartAsync(customerUpdated);
await jsonLightDeltaWriter.WriteEndAsync();
await jsonLightDeltaWriter.WriteDeltaDeletedLinkAsync(linkToOrder10643);
await jsonLightDeltaWriter.WriteDeltaLinkAsync(linkToOrder10645);
await jsonLightDeltaWriter.WriteStartAsync(order10643);
await jsonLightDeltaWriter.WriteStartAsync(shippingAddressInfo);
await jsonLightDeltaWriter.WriteStartAsync(shippingAddress);
await jsonLightDeltaWriter.WriteEndAsync(); // shippingAddress
await jsonLightDeltaWriter.WriteEndAsync(); // shippingAddressInfo
await jsonLightDeltaWriter.WriteEndAsync(); // order10643
await jsonLightDeltaWriter.WriteDeltaDeletedEntryAsync(customerDeletedEntry);
await jsonLightDeltaWriter.WriteEndAsync(); // deltaResourceSet
await jsonLightDeltaWriter.FlushAsync();
},
this.GetCustomers(),
this.GetCustomerType(),
odataVersion,
/*writingRequest*/ writingRequest);

Assert.Equal(expected, result);
}

[Fact]
public async Task WriteExpandedResourceSetAsync()
{
var result = await SetupJsonLightDeltaWriterAndRunTestAsync(
async (jsonLightDeltaWriter) =>
{
await jsonLightDeltaWriter.WriteStartAsync(deltaFeed);
await jsonLightDeltaWriter.WriteStartAsync(customerEntry);
await jsonLightDeltaWriter.WriteStartAsync(ordersNavigationLink);
await jsonLightDeltaWriter.WriteStartAsync(ordersFeed);
await jsonLightDeltaWriter.WriteStartAsync(orderEntry);
await jsonLightDeltaWriter.WriteStartAsync(shippingAddressInfo);
await jsonLightDeltaWriter.WriteStartAsync(shippingAddress);
await jsonLightDeltaWriter.WriteEndAsync(); // shippingAddress
await jsonLightDeltaWriter.WriteEndAsync(); // shippingAddressInfo
await jsonLightDeltaWriter.WriteEndAsync(); // orderEntry
await jsonLightDeltaWriter.WriteEndAsync(); // ordersFeed
await jsonLightDeltaWriter.WriteEndAsync(); // ordersNavigationLink
await jsonLightDeltaWriter.WriteStartAsync(favouriteProductsNavigationLink);
await jsonLightDeltaWriter.WriteStartAsync(favouriteProductsFeed);
await jsonLightDeltaWriter.WriteStartAsync(productEntry);
await jsonLightDeltaWriter.WriteEndAsync(); // productEntry
await jsonLightDeltaWriter.WriteEndAsync(); // favouriteProductsFeed
await jsonLightDeltaWriter.WriteEndAsync(); // favouriteProductsNavigationLink
await jsonLightDeltaWriter.WriteEndAsync(); // customerEntry
await jsonLightDeltaWriter.WriteEndAsync(); // deltaFeed
await jsonLightDeltaWriter.FlushAsync();
}, this.GetCustomers(),
this.GetCustomerType());

Assert.Equal(
"{\"@odata.context\":\"http://host/service/$metadata#Customers/$delta\"," +
"\"value\":[" +
"{\"@odata.id\":\"http://host/service/Customers('BOTTM')\",\"ContactName\":\"Susan Halvenstern\"," +
"\"Orders\":[{\"@odata.id\":\"http://host/service/Orders(10643)\",\"Id\":10643," +
"\"ShippingAddress\":{\"Street\":\"23 Tsawassen Blvd.\",\"City\":\"Tsawassen\",\"Region\":\"BC\",\"PostalCode\":\"T2F 8M4\"}}]," +
"\"FavouriteProducts\":[{\"@odata.id\":\"http://host/service/Product(1)\",\"Id\":1,\"Name\":\"Car\"}]}]}",
result);
}

#endregion Async Tests

#region Test Helper Methods

private void TestInit(IEdmModel userModel = null, bool fullMetadata = false)
Expand Down Expand Up @@ -2815,7 +2955,7 @@ private string TestPayload()
return (new StreamReader(stream)).ReadToEnd();
}

private static ODataJsonLightOutputContext CreateJsonLightOutputContext(MemoryStream stream, IEdmModel userModel, bool fullMetadata = false, ODataUri uri = null, ODataVersion version = ODataVersion.V4, bool isResponse = true)
private static ODataJsonLightOutputContext CreateJsonLightOutputContext(MemoryStream stream, IEdmModel userModel, bool fullMetadata = false, ODataUri uri = null, ODataVersion version = ODataVersion.V4, bool isResponse = true, bool isAsync = false)
{
var settings = new ODataMessageWriterSettings { Version = version, ShouldIncludeAnnotation = ODataUtils.CreateAnnotationFilter("*") };
settings.SetServiceDocumentUri(new Uri("http://host/service"));
Expand All @@ -2842,7 +2982,7 @@ private static ODataJsonLightOutputContext CreateJsonLightOutputContext(MemorySt
MediaType = mediaType,
Encoding = Encoding.UTF8,
IsResponse = isResponse,
IsAsync = false,
IsAsync = isAsync,
Model = userModel ?? EdmCoreModel.Instance
};

Expand All @@ -2859,6 +2999,38 @@ private ODataJsonLightOutputContext GetV4OutputContext(bool isResponse)
return isResponse ? V4ResponseOutputContext : V4RequestOutputContext;
}

/// <summary>
/// Sets up an ODataJsonLightDeltaWriter,
/// then runs the given test code asynchronously,
/// then flushes and reads the stream back as a string for customized verification.
/// </summary>
private async Task<string> SetupJsonLightDeltaWriterAndRunTestAsync(
Func<ODataJsonLightDeltaWriter, Task> func,
IEdmNavigationSource navigationSource,
IEdmEntityType resourceType,
ODataVersion odataVersion = ODataVersion.V4,
bool writingRequest = false)
{
this.stream = new MemoryStream();
var jsonLightOutputContext = CreateJsonLightOutputContext(
this.stream,
this.GetModel(),
/*fullMetadata*/ false,
/*uri*/ null,
odataVersion,
!writingRequest,
/*isAsync*/ true);
var jsonLightDeltaWriter = new ODataJsonLightDeltaWriter(
jsonLightOutputContext,
navigationSource,
resourceType);

await func(jsonLightDeltaWriter);

this.stream.Seek(0, SeekOrigin.Begin);
return await new StreamReader(this.stream).ReadToEndAsync();
}

#endregion
}
}