Skip to content

Commit

Permalink
Provide async APIs for CsdlWriter and SchemaWriter (#3006)
Browse files Browse the repository at this point in the history
* Add corresponding async methods for syncronous methods of EdmModelCsdlSchemaJsonWriter

* Implemented asynchronous counterparts for synchronous virtual and abstract methods for EdmModelCsdlSchemaWriter class

* Added asynchronous implementations for the synchronous methods in EdmModelCsdlSchemaXmlWriter

* Added corresponding asynchronous methods for EdmModelReferenceElementsJsonVisitor, EdmModelCsdlSerializationVisitor and EdmModelReferenceElementsXmlVisitor

* Added asynchronous counterparts for csdl writer methods

* Added async corresponding sync methods for EdmModelVisitor

* Fix WriteStartElementAsync prefix and namespace missing

* Added csdl async/await and functional tests for csdl writer async methods

* Added async APIs to net48 public API

* Fixed comments to add ConfigureAwait(false)

* Added configureAwait(false) to await tests

* Elude await/async in BeginElementAsync method Func<TElement, Task> params

* Rewrite the doc string of the async methods to add <returns> and provide correct summary

* Rewrite WriteSchemata to WriteSchema

* Move the asynhronous tests to .Async partial classes

* Added ContextUrlWriterReaderTests.Async partial class with async tests and fix asynhronous equal true when writing xml

* Added doc string for <params></params>

* Resolve missing spaces

* Added Returns Documentation string for WriteCsdlAsync

* Remove unnecessary 'else'

* Added WriteMetadataDocumentAsync methods and tests

* Rename test functions for WriteMetadataDocumentAsync

* Renaming WriteSchema to WriteSchemas and WriteSchemaAsync to WriteSchemasAsync

* Provide the correct file in copyright for the added files

* Rename partial class OasisActionsFunctionsRelationshipChangesAcceptanceTests to OasisActionsFunctionsRelationshipChangesAcceptanceTest to reuse the methods used in main class OasisActionsFunctionsRelationshipChangesAcceptanceTest
  • Loading branch information
WanjohiSammy authored Jul 23, 2024
1 parent bd08585 commit b94324c
Show file tree
Hide file tree
Showing 42 changed files with 13,023 additions and 50 deletions.
32 changes: 25 additions & 7 deletions src/Microsoft.OData.Core/ODataMetadataJsonOutputContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,12 @@ internal Task FlushAsync()
/// </summary>
/// <returns>A task representing the asynchronous operation of writing the metadata document.</returns>
/// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks>
internal override Task WriteMetadataDocumentAsync()
internal override async Task WriteMetadataDocumentAsync()
{
this.AssertAsynchronous();

return TaskUtils.GetTaskForSynchronousOperationReturningTask(
() =>
{
this.WriteMetadataDocumentImplementation();
return this.FlushAsync();
});
await this.WriteMetadataDocumentImplementationAsync().ConfigureAwait(false);
await this.FlushAsync().ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -259,6 +255,28 @@ private void WriteMetadataDocumentImplementation()
}
}

private async Task WriteMetadataDocumentImplementationAsync()
{
var writerSettings = new CsdlJsonWriterSettings
{
IsIeee754Compatible = MessageWriterSettings.IsIeee754Compatible,
};

var (success, errors) = await CsdlWriter.TryWriteCsdlAsync(this.Model, this.jsonWriter, writerSettings).ConfigureAwait(false);
if (!success)
{
Debug.Assert(errors != null, "errors != null");

StringBuilder builder = new StringBuilder();
foreach (EdmError error in errors)
{
builder.AppendLine(error.ToString());
}

throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString()));
}
}

private async Task DisposeOutputStreamAsync()
{
await this.asynchronousOutputStream.FlushAsync().ConfigureAwait(false);
Expand Down
34 changes: 27 additions & 7 deletions src/Microsoft.OData.Core/ODataMetadataOutputContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,12 @@ internal override Task WriteInStreamErrorAsync(ODataError error, bool includeDeb
/// </summary>
/// <returns>A task representing the asynchronous operation of writing the metadata document.</returns>
/// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks>
internal override Task WriteMetadataDocumentAsync()
internal override async Task WriteMetadataDocumentAsync()
{
this.AssertAsynchronous();

return TaskUtils.GetTaskForSynchronousOperationReturningTask(
() =>
{
this.WriteMetadataDocumentImplementation();
return this.FlushAsync();
});
await this.WriteMetadataDocumentImplementationAsync().ConfigureAwait(false);
await this.FlushAsync().ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -289,5 +285,29 @@ private void WriteMetadataDocumentImplementation()
throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString()));
}
}

private async Task WriteMetadataDocumentImplementationAsync()
{
CsdlXmlWriterSettings writerSettings = new CsdlXmlWriterSettings();

if (this.MessageWriterSettings.LibraryCompatibility.HasFlag(ODataLibraryCompatibility.UseLegacyVariableCasing))
{
writerSettings.LibraryCompatibility |= EdmLibraryCompatibility.UseLegacyVariableCasing;
}

var (success, errors) = await CsdlWriter.TryWriteCsdlAsync(this.Model, this.xmlWriter, CsdlTarget.OData, writerSettings).ConfigureAwait(false);
if (!success)
{
Debug.Assert(errors != null, "errors != null");

StringBuilder builder = new StringBuilder();
foreach (EdmError error in errors)
{
builder.AppendLine(error.ToString());
}

throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString()));
}
}
}
}
92 changes: 86 additions & 6 deletions src/Microsoft.OData.Edm/Csdl/CsdlJsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.OData.Edm.Csdl.Serialization;

namespace Microsoft.OData.Edm.Csdl
Expand Down Expand Up @@ -39,7 +40,7 @@ public CsdlJsonWriter(IEdmModel model, Utf8JsonWriter jsonWriter, CsdlJsonWriter
}

/// <summary>
/// Write the JSON CSDL.
/// Writes the JSON CSDL.
/// </summary>
protected override void WriteCsdl()
{
Expand All @@ -49,13 +50,30 @@ protected override void WriteCsdl()
WriteReferenceElements();

// It also MAY contain members for schemas.
WriteSchemata();
WriteSchemas();

WriteCsdlEnd();
}

/// <summary>
/// CSDL JSON Document Object
/// Asynchronously Writes the JSON CSDL.
/// </summary>
/// <returns>Task that represents the asynchronous operation.</returns>
protected override async Task WriteCsdlAsync()
{
await WriteCsdlStartAsync().ConfigureAwait(false);

// The CSDL JSON Document object MAY contain the member $Reference to reference other CSDL documents.
await WriteReferenceElementsAsync().ConfigureAwait(false);

// It also MAY contain members for schemas.
await WriteSchemasAsync().ConfigureAwait(false);

await WriteCsdlEndAsync().ConfigureAwait(false);
}

/// <summary>
/// Writes CSDL JSON Document Object
/// </summary>
private void WriteCsdlStart()
{
Expand All @@ -73,7 +91,28 @@ private void WriteCsdlStart()
}

/// <summary>
/// $Reference Object
/// Asynchronously Writes CSDL JSON Document Object
/// </summary>
/// <returns>A task that represents the asynchronous operation</returns>
private Task WriteCsdlStartAsync()
{
// A CSDL JSON document consists of a single JSON object.
this.jsonWriter.WriteStartObject();

// This document object MUST contain the member $Version.
this.jsonWriter.WriteRequiredProperty("$Version", GetVersionString(edmxVersion));

// If the CSDL JSON document is the metadata document of an OData service, the document object MUST contain the member $EntityContainer.
if (model.EntityContainer != null)
{
this.jsonWriter.WriteRequiredProperty("$EntityContainer", model.EntityContainer.FullName());
}

return Task.CompletedTask;
}

/// <summary>
/// Writes $Reference Object
/// </summary>
private void WriteReferenceElements()
{
Expand All @@ -84,9 +123,20 @@ EdmModelReferenceElementsJsonVisitor visitor
}

/// <summary>
/// Schema Object.
/// Asynchronously writes $Reference Object.
/// </summary>
private void WriteSchemata()
/// <returns>A task that represents the asynchronous operation</returns>
private Task WriteReferenceElementsAsync()
{
var visitor = new EdmModelReferenceElementsJsonVisitor(this.model, this.jsonWriter, this.edmxVersion);

return visitor.VisitEdmReferencesAsync(this.model);
}

/// <summary>
/// Writes Schema Object.
/// </summary>
private void WriteSchemas()
{
// A schema is represented as a member of the document object whose name is the schema namespace.
// Its value is an object that MAY contain the members $Alias and $Annotations.
Expand All @@ -100,13 +150,43 @@ private void WriteSchemata()
}
}

/// <summary>
/// Asynchronously Writes Schema Object
/// </summary>
/// <returns>A task that represents the asynchronous operation</returns>
private async Task WriteSchemasAsync()
{
// A schema is represented as a member of the document object whose name is the schema namespace.
// Its value is an object that MAY contain the members $Alias and $Annotations.
EdmModelCsdlSerializationVisitor visitor;
Version edmVersion = this.model.GetEdmVersion() ?? EdmConstants.EdmVersionLatest;
foreach (EdmSchema schema in this.schemas)
{
EdmModelCsdlSchemaWriter writer = new EdmModelCsdlSchemaJsonWriter(model, jsonWriter, edmVersion, settings);
visitor = new EdmModelCsdlSerializationVisitor(this.model, writer);
await visitor.VisitEdmSchemaAsync(schema, this.model.GetNamespacePrefixMappings()).ConfigureAwait(false);
}
}

private void WriteCsdlEnd()
{
// End of the CSDL JSON document.
this.jsonWriter.WriteEndObject();

this.jsonWriter.Flush();
}

/// <summary>
/// Write the end of the CSDL JSON document asynchronously.
/// </summary>
/// <returns>A task that flushing the JSON writer (jsonWriter) asynchronously.</returns>
private Task WriteCsdlEndAsync()
{
// End of the CSDL JSON document.
this.jsonWriter.WriteEndObject();

return this.jsonWriter.FlushAsync();
}
}
}
#endif
85 changes: 85 additions & 0 deletions src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

#if NETCOREAPP
using System.Text.Json;
using System.Threading.Tasks;

#endif

using System.Xml;
Expand Down Expand Up @@ -56,6 +60,17 @@ public static bool TryWriteCsdl(IEdmModel model, Utf8JsonWriter writer, out IEnu
return TryWriteCsdl(model, writer, CsdlJsonWriterSettings.Default, out errors);
}

/// <summary>
/// Asynchronously Outputs a CSDL JSON artifact to the provided <see cref="Utf8JsonWriter"/>.
/// </summary>
/// <param name="model">The Edm model to be written.</param>
/// <param name="writer">JSON writer the generated CSDL will be written to.</param>
/// <returns>A Task with a tuple with a value indicating whether serialization was successful and EdmError if any</returns>
public static Task<(bool, IEnumerable<EdmError>)> TryWriteCsdlAsync(IEdmModel model, Utf8JsonWriter writer)
{
return TryWriteCsdlAsync(model, writer, CsdlJsonWriterSettings.Default);
}

/// <summary>
/// Outputs a CSDL JSON artifact to the provided <see cref="Utf8JsonWriter"/> using the settings.
/// </summary>
Expand All @@ -82,6 +97,31 @@ public static bool TryWriteCsdl(IEdmModel model, Utf8JsonWriter writer, CsdlJson
errors = Enumerable.Empty<EdmError>();
return true;
}

/// <summary>
/// Asynchronously Outputs a CSDL JSON artifact to the provided <see cref="Utf8JsonWriter"/> using the settings.
/// </summary>
/// <param name="model">The Edm model to be written.</param>
/// <param name="writer">JSON writer the generated CSDL will be written to.</param>
/// <param name="settings">The CSDL writer settings.</param>
/// <returns>A Task with tuple with a value indicating whether serialization was successful and EdmError if any</returns>
public static async Task<(bool, IEnumerable<EdmError>)> TryWriteCsdlAsync(IEdmModel model, Utf8JsonWriter writer, CsdlJsonWriterSettings settings)
{
EdmUtil.CheckArgumentNull(model, nameof(model));
EdmUtil.CheckArgumentNull(writer, nameof(writer));
EdmUtil.CheckArgumentNull(settings, nameof(settings));

Version edmxVersion;
if (!VerifyAndGetVersion(model, out edmxVersion, out IEnumerable<EdmError> errors))
{
return (false, errors);
}

CsdlWriter csdlWriter = new CsdlJsonWriter(model, writer, settings, edmxVersion);
await csdlWriter.WriteCsdlAsync().ConfigureAwait(false);

return (true, Enumerable.Empty<EdmError>());
}
#endif

/// <summary>
Expand All @@ -97,6 +137,17 @@ public static bool TryWriteCsdl(IEdmModel model, XmlWriter writer, CsdlTarget ta
return TryWriteCsdl(model, writer, target, new CsdlXmlWriterSettings(), out errors);
}

/// <summary>
/// Asynchronously Outputs a CSDL XML artifact to the provided <see cref="XmlWriter"/>.
/// </summary>
/// <param name="model">The Edm model to be written.</param>
/// <param name="writer">Xmlwriter the generated CSDL will be written to.</param>
/// <returns>A task with tuple with a value indicating whether serialization was successful and EdmError if any</returns>
public static Task<(bool, IEnumerable<EdmError>)> TryWriteCsdlAsync(IEdmModel model, XmlWriter writer, CsdlTarget target)
{
return TryWriteCsdlAsync(model, writer, target, new CsdlXmlWriterSettings());
}

/// <summary>
/// Outputs a CSDL XML artifact to the provided <see cref="XmlWriter"/>.
/// </summary>
Expand Down Expand Up @@ -124,6 +175,31 @@ public static bool TryWriteCsdl(IEdmModel model, XmlWriter writer, CsdlTarget ta
return true;
}

/// <summary>
/// Asynchronously Outputs a CSDL XML artifact to the provided <see cref="XmlWriter"/>.
/// </summary>
/// <param name="model">Model to be written.</param>
/// <param name="writer">XmlWriter the generated CSDL will be written to.</param>
/// <param name="target">Target implementation of the CSDL being generated.</param>
/// <param name="writerSettings">The CSDL xml writer settings.</param>
/// <returns>A task with a value indicating whether serialization was successful.</returns>
public static async Task<(bool, IEnumerable<EdmError>)> TryWriteCsdlAsync(IEdmModel model, XmlWriter writer, CsdlTarget target, CsdlXmlWriterSettings writerSettings)
{
EdmUtil.CheckArgumentNull(model, "model");
EdmUtil.CheckArgumentNull(writer, "writer");

Version edmxVersion;
if (!VerifyAndGetVersion(model, out edmxVersion, out IEnumerable<EdmError> errors))
{
return (false, errors);
}

CsdlWriter csdlWriter = new CsdlXmlWriter(model, writer, edmxVersion, target, writerSettings);
await csdlWriter.WriteCsdlAsync().ConfigureAwait(false);

return (true, Enumerable.Empty<EdmError>());
}

/// <summary>
/// Write CSDL output.
/// </summary>
Expand All @@ -132,6 +208,15 @@ protected virtual void WriteCsdl()
// nothing here
}

/// <summary>
/// Asynchronously Writes CSDL output.
/// </summary>
/// <returns>Task represents an asynchronous operation that may or may not return a result.</returns>
protected virtual Task WriteCsdlAsync()
{
return Task.CompletedTask;
}

/// <summary>
/// Gets the string form of the EdmVersion.
/// Note that Version 4.01 needs two digits of minor version precision.
Expand Down
Loading

0 comments on commit b94324c

Please sign in to comment.