From 6aaf99b9b13ebc55baaad3b359b676148115e7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Tue, 23 Apr 2024 17:37:47 +0200 Subject: [PATCH 01/12] Adds IAsyncParseNodeFactory, makes deserialization methods async, updates unit tests --- .../ApiClientBuilderTests.cs | 4 +- .../DeserializationHelpersTests.cs | 89 +++++++++++- .../ParseNodeFactoryRegistryTests.cs | 71 ++++++++- ...soft.Kiota.Abstractions.v3.ncrunchsolution | 8 ++ src/ApiClientBuilder.cs | 6 +- src/serialization/IAsyncParseNodeFactory.cs | 21 +++ src/serialization/IParseNodeFactory.cs | 2 + .../KiotaJsonSerializer.Deserialization.cs | 90 +++++++++++- .../KiotaSerializer.Deserialization.cs | 135 +++++++++++++++++- src/serialization/ParseNodeFactoryRegistry.cs | 32 ++++- src/serialization/ParseNodeProxyFactory.cs | 34 ++++- src/store/BackingStoreParseNodeFactory.cs | 4 +- 12 files changed, 477 insertions(+), 19 deletions(-) create mode 100644 Microsoft.Kiota.Abstractions.v3.ncrunchsolution create mode 100644 src/serialization/IAsyncParseNodeFactory.cs diff --git a/Microsoft.Kiota.Abstractions.Tests/ApiClientBuilderTests.cs b/Microsoft.Kiota.Abstractions.Tests/ApiClientBuilderTests.cs index d377feb4..e2785376 100644 --- a/Microsoft.Kiota.Abstractions.Tests/ApiClientBuilderTests.cs +++ b/Microsoft.Kiota.Abstractions.Tests/ApiClientBuilderTests.cs @@ -50,7 +50,7 @@ public void EnableBackingStoreForParseNodeFactory() { // Arrange var parseNodeRegistry = new ParseNodeFactoryRegistry(); - var mockParseNodeFactory = new Mock(); + var mockParseNodeFactory = new Mock(); parseNodeRegistry.ContentTypeAssociatedFactories.TryAdd(StreamContentType, mockParseNodeFactory.Object); Assert.IsNotType(parseNodeRegistry.ContentTypeAssociatedFactories[StreamContentType]); @@ -67,7 +67,7 @@ public void EnableBackingStoreForParseNodeFactoryAlsoEnablesForDefaultInstance() { // Arrange var parseNodeRegistry = new ParseNodeFactoryRegistry(); - var mockParseNodeFactory = new Mock(); + var mockParseNodeFactory = new Mock(); parseNodeRegistry.ContentTypeAssociatedFactories.TryAdd(StreamContentType, mockParseNodeFactory.Object); ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories.TryAdd(StreamContentType, mockParseNodeFactory.Object); diff --git a/Microsoft.Kiota.Abstractions.Tests/Serialization/DeserializationHelpersTests.cs b/Microsoft.Kiota.Abstractions.Tests/Serialization/DeserializationHelpersTests.cs index 272d1f4a..b72e0314 100644 --- a/Microsoft.Kiota.Abstractions.Tests/Serialization/DeserializationHelpersTests.cs +++ b/Microsoft.Kiota.Abstractions.Tests/Serialization/DeserializationHelpersTests.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Kiota.Abstractions.Serialization; using Microsoft.Kiota.Abstractions.Tests.Mocks; using Moq; @@ -12,6 +14,7 @@ public class DeserializationHelpersTests { private const string _jsonContentType = "application/json"; [Fact] + [Obsolete] public void DefensiveObject() { Assert.Throws(() => KiotaSerializer.Deserialize(null, (Stream)null, null)); @@ -21,6 +24,7 @@ public void DefensiveObject() Assert.Throws(() => KiotaSerializer.Deserialize(_jsonContentType, "", null)); } [Fact] + [Obsolete] public void DefensiveObjectCollection() { Assert.Throws(() => KiotaSerializer.DeserializeCollection(null, (Stream)null, null)); @@ -30,6 +34,7 @@ public void DefensiveObjectCollection() Assert.Throws(() => KiotaSerializer.DeserializeCollection(_jsonContentType, "", null)); } [Fact] + [Obsolete] public void DeserializesObjectWithoutReflection() { var strValue = "{'id':'123'}"; @@ -38,7 +43,7 @@ public void DeserializesObjectWithoutReflection() { Id = "123" }); - var mockJsonParseNodeFactory = new Mock(); + var mockJsonParseNodeFactory = new Mock(); mockJsonParseNodeFactory.Setup(x => x.GetRootParseNode(It.IsAny(), It.IsAny())).Returns(mockParseNode.Object); mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; @@ -48,6 +53,7 @@ public void DeserializesObjectWithoutReflection() Assert.NotNull(result); } [Fact] + [Obsolete] public void DeserializesObjectWithReflection() { var strValue = "{'id':'123'}"; @@ -56,7 +62,7 @@ public void DeserializesObjectWithReflection() { Id = "123" }); - var mockJsonParseNodeFactory = new Mock(); + var mockJsonParseNodeFactory = new Mock(); mockJsonParseNodeFactory.Setup(x => x.GetRootParseNode(It.IsAny(), It.IsAny())).Returns(mockParseNode.Object); mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; @@ -66,6 +72,7 @@ public void DeserializesObjectWithReflection() Assert.NotNull(result); } [Fact] + [Obsolete] public void DeserializesCollectionOfObject() { var strValue = "{'id':'123'}"; @@ -76,7 +83,7 @@ public void DeserializesCollectionOfObject() Id = "123" } }); - var mockJsonParseNodeFactory = new Mock(); + var mockJsonParseNodeFactory = new Mock(); mockJsonParseNodeFactory.Setup(x => x.GetRootParseNode(It.IsAny(), It.IsAny())).Returns(mockParseNode.Object); mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; @@ -86,4 +93,80 @@ public void DeserializesCollectionOfObject() Assert.NotNull(result); Assert.Single(result); } + + [Fact] + public async Task DefensiveObjectAsync() + { + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeAsync(null, (Stream)null, null)); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeAsync(_jsonContentType, (Stream)null, null)); + using var stream = new MemoryStream(); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeAsync(_jsonContentType, stream, null)); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeAsync(_jsonContentType, "", null)); + } + [Fact] + public async Task DefensiveObjectCollectionAsync() + { + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeCollectionAsync(null, (Stream)null, null, default)); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, (Stream)null, null)); + using var stream = new MemoryStream(); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, stream, null)); + await Assert.ThrowsAsync(async () => await KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, "", null)); + } + [Fact] + public async Task DeserializesObjectWithoutReflectionAsync() + { + var strValue = "{'id':'123'}"; + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetObjectValue(It.IsAny>())).Returns(new TestEntity() + { + Id = "123" + }); + var mockJsonParseNodeFactory = new Mock(); + mockJsonParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult(mockParseNode.Object)); + mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); + ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; + + var result = await KiotaSerializer.DeserializeAsync(_jsonContentType, strValue, TestEntity.CreateFromDiscriminatorValue); + + Assert.NotNull(result); + } + [Fact] + public async Task DeserializesObjectWithReflectionAsync() + { + var strValue = "{'id':'123'}"; + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetObjectValue(It.IsAny>())).Returns(new TestEntity() + { + Id = "123" + }); + var mockJsonParseNodeFactory = new Mock(); + mockJsonParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult(mockParseNode.Object)); + mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); + ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; + + var result = await KiotaSerializer.DeserializeAsync(_jsonContentType, strValue); + + Assert.NotNull(result); + } + [Fact] + public async Task DeserializesCollectionOfObjectAsync() + { + var strValue = "{'id':'123'}"; + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetCollectionOfObjectValues(It.IsAny>())).Returns(new List { + new TestEntity() + { + Id = "123" + } + }); + var mockJsonParseNodeFactory = new Mock(); + mockJsonParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns(Task.FromResult(mockParseNode.Object)); + mockJsonParseNodeFactory.Setup(x => x.ValidContentType).Returns(_jsonContentType); + ParseNodeFactoryRegistry.DefaultInstance.ContentTypeAssociatedFactories[_jsonContentType] = mockJsonParseNodeFactory.Object; + + var result = await KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, strValue, TestEntity.CreateFromDiscriminatorValue); + + Assert.NotNull(result); + Assert.Single(result); + } } diff --git a/Microsoft.Kiota.Abstractions.Tests/Serialization/ParseNodeFactoryRegistryTests.cs b/Microsoft.Kiota.Abstractions.Tests/Serialization/ParseNodeFactoryRegistryTests.cs index 54654992..36de1615 100644 --- a/Microsoft.Kiota.Abstractions.Tests/Serialization/ParseNodeFactoryRegistryTests.cs +++ b/Microsoft.Kiota.Abstractions.Tests/Serialization/ParseNodeFactoryRegistryTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Kiota.Abstractions.Serialization; using Moq; @@ -24,12 +25,13 @@ public void ParseNodeFactoryRegistryDoesNotStickToOneContentType() } [Fact] + [Obsolete] public void ReturnsExpectedRootNodeForRegisteredContentType() { // Arrange var streamContentType = "application/octet-stream"; using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("test input")); - var mockParseNodeFactory = new Mock(); + var mockParseNodeFactory = new Mock(); var mockParseNode = new Mock(); mockParseNodeFactory.Setup(parseNodeFactory => parseNodeFactory.GetRootParseNode(streamContentType, It.IsAny())).Returns(mockParseNode.Object); _parseNodeFactoryRegistry.ContentTypeAssociatedFactories.TryAdd(streamContentType, mockParseNodeFactory.Object); @@ -40,12 +42,13 @@ public void ReturnsExpectedRootNodeForRegisteredContentType() Assert.Equal(mockParseNode.Object, rootParseNode); } [Fact] + [Obsolete] public void ReturnsExpectedRootNodeForVendorSpecificContentType() { // Arrange var applicationJsonContentType = "application/json"; using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("{\"test\": \"input\"}")); - var mockParseNodeFactory = new Mock(); + var mockParseNodeFactory = new Mock(); var mockParseNode = new Mock(); mockParseNodeFactory.Setup(parseNodeFactory => parseNodeFactory.GetRootParseNode(applicationJsonContentType, It.IsAny())).Returns(mockParseNode.Object); _parseNodeFactoryRegistry.ContentTypeAssociatedFactories.TryAdd(applicationJsonContentType, mockParseNodeFactory.Object); @@ -57,6 +60,7 @@ public void ReturnsExpectedRootNodeForVendorSpecificContentType() } [Fact] + [Obsolete] public void ThrowsInvalidOperationExceptionForUnregisteredContentType() { // Arrange @@ -72,6 +76,7 @@ public void ThrowsInvalidOperationExceptionForUnregisteredContentType() [Theory] [InlineData(null)] [InlineData("")] + [Obsolete] public void ThrowsArgumentNullExceptionForNoContentType(string contentType) { // Arrange @@ -82,5 +87,67 @@ public void ThrowsArgumentNullExceptionForNoContentType(string contentType) Assert.NotNull(exception); Assert.Equal("contentType", exception.ParamName); } + + // ***** + + [Fact] + public async Task ReturnsExpectedRootNodeForRegisteredContentTypeAsync() + { + // Arrange + var streamContentType = "application/octet-stream"; + using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("test input")); + var mockParseNodeFactory = new Mock(); + var mockParseNode = new Mock(); + mockParseNodeFactory.Setup(parseNodeFactory => parseNodeFactory.GetRootParseNodeAsync(streamContentType, It.IsAny(), It.IsAny())).Returns(Task.FromResult(mockParseNode.Object)); + _parseNodeFactoryRegistry.ContentTypeAssociatedFactories.TryAdd(streamContentType, mockParseNodeFactory.Object); + // Act + var rootParseNode = await _parseNodeFactoryRegistry.GetRootParseNodeAsync(streamContentType, testStream); + // Assert + Assert.NotNull(rootParseNode); + Assert.Equal(mockParseNode.Object, rootParseNode); + } + [Fact] + public async Task ReturnsExpectedRootNodeForVendorSpecificContentTypeAsync() + { + // Arrange + var applicationJsonContentType = "application/json"; + using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("{\"test\": \"input\"}")); + var mockParseNodeFactory = new Mock(); + var mockParseNode = new Mock(); + mockParseNodeFactory.Setup(parseNodeFactory => parseNodeFactory.GetRootParseNodeAsync(applicationJsonContentType, It.IsAny(), It.IsAny())).Returns(Task.FromResult(mockParseNode.Object)); + _parseNodeFactoryRegistry.ContentTypeAssociatedFactories.TryAdd(applicationJsonContentType, mockParseNodeFactory.Object); + // Act + var rootParseNode = await _parseNodeFactoryRegistry.GetRootParseNodeAsync("application/vnd+json", testStream); + // Assert + Assert.NotNull(rootParseNode); + Assert.Equal(mockParseNode.Object, rootParseNode); + } + + [Fact] + public async Task ThrowsInvalidOperationExceptionForUnregisteredContentTypeAsync() + { + // Arrange + var streamContentType = "application/octet-stream"; + using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("test input")); + // Act + var exception = await Assert.ThrowsAsync(async () => await _parseNodeFactoryRegistry.GetRootParseNodeAsync(streamContentType, testStream)); + // Assert + Assert.NotNull(exception); + Assert.Equal($"Content type {streamContentType} does not have a factory registered to be parsed", exception.Message); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public async Task ThrowsArgumentNullExceptionForNoContentTypeAsync(string contentType) + { + // Arrange + using var testStream = new MemoryStream(Encoding.UTF8.GetBytes("test input")); + // Act + var exception = await Assert.ThrowsAsync(async () => await _parseNodeFactoryRegistry.GetRootParseNodeAsync(contentType, testStream)); + // Assert + Assert.NotNull(exception); + Assert.Equal("contentType", exception.ParamName); + } } } diff --git a/Microsoft.Kiota.Abstractions.v3.ncrunchsolution b/Microsoft.Kiota.Abstractions.v3.ncrunchsolution new file mode 100644 index 00000000..13107d39 --- /dev/null +++ b/Microsoft.Kiota.Abstractions.v3.ncrunchsolution @@ -0,0 +1,8 @@ + + + True + True + True + True + + \ No newline at end of file diff --git a/src/ApiClientBuilder.cs b/src/ApiClientBuilder.cs index f83b02f2..70bf3445 100644 --- a/src/ApiClientBuilder.cs +++ b/src/ApiClientBuilder.cs @@ -29,7 +29,7 @@ public static class ApiClientBuilder /// Registers the default deserializer to the registry. /// /// The type of the parse node factory to register - public static void RegisterDefaultDeserializer() where T : IParseNodeFactory, new() + public static void RegisterDefaultDeserializer() where T : IAsyncParseNodeFactory, new() { var deserializerFactory = new T(); ParseNodeFactoryRegistry.DefaultInstance @@ -60,9 +60,9 @@ public static ISerializationWriterFactory EnableBackingStoreForSerializationWrit /// /// The parse node factory to enable the backing store on. /// A new parse node factory with the backing store enabled. - public static IParseNodeFactory EnableBackingStoreForParseNodeFactory(IParseNodeFactory original) + public static IAsyncParseNodeFactory EnableBackingStoreForParseNodeFactory(IAsyncParseNodeFactory original) { - IParseNodeFactory result = original ?? throw new ArgumentNullException(nameof(original)); + IAsyncParseNodeFactory result = original ?? throw new ArgumentNullException(nameof(original)); if(original is ParseNodeFactoryRegistry registry) { EnableBackingStoreForParseNodeRegistry(registry); diff --git a/src/serialization/IAsyncParseNodeFactory.cs b/src/serialization/IAsyncParseNodeFactory.cs new file mode 100644 index 00000000..afe0d8bf --- /dev/null +++ b/src/serialization/IAsyncParseNodeFactory.cs @@ -0,0 +1,21 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Kiota.Abstractions.Serialization +{ + /// + /// Defines the contract for a factory that creates parse nodes in an sync and async way. + /// + public interface IAsyncParseNodeFactory : IParseNodeFactory + { + /// + /// Create a parse node from the given stream and content type. + /// + /// The stream to read the parse node from. + /// The content type of the parse node. + /// The cancellation token for the task + /// A parse node. + Task GetRootParseNodeAsync(string contentType, Stream content, CancellationToken cancellationToken = default); + } +} diff --git a/src/serialization/IParseNodeFactory.cs b/src/serialization/IParseNodeFactory.cs index 09f833ff..d466ef76 100644 --- a/src/serialization/IParseNodeFactory.cs +++ b/src/serialization/IParseNodeFactory.cs @@ -2,6 +2,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ +using System; using System.IO; namespace Microsoft.Kiota.Abstractions.Serialization @@ -21,6 +22,7 @@ public interface IParseNodeFactory /// The stream to read the parse node from. /// The content type of the parse node. /// A parse node. + [Obsolete("Use GetRootParseNodeAsync instead")] IParseNode GetRootParseNode(string contentType, Stream content); } } diff --git a/src/serialization/KiotaJsonSerializer.Deserialization.cs b/src/serialization/KiotaJsonSerializer.Deserialization.cs index 27ac6817..515a0662 100644 --- a/src/serialization/KiotaJsonSerializer.Deserialization.cs +++ b/src/serialization/KiotaJsonSerializer.Deserialization.cs @@ -2,8 +2,11 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ +using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; #if NET5_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif @@ -21,19 +24,22 @@ public static partial class KiotaJsonSerializer /// /// The factory to create the object. /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable - => KiotaSerializer.Deserialize(_jsonContentType, serializedRepresentation, parsableFactory); + => KiotaSerializer.Deserialize(_jsonContentType, serializedRepresentation, parsableFactory); /// /// Deserializes the given stream into an object. /// /// The stream to deserialize. /// The factory to create the object. + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(Stream stream, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.Deserialize(_jsonContentType, stream, parsableFactory); /// /// Deserializes the given stream into an object. /// /// The stream to deserialize. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(Stream stream) where T : IParsable #else @@ -44,6 +50,7 @@ public static partial class KiotaJsonSerializer /// Deserializes the given stream into an object. /// /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else @@ -55,6 +62,7 @@ public static partial class KiotaJsonSerializer /// /// The stream to deserialize. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(Stream stream, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.DeserializeCollection(_jsonContentType, stream, parsableFactory); /// @@ -62,12 +70,14 @@ public static IEnumerable DeserializeCollection(Stream stream, ParsableFac /// /// The serialized representation of the objects. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.DeserializeCollection(_jsonContentType, serializedRepresentation, parsableFactory); /// /// Deserializes the given stream into a collection of objects based on the content type. /// /// The stream to deserialize. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(Stream stream) where T : IParsable #else @@ -78,10 +88,88 @@ public static IEnumerable DeserializeCollection(Stream stream) where T : I /// Deserializes the given stream into a collection of objects based on the content type. /// /// The serialized representation of the object. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else public static IEnumerable DeserializeCollection(string serializedRepresentation) where T : IParsable #endif => KiotaSerializer.DeserializeCollection(_jsonContentType, serializedRepresentation); + + /// + /// Deserializes the given stream into an object. + /// + /// The factory to create the object. + /// The serialized representation of the object. + /// The cancellation token for the task + public static Task DeserializeAsync(string serializedRepresentation, ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + => KiotaSerializer.DeserializeAsync(_jsonContentType, serializedRepresentation, parsableFactory, cancellationToken); + /// + /// Deserializes the given stream into an object. + /// + /// The stream to deserialize. + /// The factory to create the object. + /// The cancellation token for the task + public static Task DeserializeAsync(Stream stream, ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + => KiotaSerializer.DeserializeAsync(_jsonContentType, stream, parsableFactory, cancellationToken); + /// + /// Deserializes the given stream into an object. + /// + /// The stream to deserialize. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task DeserializeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task DeserializeAsync(Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#endif + => KiotaSerializer.DeserializeAsync(_jsonContentType, stream, cancellationToken); + /// + /// Deserializes the given stream into an object. + /// + /// The serialized representation of the object. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task DeserializeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task DeserializeAsync(string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#endif + => KiotaSerializer.DeserializeAsync(_jsonContentType, serializedRepresentation, cancellationToken); + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The stream to deserialize. + /// The factory to create the object. + /// The cancellation token for the task + public static Task> DeserializeCollectionAsync(Stream stream, ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + => KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, stream, parsableFactory, cancellationToken); + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The serialized representation of the objects. + /// The factory to create the object. + /// The cancellation token for the task + public static Task> DeserializeCollectionAsync(string serializedRepresentation, ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + => KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, serializedRepresentation, parsableFactory, cancellationToken); + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The stream to deserialize. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task> DeserializeCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task> DeserializeCollectionAsync(Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#endif + => KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, stream, cancellationToken); + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The serialized representation of the object. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task> DeserializeCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task> DeserializeCollectionAsync(string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#endif + => KiotaSerializer.DeserializeCollectionAsync(_jsonContentType, serializedRepresentation, cancellationToken); } \ No newline at end of file diff --git a/src/serialization/KiotaSerializer.Deserialization.cs b/src/serialization/KiotaSerializer.Deserialization.cs index 6f265af8..1600eb9b 100644 --- a/src/serialization/KiotaSerializer.Deserialization.cs +++ b/src/serialization/KiotaSerializer.Deserialization.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; +using System.Threading.Tasks; #if NET5_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif @@ -21,6 +23,7 @@ public static partial class KiotaSerializer /// The content type of the stream. /// The factory to create the object. /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); @@ -31,19 +34,21 @@ private static Stream GetStreamFromString(string source) { var stream = new MemoryStream(); using var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true); - + // Some clients enforce async stream processing. writer.WriteAsync(source).GetAwaiter().GetResult(); writer.Flush(); stream.Position = 0; return stream; } + /// /// Deserializes the given stream into an object based on the content type. /// /// The content type of the stream. /// The stream to deserialize. /// The factory to create the object. + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string contentType, Stream stream, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType)); @@ -57,6 +62,7 @@ private static Stream GetStreamFromString(string source) /// /// The content type of the stream. /// The stream to deserialize. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, Stream stream) where T : IParsable #else @@ -79,6 +85,7 @@ private static ParsableFactory GetFactoryFromType() where T : IParsable /// /// The content type of the stream. /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else @@ -92,6 +99,7 @@ private static ParsableFactory GetFactoryFromType() where T : IParsable /// The content type of the stream. /// The stream to deserialize. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string contentType, Stream stream, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType)); @@ -106,6 +114,7 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// The content type of the stream. /// The serialized representation of the objects. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); @@ -117,6 +126,7 @@ public static IEnumerable DeserializeCollection(string contentType, string /// /// The content type of the stream. /// The stream to deserialize. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, Stream stream) where T : IParsable #else @@ -128,10 +138,133 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// /// The content type of the stream. /// The serialized representation of the object. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else public static IEnumerable DeserializeCollection(string contentType, string serializedRepresentation) where T : IParsable #endif => DeserializeCollection(contentType, serializedRepresentation, GetFactoryFromType()); + + /// + /// Deserializes the given stream into an object based on the content type. + /// + /// The content type of the stream. + /// The factory to create the object. + /// The serialized representation of the object. + /// The cancellation token for the task + public static async Task DeserializeAsync(string contentType, string serializedRepresentation, ParsableFactory parsableFactory, + CancellationToken cancellationToken = default) where T : IParsable + { + if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); + using var stream = await GetStreamFromStringAsync(serializedRepresentation).ConfigureAwait(false); + return await DeserializeAsync(contentType, stream, parsableFactory, cancellationToken).ConfigureAwait(false); + } + private static async Task GetStreamFromStringAsync(string source) + { + var stream = new MemoryStream(); + using var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true); + + // Some clients enforce async stream processing. + await writer.WriteAsync(source); + writer.Flush(); + stream.Position = 0; + return stream; + } + + /// + /// Deserializes the given stream into an object based on the content type. + /// + /// The content type of the stream. + /// The stream to deserialize. + /// The factory to create the object. + /// The cancellation token for the task + public static async Task DeserializeAsync(string contentType, Stream stream, ParsableFactory parsableFactory, + CancellationToken cancellationToken = default) where T : IParsable + { + if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType)); + if(stream == null) throw new ArgumentNullException(nameof(stream)); + if(parsableFactory == null) throw new ArgumentNullException(nameof(parsableFactory)); + var parseNode = await ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNodeAsync(contentType, stream, cancellationToken).ConfigureAwait(false); + return parseNode.GetObjectValue(parsableFactory); + } + + /// + /// Deserializes the given stream into an object based on the content type. + /// + /// The content type of the stream. + /// The stream to deserialize. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task DeserializeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task DeserializeAsync(string contentType, Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#endif + => DeserializeAsync(contentType, stream, GetFactoryFromType(), cancellationToken); + /// + /// Deserializes the given stream into an object based on the content type. + /// + /// The content type of the stream. + /// The serialized representation of the object. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task DeserializeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task DeserializeAsync(string contentType, string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#endif + => DeserializeAsync(contentType, serializedRepresentation, GetFactoryFromType(), cancellationToken); + + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The content type of the stream. + /// The stream to deserialize. + /// The factory to create the object. + /// The cancellation token for the task + public static async Task> DeserializeCollectionAsync(string contentType, Stream stream, ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + { + if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType)); + if(stream == null) throw new ArgumentNullException(nameof(stream)); + if(parsableFactory == null) throw new ArgumentNullException(nameof(parsableFactory)); + var parseNode = await ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNodeAsync(contentType, stream, cancellationToken); + return parseNode.GetCollectionOfObjectValues(parsableFactory); + } + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The content type of the stream. + /// The serialized representation of the objects. + /// The factory to create the object. + /// The cancellation token for the task + public static async Task> DeserializeCollectionAsync(string contentType, string serializedRepresentation, + ParsableFactory parsableFactory, CancellationToken cancellationToken = default) where T : IParsable + { + if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); + using var stream = await GetStreamFromStringAsync(serializedRepresentation).ConfigureAwait(false); + return await DeserializeCollectionAsync(contentType, stream, parsableFactory, cancellationToken).ConfigureAwait(false); + } + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The content type of the stream. + /// The stream to deserialize. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task> DeserializeCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task> DeserializeCollectionAsync(string contentType, Stream stream, CancellationToken cancellationToken = default) where T : IParsable +#endif + => DeserializeCollectionAsync(contentType, stream, GetFactoryFromType(), cancellationToken); + /// + /// Deserializes the given stream into a collection of objects based on the content type. + /// + /// The content type of the stream. + /// The serialized representation of the object. + /// The cancellation token for the task +#if NET5_0_OR_GREATER + public static Task> DeserializeCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#else + public static Task> DeserializeCollectionAsync(string contentType, string serializedRepresentation, CancellationToken cancellationToken = default) where T : IParsable +#endif + => DeserializeCollectionAsync(contentType, serializedRepresentation, GetFactoryFromType(), cancellationToken); } \ No newline at end of file diff --git a/src/serialization/ParseNodeFactoryRegistry.cs b/src/serialization/ParseNodeFactoryRegistry.cs index ff162de6..8806a90b 100644 --- a/src/serialization/ParseNodeFactoryRegistry.cs +++ b/src/serialization/ParseNodeFactoryRegistry.cs @@ -7,13 +7,15 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Kiota.Abstractions.Serialization { /// /// This factory holds a list of all the registered factories for the various types of nodes. /// - public class ParseNodeFactoryRegistry : IParseNodeFactory + public class ParseNodeFactoryRegistry : IAsyncParseNodeFactory { /// /// The valid content type for the @@ -32,14 +34,16 @@ public string ValidContentType /// /// List of factories that are registered by content type. /// - public ConcurrentDictionary ContentTypeAssociatedFactories { get; set; } = new(); + public ConcurrentDictionary ContentTypeAssociatedFactories { get; set; } = new(); internal static readonly Regex contentTypeVendorCleanupRegex = new(@"[^/]+\+", RegexOptions.Compiled); + /// /// Get the instance that is the root of the content /// /// The content type of the stream /// The to parse /// + [Obsolete("Use GetRootParseNodeAsync instead")] public IParseNode GetRootParseNode(string contentType, Stream content) { if(string.IsNullOrEmpty(contentType)) @@ -56,5 +60,29 @@ public IParseNode GetRootParseNode(string contentType, Stream content) throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); } + /// + /// Get the instance that is the root of the content + /// + /// The content type of the stream + /// The to parse + /// The cancellation token for the task + /// + public async Task GetRootParseNodeAsync(string contentType, Stream content, + CancellationToken cancellationToken = default) + { + if(string.IsNullOrEmpty(contentType)) + throw new ArgumentNullException(nameof(contentType)); + _ = content ?? throw new ArgumentNullException(nameof(content)); + + var vendorSpecificContentType = contentType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); + if(ContentTypeAssociatedFactories.TryGetValue(vendorSpecificContentType, out var vendorFactory)) + return await vendorFactory.GetRootParseNodeAsync(vendorSpecificContentType, content, cancellationToken).ConfigureAwait(false); + + var cleanedContentType = contentTypeVendorCleanupRegex.Replace(vendorSpecificContentType, string.Empty); + if(ContentTypeAssociatedFactories.TryGetValue(cleanedContentType, out var factory)) + return await factory.GetRootParseNodeAsync(cleanedContentType, content, cancellationToken); + + throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); + } } } diff --git a/src/serialization/ParseNodeProxyFactory.cs b/src/serialization/ParseNodeProxyFactory.cs index e1516ace..749f3a71 100644 --- a/src/serialization/ParseNodeProxyFactory.cs +++ b/src/serialization/ParseNodeProxyFactory.cs @@ -4,19 +4,21 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Kiota.Abstractions.Serialization { /// /// Proxy factory that allows the composition of before and after callbacks on existing factories. /// - public abstract class ParseNodeProxyFactory : IParseNodeFactory + public abstract class ParseNodeProxyFactory : IAsyncParseNodeFactory { /// /// The valid content type for the instance /// public string ValidContentType { get { return _concrete.ValidContentType; } } - private readonly IParseNodeFactory _concrete; + private readonly IAsyncParseNodeFactory _concrete; private readonly Action _onBefore; private readonly Action _onAfter; /// @@ -25,7 +27,7 @@ public abstract class ParseNodeProxyFactory : IParseNodeFactory /// The concrete factory to wrap. /// The callback to invoke before the deserialization of any model object. /// The callback to invoke after the deserialization of any model object. - public ParseNodeProxyFactory(IParseNodeFactory concrete, Action onBefore, Action onAfter) + public ParseNodeProxyFactory(IAsyncParseNodeFactory concrete, Action onBefore, Action onAfter) { _concrete = concrete ?? throw new ArgumentNullException(nameof(concrete)); _onBefore = onBefore; @@ -37,6 +39,7 @@ public ParseNodeProxyFactory(IParseNodeFactory concrete, Action onBef /// The stream to read the parse node from. /// The content type of the parse node. /// A parse node. + [Obsolete("Use GetRootParseNodeAsync instead")] public IParseNode GetRootParseNode(string contentType, Stream content) { var node = _concrete.GetRootParseNode(contentType, content); @@ -54,5 +57,30 @@ public IParseNode GetRootParseNode(string contentType, Stream content) }; return node; } + /// + /// Create a parse node from the given stream and content type. + /// + /// The stream to read the parse node from. + /// The content type of the parse node. + /// The cancellation token for the task + /// A parse node. + public async Task GetRootParseNodeAsync(string contentType, Stream content, + CancellationToken cancellationToken = default) + { + var node = await _concrete.GetRootParseNodeAsync(contentType, content).ConfigureAwait(false); + var originalBefore = node.OnBeforeAssignFieldValues; + var originalAfter = node.OnAfterAssignFieldValues; + node.OnBeforeAssignFieldValues = (x) => + { + _onBefore?.Invoke(x); + originalBefore?.Invoke(x); + }; + node.OnAfterAssignFieldValues = (x) => + { + _onAfter?.Invoke(x); + originalAfter?.Invoke(x); + }; + return node; + } } } diff --git a/src/store/BackingStoreParseNodeFactory.cs b/src/store/BackingStoreParseNodeFactory.cs index 4b03f844..d555a682 100644 --- a/src/store/BackingStoreParseNodeFactory.cs +++ b/src/store/BackingStoreParseNodeFactory.cs @@ -7,14 +7,14 @@ namespace Microsoft.Kiota.Abstractions.Store { /// - /// Proxy implementation of for the backing store that automatically sets the state of the backing store when deserializing. + /// Proxy implementation of for the backing store that automatically sets the state of the backing store when deserializing. /// public class BackingStoreParseNodeFactory : ParseNodeProxyFactory { /// /// Initializes a new instance of the class given a concrete implementation of . /// - public BackingStoreParseNodeFactory(IParseNodeFactory concrete) : base( + public BackingStoreParseNodeFactory(IAsyncParseNodeFactory concrete) : base( concrete, (x) => { From 7d5acb2c017c3c3975644f23a9d9941e22beb061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 08:53:10 +0200 Subject: [PATCH 02/12] Removes ncrunch settings --- Microsoft.Kiota.Abstractions.v3.ncrunchsolution | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Microsoft.Kiota.Abstractions.v3.ncrunchsolution diff --git a/Microsoft.Kiota.Abstractions.v3.ncrunchsolution b/Microsoft.Kiota.Abstractions.v3.ncrunchsolution deleted file mode 100644 index 13107d39..00000000 --- a/Microsoft.Kiota.Abstractions.v3.ncrunchsolution +++ /dev/null @@ -1,8 +0,0 @@ - - - True - True - True - True - - \ No newline at end of file From c89c76f4dc56b9501b42b8683263b82a82011171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 09:04:29 +0200 Subject: [PATCH 03/12] Reverts public changes to IAsyncParseNodeFactory to avoid breaking backward compatibility --- src/ApiClientBuilder.cs | 6 +++--- src/serialization/ParseNodeFactoryRegistry.cs | 19 ++++++++++++++++--- src/serialization/ParseNodeProxyFactory.cs | 10 +++++++--- src/store/BackingStoreParseNodeFactory.cs | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/ApiClientBuilder.cs b/src/ApiClientBuilder.cs index 70bf3445..141be487 100644 --- a/src/ApiClientBuilder.cs +++ b/src/ApiClientBuilder.cs @@ -29,7 +29,7 @@ public static class ApiClientBuilder /// Registers the default deserializer to the registry. /// /// The type of the parse node factory to register - public static void RegisterDefaultDeserializer() where T : IAsyncParseNodeFactory, new() + public static void RegisterDefaultDeserializer() where T : IParseNodeFactory, new() { var deserializerFactory = new T(); ParseNodeFactoryRegistry.DefaultInstance @@ -60,9 +60,9 @@ public static ISerializationWriterFactory EnableBackingStoreForSerializationWrit /// /// The parse node factory to enable the backing store on. /// A new parse node factory with the backing store enabled. - public static IAsyncParseNodeFactory EnableBackingStoreForParseNodeFactory(IAsyncParseNodeFactory original) + public static IParseNodeFactory EnableBackingStoreForParseNodeFactory(IParseNodeFactory original) { - IAsyncParseNodeFactory result = original ?? throw new ArgumentNullException(nameof(original)); + var result = original ?? throw new ArgumentNullException(nameof(original)); if(original is ParseNodeFactoryRegistry registry) { EnableBackingStoreForParseNodeRegistry(registry); diff --git a/src/serialization/ParseNodeFactoryRegistry.cs b/src/serialization/ParseNodeFactoryRegistry.cs index 8806a90b..adb8c837 100644 --- a/src/serialization/ParseNodeFactoryRegistry.cs +++ b/src/serialization/ParseNodeFactoryRegistry.cs @@ -34,7 +34,7 @@ public string ValidContentType /// /// List of factories that are registered by content type. /// - public ConcurrentDictionary ContentTypeAssociatedFactories { get; set; } = new(); + public ConcurrentDictionary ContentTypeAssociatedFactories { get; set; } = new(); internal static readonly Regex contentTypeVendorCleanupRegex = new(@"[^/]+\+", RegexOptions.Compiled); /// @@ -76,11 +76,24 @@ public async Task GetRootParseNodeAsync(string contentType, Stream c var vendorSpecificContentType = contentType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); if(ContentTypeAssociatedFactories.TryGetValue(vendorSpecificContentType, out var vendorFactory)) - return await vendorFactory.GetRootParseNodeAsync(vendorSpecificContentType, content, cancellationToken).ConfigureAwait(false); + { + if(vendorFactory is not IAsyncParseNodeFactory vendorFactoryAsync) + { + throw new Exception("IAsyncParseNodeFactory is required for async operations"); + } + + return await vendorFactoryAsync.GetRootParseNodeAsync(vendorSpecificContentType, content, cancellationToken).ConfigureAwait(false); + } var cleanedContentType = contentTypeVendorCleanupRegex.Replace(vendorSpecificContentType, string.Empty); if(ContentTypeAssociatedFactories.TryGetValue(cleanedContentType, out var factory)) - return await factory.GetRootParseNodeAsync(cleanedContentType, content, cancellationToken); + { + if(factory is not IAsyncParseNodeFactory vendorFactoryAsync) + { + throw new Exception("IAsyncParseNodeFactory is required for async operations"); + } + return await vendorFactoryAsync.GetRootParseNodeAsync(cleanedContentType, content, cancellationToken); + } throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); } diff --git a/src/serialization/ParseNodeProxyFactory.cs b/src/serialization/ParseNodeProxyFactory.cs index 749f3a71..8d08380a 100644 --- a/src/serialization/ParseNodeProxyFactory.cs +++ b/src/serialization/ParseNodeProxyFactory.cs @@ -18,7 +18,7 @@ public abstract class ParseNodeProxyFactory : IAsyncParseNodeFactory /// The valid content type for the instance /// public string ValidContentType { get { return _concrete.ValidContentType; } } - private readonly IAsyncParseNodeFactory _concrete; + private readonly IParseNodeFactory _concrete; private readonly Action _onBefore; private readonly Action _onAfter; /// @@ -27,7 +27,7 @@ public abstract class ParseNodeProxyFactory : IAsyncParseNodeFactory /// The concrete factory to wrap. /// The callback to invoke before the deserialization of any model object. /// The callback to invoke after the deserialization of any model object. - public ParseNodeProxyFactory(IAsyncParseNodeFactory concrete, Action onBefore, Action onAfter) + public ParseNodeProxyFactory(IParseNodeFactory concrete, Action onBefore, Action onAfter) { _concrete = concrete ?? throw new ArgumentNullException(nameof(concrete)); _onBefore = onBefore; @@ -67,7 +67,11 @@ public IParseNode GetRootParseNode(string contentType, Stream content) public async Task GetRootParseNodeAsync(string contentType, Stream content, CancellationToken cancellationToken = default) { - var node = await _concrete.GetRootParseNodeAsync(contentType, content).ConfigureAwait(false); + if (_concrete is not IAsyncParseNodeFactory asyncConcrete) + { + throw new Exception("IAsyncParseNodeFactory is required for async operations"); + } + var node = await asyncConcrete.GetRootParseNodeAsync(contentType, content).ConfigureAwait(false); var originalBefore = node.OnBeforeAssignFieldValues; var originalAfter = node.OnAfterAssignFieldValues; node.OnBeforeAssignFieldValues = (x) => diff --git a/src/store/BackingStoreParseNodeFactory.cs b/src/store/BackingStoreParseNodeFactory.cs index d555a682..965bfbdc 100644 --- a/src/store/BackingStoreParseNodeFactory.cs +++ b/src/store/BackingStoreParseNodeFactory.cs @@ -14,7 +14,7 @@ public class BackingStoreParseNodeFactory : ParseNodeProxyFactory /// /// Initializes a new instance of the class given a concrete implementation of . /// - public BackingStoreParseNodeFactory(IAsyncParseNodeFactory concrete) : base( + public BackingStoreParseNodeFactory(IParseNodeFactory concrete) : base( concrete, (x) => { From b50ddb208fbc5f75bb822f6c35cd46c47ac18910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 09:15:26 +0200 Subject: [PATCH 04/12] Makes writer.Flush async --- src/serialization/KiotaSerializer.Deserialization.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serialization/KiotaSerializer.Deserialization.cs b/src/serialization/KiotaSerializer.Deserialization.cs index 1600eb9b..aa74d7a7 100644 --- a/src/serialization/KiotaSerializer.Deserialization.cs +++ b/src/serialization/KiotaSerializer.Deserialization.cs @@ -166,8 +166,8 @@ private static async Task GetStreamFromStringAsync(string source) using var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true); // Some clients enforce async stream processing. - await writer.WriteAsync(source); - writer.Flush(); + await writer.WriteAsync(source).ConfigureAwait(false); + await writer.FlushAsync().ConfigureAwait(false); stream.Position = 0; return stream; } From 225bc05a846d0aa19d5047405682dcfd714c1d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 09:34:52 +0200 Subject: [PATCH 05/12] Removes obsolete attribute on functions taking non-stream content --- src/serialization/KiotaJsonSerializer.Deserialization.cs | 4 ---- src/serialization/KiotaSerializer.Deserialization.cs | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/serialization/KiotaJsonSerializer.Deserialization.cs b/src/serialization/KiotaJsonSerializer.Deserialization.cs index 515a0662..677533b7 100644 --- a/src/serialization/KiotaJsonSerializer.Deserialization.cs +++ b/src/serialization/KiotaJsonSerializer.Deserialization.cs @@ -24,7 +24,6 @@ public static partial class KiotaJsonSerializer /// /// The factory to create the object. /// The serialized representation of the object. - [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.Deserialize(_jsonContentType, serializedRepresentation, parsableFactory); /// @@ -50,7 +49,6 @@ public static partial class KiotaJsonSerializer /// Deserializes the given stream into an object. /// /// The serialized representation of the object. - [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else @@ -70,7 +68,6 @@ public static IEnumerable DeserializeCollection(Stream stream, ParsableFac /// /// The serialized representation of the objects. /// The factory to create the object. - [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.DeserializeCollection(_jsonContentType, serializedRepresentation, parsableFactory); /// @@ -88,7 +85,6 @@ public static IEnumerable DeserializeCollection(Stream stream) where T : I /// Deserializes the given stream into a collection of objects based on the content type. /// /// The serialized representation of the object. - [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else diff --git a/src/serialization/KiotaSerializer.Deserialization.cs b/src/serialization/KiotaSerializer.Deserialization.cs index aa74d7a7..c04d442f 100644 --- a/src/serialization/KiotaSerializer.Deserialization.cs +++ b/src/serialization/KiotaSerializer.Deserialization.cs @@ -23,12 +23,14 @@ public static partial class KiotaSerializer /// The content type of the stream. /// The factory to create the object. /// The serialized representation of the object. - [Obsolete("Use DeserializeAsync instead")] + //[Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); using var stream = GetStreamFromString(serializedRepresentation); +#pragma warning disable CS0618 // Type or member is obsolete return Deserialize(contentType, stream, parsableFactory); +#pragma warning restore CS0618 // Type or member is obsolete } private static Stream GetStreamFromString(string source) { @@ -85,7 +87,6 @@ private static ParsableFactory GetFactoryFromType() where T : IParsable /// /// The content type of the stream. /// The serialized representation of the object. - [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else @@ -114,12 +115,13 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// The content type of the stream. /// The serialized representation of the objects. /// The factory to create the object. - [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); using var stream = GetStreamFromString(serializedRepresentation); +#pragma warning disable CS0618 // Type or member is obsolete return DeserializeCollection(contentType, stream, parsableFactory); +#pragma warning restore CS0618 // Type or member is obsolete } /// /// Deserializes the given stream into a collection of objects based on the content type. @@ -138,7 +140,6 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// /// The content type of the stream. /// The serialized representation of the object. - [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else From 7a79e226808612c87408926e72da46fdedabad31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 16:05:41 +0200 Subject: [PATCH 06/12] Optimizes ParseNodeFactoryRegistry --- src/serialization/ParseNodeFactoryRegistry.cs | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/serialization/ParseNodeFactoryRegistry.cs b/src/serialization/ParseNodeFactoryRegistry.cs index adb8c837..bb833605 100644 --- a/src/serialization/ParseNodeFactoryRegistry.cs +++ b/src/serialization/ParseNodeFactoryRegistry.cs @@ -50,15 +50,8 @@ public IParseNode GetRootParseNode(string contentType, Stream content) throw new ArgumentNullException(nameof(contentType)); _ = content ?? throw new ArgumentNullException(nameof(content)); - var vendorSpecificContentType = contentType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); - if(ContentTypeAssociatedFactories.TryGetValue(vendorSpecificContentType, out var vendorFactory)) - return vendorFactory.GetRootParseNode(vendorSpecificContentType, content); - - var cleanedContentType = contentTypeVendorCleanupRegex.Replace(vendorSpecificContentType, string.Empty); - if(ContentTypeAssociatedFactories.TryGetValue(cleanedContentType, out var factory)) - return factory.GetRootParseNode(cleanedContentType, content); - - throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); + var (factory, correctContentType) = GetFactory(contentType); + return factory.GetRootParseNode(correctContentType, content); } /// /// Get the instance that is the root of the content @@ -74,28 +67,46 @@ public async Task GetRootParseNodeAsync(string contentType, Stream c throw new ArgumentNullException(nameof(contentType)); _ = content ?? throw new ArgumentNullException(nameof(content)); + var (factory, correctContentType) = GetFactory(contentType); + + return await factory.GetRootParseNodeAsync(correctContentType, content, cancellationToken).ConfigureAwait(false); + } + /// + /// Get the instance for the given . + /// + /// Type of the . + /// The content type of the stream + /// + /// + /// + public (T, string ContentType) GetFactory(string contentType) + where T: IParseNodeFactory + { + string resultContentType; var vendorSpecificContentType = contentType.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).First(); - if(ContentTypeAssociatedFactories.TryGetValue(vendorSpecificContentType, out var vendorFactory)) + IParseNodeFactory? factory; + if(!ContentTypeAssociatedFactories.TryGetValue(vendorSpecificContentType, out factory)) { - if(vendorFactory is not IAsyncParseNodeFactory vendorFactoryAsync) + var cleanedContentType = resultContentType = contentTypeVendorCleanupRegex.Replace(vendorSpecificContentType, string.Empty); + if(!ContentTypeAssociatedFactories.TryGetValue(cleanedContentType, out factory)) { - throw new Exception("IAsyncParseNodeFactory is required for async operations"); + throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); } - - return await vendorFactoryAsync.GetRootParseNodeAsync(vendorSpecificContentType, content, cancellationToken).ConfigureAwait(false); - } - - var cleanedContentType = contentTypeVendorCleanupRegex.Replace(vendorSpecificContentType, string.Empty); - if(ContentTypeAssociatedFactories.TryGetValue(cleanedContentType, out var factory)) - { - if(factory is not IAsyncParseNodeFactory vendorFactoryAsync) + else { - throw new Exception("IAsyncParseNodeFactory is required for async operations"); + resultContentType = cleanedContentType; } - return await vendorFactoryAsync.GetRootParseNodeAsync(cleanedContentType, content, cancellationToken); + } + else + { + resultContentType = vendorSpecificContentType; } - throw new InvalidOperationException($"Content type {cleanedContentType} does not have a factory registered to be parsed"); + if(factory is T typedFactory) + { + return (typedFactory, resultContentType); + } + throw new Exception($"{typeof(T).Name} factory is required"); } } } From 70aa3f0d0304ae0ba887a607e315eaa17aa511cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 16:17:43 +0200 Subject: [PATCH 07/12] Optimizes ParseNodeProxyFactory --- src/serialization/ParseNodeProxyFactory.cs | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/serialization/ParseNodeProxyFactory.cs b/src/serialization/ParseNodeProxyFactory.cs index 8d08380a..979a1c1a 100644 --- a/src/serialization/ParseNodeProxyFactory.cs +++ b/src/serialization/ParseNodeProxyFactory.cs @@ -43,6 +43,15 @@ public ParseNodeProxyFactory(IParseNodeFactory concrete, Action onBef public IParseNode GetRootParseNode(string contentType, Stream content) { var node = _concrete.GetRootParseNode(contentType, content); + WireParseNode(node); + return node; + } + /// + /// Wires node to before and after actions. + /// + /// A parse node to wire. + private void WireParseNode(IParseNode node) + { var originalBefore = node.OnBeforeAssignFieldValues; var originalAfter = node.OnAfterAssignFieldValues; node.OnBeforeAssignFieldValues = (x) => @@ -55,7 +64,6 @@ public IParseNode GetRootParseNode(string contentType, Stream content) _onAfter?.Invoke(x); originalAfter?.Invoke(x); }; - return node; } /// /// Create a parse node from the given stream and content type. @@ -72,18 +80,7 @@ public async Task GetRootParseNodeAsync(string contentType, Stream c throw new Exception("IAsyncParseNodeFactory is required for async operations"); } var node = await asyncConcrete.GetRootParseNodeAsync(contentType, content).ConfigureAwait(false); - var originalBefore = node.OnBeforeAssignFieldValues; - var originalAfter = node.OnAfterAssignFieldValues; - node.OnBeforeAssignFieldValues = (x) => - { - _onBefore?.Invoke(x); - originalBefore?.Invoke(x); - }; - node.OnAfterAssignFieldValues = (x) => - { - _onAfter?.Invoke(x); - originalAfter?.Invoke(x); - }; + WireParseNode(node); return node; } } From e4ef81c49dbbb0fdbf6e82ba2c00506736800581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 24 Apr 2024 17:11:45 +0200 Subject: [PATCH 08/12] Fixes wrong T parameter when calling ParseNodeFactoryRegistry.GetFactory --- src/serialization/ParseNodeFactoryRegistry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serialization/ParseNodeFactoryRegistry.cs b/src/serialization/ParseNodeFactoryRegistry.cs index bb833605..d4750d6a 100644 --- a/src/serialization/ParseNodeFactoryRegistry.cs +++ b/src/serialization/ParseNodeFactoryRegistry.cs @@ -50,7 +50,7 @@ public IParseNode GetRootParseNode(string contentType, Stream content) throw new ArgumentNullException(nameof(contentType)); _ = content ?? throw new ArgumentNullException(nameof(content)); - var (factory, correctContentType) = GetFactory(contentType); + var (factory, correctContentType) = GetFactory(contentType); return factory.GetRootParseNode(correctContentType, content); } /// From 60be47a13e6eb65e718e5a214bb1381c2479e65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Fri, 26 Apr 2024 19:59:52 +0200 Subject: [PATCH 09/12] Marks or sync deserialization methods as obsolete --- src/serialization/KiotaJsonSerializer.Deserialization.cs | 4 ++++ src/serialization/KiotaSerializer.Deserialization.cs | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/serialization/KiotaJsonSerializer.Deserialization.cs b/src/serialization/KiotaJsonSerializer.Deserialization.cs index 677533b7..515a0662 100644 --- a/src/serialization/KiotaJsonSerializer.Deserialization.cs +++ b/src/serialization/KiotaJsonSerializer.Deserialization.cs @@ -24,6 +24,7 @@ public static partial class KiotaJsonSerializer /// /// The factory to create the object. /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.Deserialize(_jsonContentType, serializedRepresentation, parsableFactory); /// @@ -49,6 +50,7 @@ public static partial class KiotaJsonSerializer /// Deserializes the given stream into an object. /// /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else @@ -68,6 +70,7 @@ public static IEnumerable DeserializeCollection(Stream stream, ParsableFac /// /// The serialized representation of the objects. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable => KiotaSerializer.DeserializeCollection(_jsonContentType, serializedRepresentation, parsableFactory); /// @@ -85,6 +88,7 @@ public static IEnumerable DeserializeCollection(Stream stream) where T : I /// Deserializes the given stream into a collection of objects based on the content type. /// /// The serialized representation of the object. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string serializedRepresentation) where T : IParsable #else diff --git a/src/serialization/KiotaSerializer.Deserialization.cs b/src/serialization/KiotaSerializer.Deserialization.cs index c04d442f..aa74d7a7 100644 --- a/src/serialization/KiotaSerializer.Deserialization.cs +++ b/src/serialization/KiotaSerializer.Deserialization.cs @@ -23,14 +23,12 @@ public static partial class KiotaSerializer /// The content type of the stream. /// The factory to create the object. /// The serialized representation of the object. - //[Obsolete("Use DeserializeAsync instead")] + [Obsolete("Use DeserializeAsync instead")] public static T? Deserialize(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); using var stream = GetStreamFromString(serializedRepresentation); -#pragma warning disable CS0618 // Type or member is obsolete return Deserialize(contentType, stream, parsableFactory); -#pragma warning restore CS0618 // Type or member is obsolete } private static Stream GetStreamFromString(string source) { @@ -87,6 +85,7 @@ private static ParsableFactory GetFactoryFromType() where T : IParsable /// /// The content type of the stream. /// The serialized representation of the object. + [Obsolete("Use DeserializeAsync instead")] #if NET5_0_OR_GREATER public static T? Deserialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else @@ -115,13 +114,12 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// The content type of the stream. /// The serialized representation of the objects. /// The factory to create the object. + [Obsolete("Use DeserializeCollectionAsync instead")] public static IEnumerable DeserializeCollection(string contentType, string serializedRepresentation, ParsableFactory parsableFactory) where T : IParsable { if(string.IsNullOrEmpty(serializedRepresentation)) throw new ArgumentNullException(nameof(serializedRepresentation)); using var stream = GetStreamFromString(serializedRepresentation); -#pragma warning disable CS0618 // Type or member is obsolete return DeserializeCollection(contentType, stream, parsableFactory); -#pragma warning restore CS0618 // Type or member is obsolete } /// /// Deserializes the given stream into a collection of objects based on the content type. @@ -140,6 +138,7 @@ public static IEnumerable DeserializeCollection(string contentType, Stream /// /// The content type of the stream. /// The serialized representation of the object. + [Obsolete("Use DeserializeCollectionAsync instead")] #if NET5_0_OR_GREATER public static IEnumerable DeserializeCollection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string contentType, string serializedRepresentation) where T : IParsable #else From cca0cde56bdb921909179d7fdd754c23e0cc401b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sat, 27 Apr 2024 14:43:58 +0200 Subject: [PATCH 10/12] Marks KiotaSerializer.GetStreamFromString as obsolete, removes asnyc within --- src/serialization/KiotaSerializer.Deserialization.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serialization/KiotaSerializer.Deserialization.cs b/src/serialization/KiotaSerializer.Deserialization.cs index aa74d7a7..63f6a07e 100644 --- a/src/serialization/KiotaSerializer.Deserialization.cs +++ b/src/serialization/KiotaSerializer.Deserialization.cs @@ -30,13 +30,13 @@ public static partial class KiotaSerializer using var stream = GetStreamFromString(serializedRepresentation); return Deserialize(contentType, stream, parsableFactory); } + [Obsolete("Use GetStreamFromStringAsync instead")] private static Stream GetStreamFromString(string source) { var stream = new MemoryStream(); using var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true); - // Some clients enforce async stream processing. - writer.WriteAsync(source).GetAwaiter().GetResult(); + writer.Write(source); writer.Flush(); stream.Position = 0; return stream; From 4390113e68053c59ece134025573d3f9a62363ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Sat, 27 Apr 2024 14:57:22 +0200 Subject: [PATCH 11/12] Updates changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e353bc..d9fc4976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +Adds asynchronous deserialization support and marks synchronous as obsolete. https://github.com/microsoft/kiota-abstractions-dotnet/issues/223 + +### Added + +- Added asynchronous deserialization methods (to KiotaJsonSerializer.Deserialization and KiotaSerializer.Deserialization). +- Added IAsyncParseNodeFactory interface to provide asynchronous version of GetRootParseNode: GetRootParseNodeAsync. +- Added ParseNodeFactoryRegistry.GetRootParseNodeAsync method. +- Added ParseNodeProxyFactory.GetRootParseNodeAsync method + +### Changed + +- Marked synchronous deserialization methods as obsolete. +- Marked IParseNodeFactory.GetRootParseNode as obsolete. +- Refactored ParseNodeFactoryRegistry.GetFactory to support both asynchronous (IAsyncParseNodeFactory) and synchronous (IParseNodeFactory) factories. + ## [1.8.4] - 2024-04-19 - Bumps Std.UriTemplate to version 0.0.57 From 70fd56829af8533ad4b1153499c6a821f7b2fa26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Marki=C4=8D?= Date: Wed, 1 May 2024 16:42:26 +0200 Subject: [PATCH 12/12] Sets version to [1.9.0] - 2024-05-06 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9fc4976..3a41d403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.9.0] - 2024-05-06 + Adds asynchronous deserialization support and marks synchronous as obsolete. https://github.com/microsoft/kiota-abstractions-dotnet/issues/223 ### Added