Skip to content

Commit

Permalink
Implement asynchronous support in ODataJsonLightCollectionSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
John Gathogo committed Apr 12, 2021
1 parent 59bfcdf commit 1546196
Show file tree
Hide file tree
Showing 2 changed files with 312 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.OData.JsonLight
#region Namespaces

using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.OData.Edm;

#endregion Namespaces
Expand Down Expand Up @@ -87,5 +88,73 @@ internal void WriteCollectionEnd()
this.JsonWriter.EndObjectScope();
}
}

/// <summary>
/// Asynchronously writes the start of a collection.
/// </summary>
/// <param name="collectionStart">The collection start to write.</param>
/// <param name="itemTypeReference">The item type of the collection or null if no metadata is available.</param>
internal async Task WriteCollectionStartAsync(ODataCollectionStart collectionStart, IEdmTypeReference itemTypeReference)
{
Debug.Assert(collectionStart != null, "collectionStart != null");

if (this.writingTopLevelCollection)
{
// "{"
await this.AsynchronousJsonWriter.StartObjectScopeAsync()
.ConfigureAwait(false);

// "@odata.context":...
await this.WriteContextUriPropertyAsync(
ODataPayloadKind.Collection,
() => ODataContextUrlInfo.Create(collectionStart.SerializationInfo, itemTypeReference))
.ConfigureAwait(false);

// "@odata.count":...
if (collectionStart.Count.HasValue)
{
await this.AsynchronousODataAnnotationWriter.WriteInstanceAnnotationNameAsync(ODataAnnotationNames.ODataCount)
.ConfigureAwait(false);
await this.AsynchronousJsonWriter.WriteValueAsync(collectionStart.Count.Value)
.ConfigureAwait(false);
}

// "@odata.nextlink":...
if (collectionStart.NextPageLink != null)
{
await this.AsynchronousODataAnnotationWriter.WriteInstanceAnnotationNameAsync(ODataAnnotationNames.ODataNextLink)
.ConfigureAwait(false);
await this.AsynchronousJsonWriter.WriteValueAsync(this.UriToString(collectionStart.NextPageLink))
.ConfigureAwait(false);
}

// "value":
await this.AsynchronousJsonWriter.WriteValuePropertyNameAsync()
.ConfigureAwait(false);
}

// Write the start of the array for the collection items
// "["
await this.AsynchronousJsonWriter.StartArrayScopeAsync()
.ConfigureAwait(false);
}

/// <summary>
/// Asynchronously writes the end of a collection.
/// </summary>
internal async Task WriteCollectionEndAsync()
{
// Write the end of the array for the collection items
// "]"
await this.AsynchronousJsonWriter.EndArrayScopeAsync()
.ConfigureAwait(false);

if (this.writingTopLevelCollection)
{
// "}"
await this.AsynchronousJsonWriter.EndObjectScopeAsync()
.ConfigureAwait(false);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//---------------------------------------------------------------------
// <copyright file="ODataJsonLightCollectionSerializerTests.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.OData.Edm;
using Microsoft.OData.JsonLight;
using Xunit;

namespace Microsoft.OData.Tests.JsonLight
{
/// <summary>
/// Unit tests for ODataJsonLightCollectionSerializer.
/// </summary>
public class ODataJsonLightCollectionSerializerTests
{
private EdmModel model;
private MemoryStream stream;
private ODataMessageWriterSettings settings;
private EdmEntityType entityType;

public ODataJsonLightCollectionSerializerTests()
{
this.model = new EdmModel();

this.entityType = new EdmEntityType("NS", "Product");
this.entityType.AddKeys(this.entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
this.entityType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);

this.model.AddElement(this.entityType);

this.stream = new MemoryStream();
this.settings = new ODataMessageWriterSettings
{
EnableMessageStreamDisposal = false,
Version = ODataVersion.V4
};
this.settings.SetServiceDocumentUri(new Uri("http://tempuri.org"));
}

[Fact]
public async Task WriteCollectionStartAsync_ForNonTopLevelCollection()
{
var collectionStart = new ODataCollectionStart
{
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(String)"
}
};

var itemTypeReference = EdmCoreModel.Instance.GetString(false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
(jsonLightCollectionSerializer) =>
{
return jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
});

Assert.Equal("[", result);
}

[Fact]
public async Task WriteCollectionEndAsync_ForNonTopLevelCollection()
{
var collectionStart = new ODataCollectionStart
{
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(String)"
}
};

var itemTypeReference = EdmCoreModel.Instance.GetString(false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
async (jsonLightCollectionSerializer) =>
{
await jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
await jsonLightCollectionSerializer.WriteCollectionEndAsync();
});

Assert.Equal("[]", result);
}

[Fact]
public async Task WriteCollectionStartAsync_ForTopLevelCollection()
{
var collectionStart = new ODataCollectionStart
{
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(Product)"
}
};

var itemTypeReference = new EdmEntityTypeReference(this.entityType, false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
(jsonLightCollectionSerializer) =>
{
return jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
},
/* container */ null,
/* writeTopLevelCollection */ true);

Assert.Equal("{\"@odata.context\":\"http://tempuri.org/$metadata#Collection(Product)\",\"value\":[", result);
}

[Fact]
public async Task WriteCollectionStartAsync_ForTopLevelCollectionWithNextPageLink()
{
var collectionStart = new ODataCollectionStart
{
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(Product)"
},
NextPageLink = new Uri("http://tempuri.org/Products?$skiptoken=Id-5")
};

var itemTypeReference = new EdmEntityTypeReference(this.entityType, false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
(jsonLightCollectionSerializer) =>
{
return jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
},
/* container */ null,
/* writeTopLevelCollection */ true);

Assert.Equal(
"{\"@odata.context\":\"http://tempuri.org/$metadata#Collection(Product)\"," +
"\"@odata.nextLink\":\"http://tempuri.org/Products?$skiptoken=Id-5\"," +
"\"value\":[",
result);
}

[Fact]
public async Task WriteCollectionStartAsync_ForTopLevelCollectionWithItemCount()
{
var collectionStart = new ODataCollectionStart
{
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(Product)"
},
Count = 10
};

var itemTypeReference = new EdmEntityTypeReference(this.entityType, false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
(jsonLightCollectionSerializer) =>
{
return jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
},
/* container */ null,
/* writeTopLevelCollection */ true);

Assert.Equal(
"{\"@odata.context\":\"http://tempuri.org/$metadata#Collection(Product)\"," +
"\"@odata.count\":10," +
"\"value\":[",
result);
}

[Fact]
public async Task WriteCollectionEndAsync_ForTopLevelCollection()
{
var collectionStart = new ODataCollectionStart
{
Name = "Products",
SerializationInfo = new ODataCollectionStartSerializationInfo
{
CollectionTypeName = "Collection(Product)"
},
NextPageLink = new Uri("http://tempuri.org/Products?$skiptoken=Id-5"),
Count = 10
};

var itemTypeReference = new EdmEntityTypeReference(this.entityType, false);

var result = await SetupODataJsonLightCollectionSerializerAndRunTestAsync(
async (jsonLightCollectionSerializer) =>
{
await jsonLightCollectionSerializer.WriteCollectionStartAsync(collectionStart, itemTypeReference);
await jsonLightCollectionSerializer.WriteCollectionEndAsync();
},
/* container */ null,
/* writeTopLevelCollection */ true);

Assert.Equal(
"{\"@odata.context\":\"http://tempuri.org/$metadata#Collection(Product)\"," +
"\"@odata.count\":10," +
"\"@odata.nextLink\":\"http://tempuri.org/Products?$skiptoken=Id-5\"," +
"\"value\":[]}",
result);
}

private ODataJsonLightCollectionSerializer CreateODataJsonLightCollectionSerializer(bool writingResponse, IServiceProvider container = null, bool isAsync = false, bool writingTopLevelCollection = false)
{
var messageInfo = new ODataMessageInfo
{
MessageStream = this.stream,
MediaType = new ODataMediaType("application", "json"),
#if NETCOREAPP1_1
Encoding = Encoding.GetEncoding(0),
#else
Encoding = Encoding.Default,
#endif
IsResponse = writingResponse,
IsAsync = isAsync,
Model = model,
Container = container
};
var context = new ODataJsonLightOutputContext(messageInfo, this.settings);
return new ODataJsonLightCollectionSerializer(context, writingTopLevelCollection);
}

/// <summary>
/// Sets up an ODataJsonLightCollectionSerializer,
/// then runs the given test code asynchonously,
/// then flushes and reads the stream back as a string for customized verification.
/// </summary>
private async Task<string> SetupODataJsonLightCollectionSerializerAndRunTestAsync(Func<ODataJsonLightCollectionSerializer, Task> func, IServiceProvider container = null, bool writingTopLevelCollection = false)
{
var jsonLightCollectionSerializer = CreateODataJsonLightCollectionSerializer(true, container, true, writingTopLevelCollection);
await func(jsonLightCollectionSerializer);
await jsonLightCollectionSerializer.JsonLightOutputContext.FlushAsync();
await jsonLightCollectionSerializer.AsynchronousJsonWriter.FlushAsync();

this.stream.Position = 0;

return await new StreamReader(this.stream).ReadToEndAsync();
}
}
}

0 comments on commit 1546196

Please sign in to comment.