From eeda85d7437479e3735495b2c578bf9cbda39b98 Mon Sep 17 00:00:00 2001 From: Justin Adler Date: Fri, 29 Dec 2023 17:24:59 +1100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Case=20insensitive=20deserializa?= =?UTF-8?q?tion=20for=20complex=20json=20queue=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ParseMessageAsyncTests.cs | 33 +++++++++++++++++++ .../HybridQueue.cs | 18 ++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/SimpleAzure.Storage.HybridQueues.Tests/IntegrationTests/HybridQueueTests/ParseMessageAsyncTests.cs diff --git a/src/SimpleAzure.Storage.HybridQueues.Tests/IntegrationTests/HybridQueueTests/ParseMessageAsyncTests.cs b/src/SimpleAzure.Storage.HybridQueues.Tests/IntegrationTests/HybridQueueTests/ParseMessageAsyncTests.cs new file mode 100644 index 0000000..8542cdf --- /dev/null +++ b/src/SimpleAzure.Storage.HybridQueues.Tests/IntegrationTests/HybridQueueTests/ParseMessageAsyncTests.cs @@ -0,0 +1,33 @@ +using Azure.Storage.Queues.Models; + + +namespace WorldDomination.SimpleAzure.Storage.HybridQueues.Tests.IntegrationTests.HybridQueueTests +{ + public class ParseMessageAsyncTests : CustomAzuriteTestContainer + { + public record FakePerson(string Name, int Age); + + [Theory] + [InlineData("{\"Name\": \"Anabel\", \"Age\": 30}")] // Note: Capitalized keys. + [InlineData("{\"name\": \"Anabel\", \"age\": 30}")] // Note: Lowercase keys. + [InlineData("{\"nAmE\": \"Anabel\", \"aGe\": 30}")] // Note: Mixed case keys. + public async Task ParseMessageAsync_LowercaseJsonKeys_CaseInsensitive(string json) + { + // Arrange. + var queueMessage = QueuesModelFactory.QueueMessage( + "1", + "2", + new BinaryData(json), + 0); + + // Act. + var fakePerson = await HybridQueue.ParseMessageAsync(queueMessage, CancellationToken.None); + + // Assert. + fakePerson.ShouldNotBeNull(); + fakePerson.Content.ShouldNotBeNull(); + fakePerson.Content.Name.ShouldBe("Anabel"); + fakePerson.Content.Age.ShouldBe(30); + } + } +} diff --git a/src/SimpleAzure.Storage.HybridQueues/HybridQueue.cs b/src/SimpleAzure.Storage.HybridQueues/HybridQueue.cs index 0910069..e00ce01 100644 --- a/src/SimpleAzure.Storage.HybridQueues/HybridQueue.cs +++ b/src/SimpleAzure.Storage.HybridQueues/HybridQueue.cs @@ -22,6 +22,11 @@ public sealed class HybridQueue( ContentType = "application/json", }; + private static readonly JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNameCaseInsensitive = true + }; + public async Task SetupContainerStorageAsync(bool isLoggingEnabled, CancellationToken cancellationToken) { var blobAzureResponse = await _blobContainerClient @@ -234,6 +239,8 @@ public async Task> ParseMessageAsync(QueueMessage queueMessa { var message = queueMessage.Body.ToString().AssumeNotNull(); + // Queue Message: Guid == item is in Blob Storage. + // Blob Storage: Complex Type. Json message. if (Guid.TryParse(message, out var blobId)) { using var _ = _logger.BeginCustomScope((nameof(blobId), blobId)); @@ -245,7 +252,8 @@ public async Task> ParseMessageAsync(QueueMessage queueMessa using var stream = await blobClient.OpenReadAsync(null, cancellationToken).ConfigureAwait(false); _logger.LogDebug("About to deserialize stream for a blob item from Blob Storage."); - var blobItem = await JsonSerializer.DeserializeAsync(stream, cancellationToken: cancellationToken).ConfigureAwait(false); + + var blobItem = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false); _logger.LogDebug("Finished deserializing stream for a blob item from Blob Storage."); if (blobItem is null) @@ -256,6 +264,9 @@ public async Task> ParseMessageAsync(QueueMessage queueMessa var hybridMessage = new HybridMessage(blobItem, queueMessage.MessageId, queueMessage.PopReceipt, blobId); return hybridMessage; } + + // Queue Message: simple type. Not JSON. + // Blob Storage: N/A else if (typeof(T).IsASimpleType()) { _logger.LogDebug("Retrieving item: which is a simle type and not a guid/not in Blob Storage."); @@ -266,12 +277,15 @@ public async Task> ParseMessageAsync(QueueMessage queueMessa var hybridMessage = new HybridMessage(value, queueMessage.MessageId, queueMessage.PopReceipt, null); return hybridMessage; } + + // Queue Message: complex type. Json. + // Blob Storage: N/A else { // Complex type, so lets assume it was serialized as Json ... so now we deserialize it. _logger.LogDebug("Retrieving a complex item: assumed as json so deserializing it."); - var item = JsonSerializer.Deserialize(message); + var item = JsonSerializer.Deserialize(message, _jsonSerializerOptions); if (item is null) { throw new InvalidOperationException($"Could not deserialize complex type for message '{queueMessage.MessageId}'.");