diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props index 45a1c3a2550b0..4d986f54f52c5 100644 --- a/eng/Packages.Data.props +++ b/eng/Packages.Data.props @@ -82,7 +82,7 @@ - + diff --git a/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs b/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs index d1f18b04a33fb..b5dc6a22b536a 100644 --- a/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs +++ b/sdk/core/Azure.Core.Experimental/api/Azure.Core.Experimental.netstandard2.0.cs @@ -86,3 +86,13 @@ public void Set(string propertyName, string? value) { } public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) { throw null; } } } +namespace Azure.Messaging +{ + public abstract partial class MessageWithMetadata + { + protected MessageWithMetadata() { } + public abstract string ContentType { get; set; } + public abstract System.BinaryData Data { get; set; } + public abstract bool IsReadOnly { get; } + } +} diff --git a/sdk/core/Azure.Core.Experimental/src/MessageWithMetadata.cs b/sdk/core/Azure.Core.Experimental/src/MessageWithMetadata.cs new file mode 100644 index 0000000000000..6e82376136b67 --- /dev/null +++ b/sdk/core/Azure.Core.Experimental/src/MessageWithMetadata.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.Messaging +{ + /// + /// An abstraction for a message containing a content type along with its data. + /// + public abstract class MessageWithMetadata + { + /// + /// Gets or sets the message data. + /// + public abstract BinaryData Data { get; set; } + + /// + /// Gets or sets the message content type. + /// + public abstract string ContentType { get; set; } + + /// + /// Gets whether the message is read only or not. + /// + public abstract bool IsReadOnly { get; } + } +} \ No newline at end of file diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/Azure.Messaging.EventHubs.Processor.sln b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/Azure.Messaging.EventHubs.Processor.sln index c514d9e659bfd..3b926a0bc12af 100755 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/Azure.Messaging.EventHubs.Processor.sln +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/Azure.Messaging.EventHubs.Processor.sln @@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{79 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Messaging.EventHubs", "..\Azure.Messaging.EventHubs\src\Azure.Messaging.EventHubs.csproj", "{87A3ED70-190D-4E6B-A568-40DF5B9F3939}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Core.Experimental", "..\..\core\Azure.Core.Experimental\src\Azure.Core.Experimental.csproj", "{0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {87A3ED70-190D-4E6B-A568-40DF5B9F3939}.Debug|Any CPU.Build.0 = Debug|Any CPU {87A3ED70-190D-4E6B-A568-40DF5B9F3939}.Release|Any CPU.ActiveCfg = Release|Any CPU {87A3ED70-190D-4E6B-A568-40DF5B9F3939}.Release|Any CPU.Build.0 = Release|Any CPU + {0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -42,6 +48,7 @@ Global GlobalSection(NestedProjects) = preSolution {7DFF0E65-DC9A-410D-9A11-AD6A06860FE1} = {797FF941-76FD-45FD-AC17-A73DFE2BA621} {87A3ED70-190D-4E6B-A568-40DF5B9F3939} = {797FF941-76FD-45FD-AC17-A73DFE2BA621} + {0BFCCE8E-85FD-4DF7-8E5D-A6CE9ACDB175} = {797FF941-76FD-45FD-AC17-A73DFE2BA621} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {44BD3BD5-61DF-464D-8627-E00B0BC4B3A3} diff --git a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj index a364a95ae0c33..bc6b4fd2c1189 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj +++ b/sdk/eventhub/Azure.Messaging.EventHubs.Processor/src/Azure.Messaging.EventHubs.Processor.csproj @@ -13,7 +13,11 @@ + @@ -59,4 +63,7 @@ Azure.Messaging.EventHubs + + + diff --git a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs index 801250f188215..78d0cf1d6ad19 100644 --- a/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs +++ b/sdk/eventhub/Azure.Messaging.EventHubs/src/EventData.cs @@ -7,8 +7,8 @@ using System.IO; using System.Text; using Azure.Core; -using Azure.Core.Amqp; using Azure.Core.Serialization; +using Azure.Core.Amqp; using Azure.Messaging.EventHubs.Amqp; using Azure.Messaging.EventHubs.Consumer; using Azure.Messaging.EventHubs.Producer; @@ -19,7 +19,7 @@ namespace Azure.Messaging.EventHubs /// An Event Hubs event, encapsulating a set of data and its associated metadata. /// /// - public class EventData + public class EventData : MessageWithMetadata { /// The AMQP representation of the event, allowing access to additional protocol data elements not used directly by the Event Hubs client library. private readonly AmqpAnnotatedMessage _amqpMessage; @@ -48,6 +48,17 @@ public BinaryData EventBody set => _amqpMessage.Body = AmqpMessageBody.FromData(MessageBody.FromReadOnlyMemorySegment(value.ToMemory())); } + /// + /// Hidden property that shadows the property. This is added + /// in order to inherit from . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override BinaryData Data + { + get => EventBody; + set => EventBody = value; + } + /// /// A MIME type describing the data contained in the , /// intended to allow consumers to make informed decisions for inspecting and @@ -71,7 +82,7 @@ public BinaryData EventBody /// /// RFC2046 (MIME Types) /// - public string ContentType + public override string ContentType { get { @@ -96,6 +107,13 @@ public string ContentType } } + /// + /// Hidden property that indicates that the is not read-only. This is part of + /// the abstraction. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool IsReadOnly => false; + /// /// An application-defined value that uniquely identifies the event. The identifier is /// a free-form value and can reflect a GUID or an identifier derived from the application diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.sln b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.sln index afe1df921037d..80a209b9980c2 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.sln +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.sln @@ -11,6 +11,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core.TestFramework", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Data.SchemaRegistry", "..\Azure.Data.SchemaRegistry\src\Azure.Data.SchemaRegistry.csproj", "{D451EE68-ADE4-4780-A002-2D13DEA888A2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Core.Experimental", "..\..\core\Azure.Core.Experimental\src\Azure.Core.Experimental.csproj", "{8A0A0FBB-2B12-49B3-951E-5402D64075DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Messaging.EventHubs", "..\..\eventhub\Azure.Messaging.EventHubs\src\Azure.Messaging.EventHubs.csproj", "{FDC382B3-7255-4D33-AAA9-1168B7D01E94}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Messaging.ServiceBus", "..\..\servicebus\Azure.Messaging.ServiceBus\src\Azure.Messaging.ServiceBus.csproj", "{69E3D417-F0A9-4C6B-B9C5-8C29E9F2A660}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +39,18 @@ Global {D451EE68-ADE4-4780-A002-2D13DEA888A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {D451EE68-ADE4-4780-A002-2D13DEA888A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {D451EE68-ADE4-4780-A002-2D13DEA888A2}.Release|Any CPU.Build.0 = Release|Any CPU + {8A0A0FBB-2B12-49B3-951E-5402D64075DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A0A0FBB-2B12-49B3-951E-5402D64075DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A0A0FBB-2B12-49B3-951E-5402D64075DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A0A0FBB-2B12-49B3-951E-5402D64075DD}.Release|Any CPU.Build.0 = Release|Any CPU + {FDC382B3-7255-4D33-AAA9-1168B7D01E94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDC382B3-7255-4D33-AAA9-1168B7D01E94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDC382B3-7255-4D33-AAA9-1168B7D01E94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDC382B3-7255-4D33-AAA9-1168B7D01E94}.Release|Any CPU.Build.0 = Release|Any CPU + {69E3D417-F0A9-4C6B-B9C5-8C29E9F2A660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69E3D417-F0A9-4C6B-B9C5-8C29E9F2A660}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69E3D417-F0A9-4C6B-B9C5-8C29E9F2A660}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69E3D417-F0A9-4C6B-B9C5-8C29E9F2A660}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/README.md b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/README.md index 05117c0ea05ac..920effabeed5d 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/README.md +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/README.md @@ -1,6 +1,6 @@ # Azure Schema Registry Apache Avro client library for .NET -Azure Schema Registry is a schema repository service hosted by Azure Event Hubs, providing schema storage, versioning, and management. This package provides an Avro serializer capable of serializing and deserializing payloads containing Schema Registry schema identifiers and Avro-encoded data. +Azure Schema Registry is a schema repository service hosted by Azure Event Hubs, providing schema storage, versioning, and management. This package provides an Avro encoder capable of encoding and decoding payloads containing Schema Registry schema identifiers and Avro-encoded data. ## Getting started @@ -50,23 +50,17 @@ var schemaRegistryClient = new SchemaRegistryClient(fullyQualifiedNamespace: ful ## Key concepts -### ObjectSerializer +### Encoder -This library provides a serializer, [SchemaRegistryAvroObjectSerializer][schema_registry_avro_serializer], that implements the [ObjectSerializer][object_serializer] abstract class. This allows a developer to use this serializer in any .NET Azure SDKs that utilize ObjectSerializer. The SchemaRegistryAvroObjectSerializer utilitizes a SchemaRegistryClient to construct messages using a wire format containing schema information such as a schema ID. +This library provides an encoder, [SchemaRegistryAvroEncoder] +[schema_registry_avro_encoder], that interacts with `EventData` events. The SchemaRegistryAvroEncoder utilizes a SchemaRegistryClient to enrich the `EventData` events with the schema ID for the schema used to encode the data. -This serializer requires the [Apache Avro library][apache_avro_library]. The payload types accepted by this serializer include [GenericRecord][generic_record] and [ISpecificRecord][specific_record]. +This encoder requires the [Apache Avro library][apache_avro_library]. The payload types accepted by this encoder include [GenericRecord][generic_record] and [ISpecificRecord][specific_record]. -### Wire Format -The serializer in this library creates messages in a wire format. The format is the following: +### Examples -- Bytes [0-3] – record format indicator – currently is \x00\x00\x00\x00 -- Bytes [4-35] – UTF-8 GUID, identifying the schema in a Schema Registry instance -- Bytes [36-end] – serialized payload bytes - -## Examples - -The following shows examples of what is available through the SchemaRegistryAvroObjectSerializer. There are both sync and async methods available for these operations. These examples use a generated Apache Avro class [Employee.cs][employee] created using this schema: +The following shows examples of what is available through the `SchemaRegistryAvroEncoder`. There are both sync and async methods available for these operations. These examples use a generated Apache Avro class [Employee.cs][employee] created using this schema: ```json { @@ -82,31 +76,30 @@ The following shows examples of what is available through the SchemaRegistryAvro Details on generating a class using the Apache Avro library can be found in the [Avro C# Documentation][avro_csharp_documentation]. -* [Serialize](#serialize) -* [Deserialize](#deserialize) +### Encode and decode data using the Event Hub EventData model -### Serialize +In order to encode an `EventData` instance with Avro information, you can do the following: +```C# Snippet:SchemaRegistryAvroEncodeEventData +var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); -Register a schema to be stored in the Azure Schema Registry. +var employee = new Employee { Age = 42, Name = "Caketown" }; +EventData eventData = await encoder.EncodeMessageDataAsync(employee); -```C# Snippet:SchemaRegistryAvroSerialize -var employee = new Employee { Age = 42, Name = "John Doe" }; +// the schema Id will be included as a parameter of the content type +Console.WriteLine(eventData.ContentType); -using var memoryStream = new MemoryStream(); -var serializer = new SchemaRegistryAvroObjectSerializer(schemaRegistryClient, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); -serializer.Serialize(memoryStream, employee, typeof(Employee), CancellationToken.None); +// the serialized Avro data will be stored in the EventBody +Console.WriteLine(eventData.EventBody); ``` -### Deserialize - -Retrieve a previously registered schema ID from the Azure Schema Registry. - -```C# Snippet:SchemaRegistryAvroDeserialize -var serializer = new SchemaRegistryAvroObjectSerializer(schemaRegistryClient, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); -memoryStream.Position = 0; -Employee employee = (Employee)serializer.Deserialize(memoryStream, typeof(Employee), CancellationToken.None); +To decode an `EventData` event that you are consuming: +```C# Snippet:SchemaRegistryAvroDecodeEventData +Employee deserialized = (Employee)await encoder.DecodeMessageDataAsync(eventData, typeof(Employee)); +Console.WriteLine(deserialized.Age); +Console.WriteLine(deserialized.Name); ``` + ## Troubleshooting Information on troubleshooting steps will be provided as potential issues are discovered. @@ -139,8 +132,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ [code_of_conduct_faq]: https://opensource.microsoft.com/codeofconduct/faq/ [email_opencode]: mailto:opencode@microsoft.com -[object_serializer]: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/src/Serialization/ObjectSerializer.cs -[schema_registry_avro_serializer]: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializer.cs +[schema_registry_avro_encoder]: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroEncoder.cs [employee]: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Models/Employee.cs [avro_csharp_documentation]: https://avro.apache.org/docs/current/api/csharp/html/index.html [apache_avro_library]: https://www.nuget.org/packages/Apache.Avro/ diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/api/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.netstandard2.0.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/api/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.netstandard2.0.cs index 338fd2b7eaeb8..b540da681814a 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/api/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.netstandard2.0.cs +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/api/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.netstandard2.0.cs @@ -1,16 +1,16 @@ namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro { - public partial class SchemaRegistryAvroObjectSerializer : Azure.Core.Serialization.ObjectSerializer + public partial class SchemaRegistryAvroEncoder { - public SchemaRegistryAvroObjectSerializer(Azure.Data.SchemaRegistry.SchemaRegistryClient client, string groupName, Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.SchemaRegistryAvroObjectSerializerOptions options = null) { } - public override object Deserialize(System.IO.Stream stream, System.Type returnType, System.Threading.CancellationToken cancellationToken) { throw null; } - public override System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream stream, System.Type returnType, System.Threading.CancellationToken cancellationToken) { throw null; } - public override void Serialize(System.IO.Stream stream, object value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { } - public override System.Threading.Tasks.ValueTask SerializeAsync(System.IO.Stream stream, object value, System.Type inputType, System.Threading.CancellationToken cancellationToken) { throw null; } + public SchemaRegistryAvroEncoder(Azure.Data.SchemaRegistry.SchemaRegistryClient client, string groupName, Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.SchemaRegistryAvroObjectEncoderOptions options = null) { } + public object DecodeMessageData(Azure.Messaging.MessageWithMetadata message, System.Type returnType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask DecodeMessageDataAsync(Azure.Messaging.MessageWithMetadata message, System.Type returnType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask EncodeMessageDataAsync(object data, System.Type inputType = null, System.Func messageFactory = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where T : Azure.Messaging.MessageWithMetadata { throw null; } + public T EncodeMessageData(object data, System.Type inputType = null, System.Func messageFactory = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) where T : Azure.Messaging.MessageWithMetadata { throw null; } } - public partial class SchemaRegistryAvroObjectSerializerOptions + public partial class SchemaRegistryAvroObjectEncoderOptions { - public SchemaRegistryAvroObjectSerializerOptions() { } + public SchemaRegistryAvroObjectEncoderOptions() { } public bool AutoRegisterSchemas { get { throw null; } set { } } } } diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.csproj b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.csproj index 07990885b82d4..d37785f371c07 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.csproj +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.csproj @@ -8,9 +8,7 @@ - - @@ -22,5 +20,9 @@ + + + + diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Properties/AssemblyInfo.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000..c77ed5dbcbadf --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroEncoder.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroEncoder.cs new file mode 100644 index 0000000000000..6e27212cff907 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroEncoder.cs @@ -0,0 +1,341 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Avro; +using Avro.Generic; +using Avro.IO; +using Avro.Specific; +using Azure.Core; +using Azure.Core.Serialization; +using Azure.Data.SchemaRegistry; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core.Pipeline; +using Azure.Messaging; + +namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro +{ + /// + /// A uses the to + /// encode and decode Avro payloads. + /// + public class SchemaRegistryAvroEncoder + { + private readonly SchemaRegistryClient _client; + private readonly string _groupName; + private readonly SchemaRegistryAvroObjectEncoderOptions _options; + private const string AvroMimeType = "avro/binary"; + + /// + /// Initializes new instance of . + /// + public SchemaRegistryAvroEncoder(SchemaRegistryClient client, string groupName, SchemaRegistryAvroObjectEncoderOptions options = null) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _groupName = groupName ?? throw new ArgumentNullException(nameof(groupName)); + _options = options; + } + + // TODO support backcompat for first beta + // private static readonly byte[] EmptyRecordFormatIndicator = { 0, 0, 0, 0 }; + // private const int RecordFormatIndicatorLength = 4; + // private const int SchemaIdLength = 32; + // private const int PayloadStartPosition = RecordFormatIndicatorLength + SchemaIdLength; + private readonly ConcurrentDictionary _idToSchemaMap = new(); + private readonly ConcurrentDictionary _schemaToIdMap = new(); + + private enum SupportedType + { + SpecificRecord, + GenericRecord + } + + private static SupportedType GetSupportedTypeOrThrow(Type type) + { + if (typeof(ISpecificRecord).IsAssignableFrom(type)) + { + return SupportedType.SpecificRecord; + } + + if (typeof(GenericRecord).IsAssignableFrom(type)) + { + return SupportedType.GenericRecord; + } + + throw new ArgumentException($"Type {type.Name} is not supported for serialization operations."); + } + + private async Task GetSchemaIdAsync(Schema schema, bool async, CancellationToken cancellationToken) + { + if (_schemaToIdMap.TryGetValue(schema, out string schemaId)) + { + return schemaId; + } + + SchemaProperties schemaProperties; + if (async) + { + schemaProperties = _options.AutoRegisterSchemas + ? (await _client + .RegisterSchemaAsync(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) + .ConfigureAwait(false)).Value + : await _client + .GetSchemaPropertiesAsync(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) + .ConfigureAwait(false); + } + else + { + schemaProperties = _options.AutoRegisterSchemas + ? _client.RegisterSchema(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) + : _client.GetSchemaProperties(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken); + } + + string id = schemaProperties.Id; + + _schemaToIdMap.TryAdd(schema, id); + _idToSchemaMap.TryAdd(id, schema); + return id; + } + + private static DatumWriter GetWriterAndSchema(object value, SupportedType supportedType, out Schema schema) + { + switch (supportedType) + { + case SupportedType.SpecificRecord: + schema = ((ISpecificRecord)value).Schema; + return new SpecificDatumWriter(schema); + case SupportedType.GenericRecord: + schema = ((GenericRecord)value).Schema; + return new GenericDatumWriter(schema); + default: + throw new ArgumentException($"Invalid supported type value: {supportedType}"); + } + } + + internal (string SchemaId, BinaryData Data) Encode(object value, Type inputType, CancellationToken cancellationToken) => + EncodeInternalAsync(value, inputType, false, cancellationToken).EnsureCompleted(); + + internal async ValueTask<(string SchemaId, BinaryData Data)> EncodeAsync(object value, Type inputType, CancellationToken cancellationToken) => + await EncodeInternalAsync(value, inputType, true, cancellationToken).ConfigureAwait(false); + + private async ValueTask<(string SchemaId, BinaryData Data)> EncodeInternalAsync( + object value, + Type inputType, + bool async, + CancellationToken cancellationToken) + { + Argument.AssertNotNull(value, nameof(value)); + Argument.AssertNotNull(inputType, nameof(inputType)); + + var supportedType = GetSupportedTypeOrThrow(inputType); + var writer = GetWriterAndSchema(value, supportedType, out var schema); + + using Stream stream = new MemoryStream(); + var binaryEncoder = new BinaryEncoder(stream); + + writer.Write(value, binaryEncoder); + binaryEncoder.Flush(); + stream.Position = 0; + BinaryData data = BinaryData.FromStream(stream); + + if (async) + { + return (await GetSchemaIdAsync(schema, true, cancellationToken).ConfigureAwait(false), data); + } + else + { + return (GetSchemaIdAsync(schema, false, cancellationToken).EnsureCompleted(), data); + } + } + + private async Task GetSchemaByIdAsync(string schemaId, bool async, CancellationToken cancellationToken) + { + if (_idToSchemaMap.TryGetValue(schemaId, out Schema cachedSchema)) + { + return cachedSchema; + } + + string schemaDefinition; + if (async) + { + schemaDefinition = (await _client.GetSchemaAsync(schemaId, cancellationToken).ConfigureAwait(false)).Value.Definition; + } + else + { + schemaDefinition = _client.GetSchema(schemaId, cancellationToken).Value.Definition; + } + var schema = Schema.Parse(schemaDefinition); + _idToSchemaMap.TryAdd(schemaId, schema); + _schemaToIdMap.TryAdd(schema, schemaId); + return schema; + } + + private static DatumReader GetReader(Schema schema, SupportedType supportedType) + { + switch (supportedType) + { + case SupportedType.SpecificRecord: + return new SpecificDatumReader(schema, schema); + case SupportedType.GenericRecord: + return new GenericDatumReader(schema, schema); + default: + throw new ArgumentException($"Invalid supported type value: {supportedType}"); + } + } + + /// + /// Encodes the message data into Avro and stores it in . The + /// will be set to "avro/binary+schemaId" where schemaId is the ID of the schema used to encode the data. + /// + /// The data to serialize to Avro and encode into the message. + /// The type to use to serialize the data. + /// Optional func to create a derived instance of given the serialized Avro. + /// If not specified, it is assumed that the derived type has a public constructor accepting a instance. + /// An optional instance to signal the request to cancel the operation. + public T EncodeMessageData( + object data, + Type inputType = default, + Func messageFactory = default, + CancellationToken cancellationToken = default) where T : MessageWithMetadata + => EncodeMessageDataInternalAsync(data, inputType, messageFactory, false, cancellationToken).EnsureCompleted(); + + /// + /// Encodes the message data into Avro and stores it in . The + /// will be set to "avro/binary+schemaId" where schemaId is the ID of the schema used to encode the data. + /// + /// The data to serialize to Avro and encode into the message. + /// The type to use to serialize the data. + /// Optional func to create a derived instance of given the serialized Avro. + /// If not specified, it is assumed that the derived type has a public constructor accepting a instance. + /// An optional instance to signal the request to cancel the operation. + public async ValueTask EncodeMessageDataAsync( + object data, + Type inputType = default, + Func messageFactory = default, + CancellationToken cancellationToken = default) where T : MessageWithMetadata + => await EncodeMessageDataInternalAsync(data, inputType, messageFactory, true, cancellationToken).ConfigureAwait(false); + + private async ValueTask EncodeMessageDataInternalAsync( + object data, + Type inputType, + Func messageFactory, + bool async, + CancellationToken cancellationToken) where T : MessageWithMetadata + { + (string schemaId, BinaryData bd) = async + ? await EncodeAsync(data, inputType ?? data?.GetType(), cancellationToken).ConfigureAwait(false) + : Encode(data, inputType ?? data?.GetType(), cancellationToken); + + MessageWithMetadata message; + if (messageFactory == default) + { + message = (MessageWithMetadata)Activator.CreateInstance(typeof(T), bd); + } + else + { + message = messageFactory.Invoke(bd); + } + message.ContentType = $"{AvroMimeType}+{schemaId}"; + return (T) message; + } + + /// + /// Decodes the message data into the specified type using the schema information populated in . + /// + /// The message containing the data to decode. + /// The type to deserialize to. + /// An optional instance to signal the request to cancel the operation. + /// The deserialized data. + /// Thrown if the content type is not in the expected format. + /// Thrown if an attempt is made to decode non-Avro data. + public object DecodeMessageData( + MessageWithMetadata message, + Type returnType, + CancellationToken cancellationToken = default) + => DecodeMessageBodyInternalAsync(message.Data, message.ContentType, returnType, false, cancellationToken).EnsureCompleted(); + + /// + /// Decodes the message data into the specified type using the schema information populated in . + /// + /// The message containing the data to decode. + /// The type to deserialize to. + /// An optional instance to signal the request to cancel the operation. + /// The deserialized data. + /// Thrown if the content type is not in the expected format. + /// Thrown if an attempt is made to decode non-Avro data. + public async ValueTask DecodeMessageDataAsync( + MessageWithMetadata message, + Type returnType, + CancellationToken cancellationToken = default) + => await DecodeMessageBodyInternalAsync(message.Data, message.ContentType, returnType, true, cancellationToken).ConfigureAwait(false); + + private async ValueTask DecodeMessageBodyInternalAsync( + BinaryData data, + string contentType, + Type returnType, + bool async, + CancellationToken cancellationToken) + { + string[] contentTypeArray = contentType.Split('+'); + if (contentTypeArray.Length != 2) + { + throw new FormatException("Content type was not in the expected format of MIME type + schema ID"); + } + + if (contentTypeArray[0] != AvroMimeType) + { + throw new InvalidOperationException("An avro encoder may only be used on content that is of 'avro/binary' type"); + } + + if (async) + { + return await DecodeAsync(data, contentTypeArray[1], returnType, cancellationToken).ConfigureAwait(false); + } + else + { + return Decode(data, contentTypeArray[1], returnType, cancellationToken); + } + } + + internal object Decode(BinaryData data, string schemaId, Type returnType, CancellationToken cancellationToken) => + DecodeInternalAsync(data, schemaId, returnType, false, cancellationToken).EnsureCompleted(); + + internal async ValueTask DecodeAsync(BinaryData data, string schemaId, Type returnType, CancellationToken cancellationToken) => + await DecodeInternalAsync(data, schemaId, returnType, true, cancellationToken).ConfigureAwait(false); + + private async ValueTask DecodeInternalAsync( + BinaryData data, + string schemaId, + Type returnType, + bool async, + CancellationToken cancellationToken) + { + Argument.AssertNotNull(data, nameof(data)); + Argument.AssertNotNull(returnType, nameof(returnType)); + Argument.AssertNotNull(schemaId, nameof(schemaId)); + + SupportedType supportedType = GetSupportedTypeOrThrow(returnType); + + Schema schema; + if (async) + { + schema = await GetSchemaByIdAsync(schemaId, true, cancellationToken).ConfigureAwait(false); + } + else + { + schema = GetSchemaByIdAsync(schemaId, false, cancellationToken).EnsureCompleted(); + } + + var binaryDecoder = new BinaryDecoder(data.ToStream()); + DatumReader reader = GetReader(schema, supportedType); + return reader.Read(reuse: null, binaryDecoder); + } + } +} diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializerOptions.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectEncoderOptions.cs similarity index 82% rename from sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializerOptions.cs rename to sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectEncoderOptions.cs index 6942dcaadcbe7..f7f8475b2c9bb 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializerOptions.cs +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectEncoderOptions.cs @@ -4,9 +4,9 @@ namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro { /// - /// Options for . + /// Options for . /// - public class SchemaRegistryAvroObjectSerializerOptions + public class SchemaRegistryAvroObjectEncoderOptions { /// /// Gets or sets the automatic registration of schemas flag. diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializer.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializer.cs deleted file mode 100644 index c1eefdb2a1d0c..0000000000000 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/src/SchemaRegistryAvroObjectSerializer.cs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Avro; -using Avro.Generic; -using Avro.IO; -using Avro.Specific; -using Azure.Core; -using Azure.Core.Serialization; -using Azure.Data.SchemaRegistry; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Azure.Core.Pipeline; - -namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro -{ - /// - /// A implementation that uses for Avro serialization/deserialization. - /// - public class SchemaRegistryAvroObjectSerializer : ObjectSerializer - { - private readonly SchemaRegistryClient _client; - private readonly string _groupName; - private readonly SchemaRegistryAvroObjectSerializerOptions _options; - - /// - /// Initializes new instance of . - /// - public SchemaRegistryAvroObjectSerializer(SchemaRegistryClient client, string groupName, SchemaRegistryAvroObjectSerializerOptions options = null) - { - _client = client ?? throw new ArgumentNullException(nameof(client)); - _groupName = groupName ?? throw new ArgumentNullException(nameof(groupName)); - _options = options; - } - - private static readonly byte[] EmptyRecordFormatIndicator = { 0, 0, 0, 0 }; - private static readonly Encoding Utf8Encoding = new UTF8Encoding(false); - - private const int RecordFormatIndicatorLength = 4; - private const int SchemaIdLength = 32; - private const int PayloadStartPosition = RecordFormatIndicatorLength + SchemaIdLength; - private readonly ConcurrentDictionary _idToSchemaMap = new(); - private readonly ConcurrentDictionary _schemaToIdMap = new(); - - private enum SupportedType - { - SpecificRecord, - GenericRecord - } - - private static SupportedType GetSupportedTypeOrThrow(Type type) - { - if (typeof(ISpecificRecord).IsAssignableFrom(type)) - { - return SupportedType.SpecificRecord; - } - - if (typeof(GenericRecord).IsAssignableFrom(type)) - { - return SupportedType.GenericRecord; - } - - throw new ArgumentException($"Type {type.Name} is not supported for serialization operations."); - } - - private async Task GetSchemaIdAsync(Schema schema, bool async, CancellationToken cancellationToken) - { - if (_schemaToIdMap.TryGetValue(schema, out string schemaId)) - { - return schemaId; - } - - SchemaProperties schemaProperties; - if (async) - { - schemaProperties = _options.AutoRegisterSchemas - ? (await _client - .RegisterSchemaAsync(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) - .ConfigureAwait(false)).Value - : await _client - .GetSchemaPropertiesAsync(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) - .ConfigureAwait(false); - } - else - { - schemaProperties = _options.AutoRegisterSchemas - ? _client.RegisterSchema(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken) - : _client.GetSchemaProperties(_groupName, schema.Fullname, schema.ToString(), SchemaFormat.Avro, cancellationToken); - } - - string id = schemaProperties.Id; - - _schemaToIdMap.TryAdd(schema, id); - _idToSchemaMap.TryAdd(id, schema); - return id; - } - - private static DatumWriter GetWriterAndSchema(object value, SupportedType supportedType, out Schema schema) - { - switch (supportedType) - { - case SupportedType.SpecificRecord: - schema = ((ISpecificRecord)value).Schema; - return new SpecificDatumWriter(schema); - case SupportedType.GenericRecord: - schema = ((GenericRecord)value).Schema; - return new GenericDatumWriter(schema); - default: - throw new ArgumentException($"Invalid supported type value: {supportedType}"); - } - } - - /// - public override void Serialize(Stream stream, object value, Type inputType, CancellationToken cancellationToken) => - SerializeInternalAsync(stream, value, inputType, false, cancellationToken).EnsureCompleted(); - - /// - public override async ValueTask SerializeAsync(Stream stream, object value, Type inputType, CancellationToken cancellationToken) => - await SerializeInternalAsync(stream, value, inputType, true, cancellationToken).ConfigureAwait(false); - - private async ValueTask SerializeInternalAsync( - Stream stream, - object value, - Type inputType, - bool async, - CancellationToken cancellationToken) - { - Argument.AssertNotNull(stream, nameof(stream)); - Argument.AssertNotNull(value, nameof(value)); - Argument.AssertNotNull(inputType, nameof(inputType)); - - var supportedType = GetSupportedTypeOrThrow(inputType); - var writer = GetWriterAndSchema(value, supportedType, out var schema); - - string schemaId; - if (async) - { - schemaId = await GetSchemaIdAsync(schema, true, cancellationToken).ConfigureAwait(false); - } - else - { - schemaId = GetSchemaIdAsync(schema, false, cancellationToken).EnsureCompleted(); - } - - var binaryEncoder = new BinaryEncoder(stream); - - if (async) - { - await stream.WriteAsync(EmptyRecordFormatIndicator, 0, RecordFormatIndicatorLength, cancellationToken).ConfigureAwait(false); - await stream.WriteAsync(Utf8Encoding.GetBytes(schemaId), 0, SchemaIdLength, cancellationToken).ConfigureAwait(false); - } - else - { - stream.Write(EmptyRecordFormatIndicator, 0, RecordFormatIndicatorLength); - stream.Write(Utf8Encoding.GetBytes(schemaId), 0, SchemaIdLength); - } - - writer.Write(value, binaryEncoder); - binaryEncoder.Flush(); - } - - private async Task GetSchemaByIdAsync(string schemaId, bool async, CancellationToken cancellationToken) - { - if (_idToSchemaMap.TryGetValue(schemaId, out Schema cachedSchema)) - { - return cachedSchema; - } - - string schemaContent; - if (async) - { - schemaContent = (await _client.GetSchemaAsync(schemaId, cancellationToken).ConfigureAwait(false)).Value.Definition; - } - else - { - schemaContent = _client.GetSchema(schemaId, cancellationToken).Value.Definition; - } - var schema = Schema.Parse(schemaContent); - _idToSchemaMap.TryAdd(schemaId, schema); - _schemaToIdMap.TryAdd(schema, schemaId); - return schema; - } - - private static DatumReader GetReader(Schema schema, SupportedType supportedType) - { - switch (supportedType) - { - case SupportedType.SpecificRecord: - return new SpecificDatumReader(schema, schema); - case SupportedType.GenericRecord: - return new GenericDatumReader(schema, schema); - default: - throw new ArgumentException($"Invalid supported type value: {supportedType}"); - } - } - - private static ReadOnlyMemory CopyToReadOnlyMemory(Stream stream) - { - using var tempMemoryStream = new MemoryStream(); - stream.CopyTo(tempMemoryStream); - return new ReadOnlyMemory(tempMemoryStream.ToArray()); - } - - private static void ValidateRecordFormatIdentifier(ReadOnlyMemory message) - { - var recordFormatIdentifier = message.Slice(0, RecordFormatIndicatorLength).ToArray(); - if (!recordFormatIdentifier.SequenceEqual(EmptyRecordFormatIndicator)) - { - throw new InvalidDataContractException( - $"The record format identifier ({recordFormatIdentifier[0]:X} {recordFormatIdentifier[1]:X} {recordFormatIdentifier[2]:X} {recordFormatIdentifier[3]:X}) for the message is invalid."); - } - } - - /// - public override object Deserialize(Stream stream, Type returnType, CancellationToken cancellationToken) => - DeserializeInternalAsync(stream, returnType, false, cancellationToken).EnsureCompleted(); - - /// - public override async ValueTask DeserializeAsync(Stream stream, Type returnType, CancellationToken cancellationToken) => - await DeserializeInternalAsync(stream, returnType, true, cancellationToken).ConfigureAwait(false); - - private async ValueTask DeserializeInternalAsync( - Stream stream, - Type returnType, - bool async, - CancellationToken cancellationToken) - { - Argument.AssertNotNull(stream, nameof(stream)); - Argument.AssertNotNull(returnType, nameof(returnType)); - - SupportedType supportedType = GetSupportedTypeOrThrow(returnType); - ReadOnlyMemory message = CopyToReadOnlyMemory(stream); - ValidateRecordFormatIdentifier(message); - byte[] schemaIdBytes = message.Slice(RecordFormatIndicatorLength, SchemaIdLength).ToArray(); - string schemaId = Utf8Encoding.GetString(schemaIdBytes); - - Schema schema; - if (async) - { - schema = await GetSchemaByIdAsync(schemaId, true, cancellationToken).ConfigureAwait(false); - } - else - { - schema = GetSchemaByIdAsync(schemaId, false, cancellationToken).EnsureCompleted(); - } - - using var valueStream = new MemoryStream(message.Slice(PayloadStartPosition, message.Length - PayloadStartPosition).ToArray()); - - var binaryDecoder = new BinaryDecoder(valueStream); - DatumReader reader = GetReader(schema, supportedType); - return reader.Read(reuse: null, binaryDecoder); - } - } -} diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests.csproj b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests.csproj index e2008bcd1a602..82ec8c83964f5 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests.csproj +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests.csproj @@ -20,6 +20,9 @@ + + + diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Samples/Sample01_ReadmeSnippets.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Samples/Sample01_ReadmeSnippets.cs index e2fcece238f5b..982a7b9fa90dc 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Samples/Sample01_ReadmeSnippets.cs +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/Samples/Sample01_ReadmeSnippets.cs @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using Azure.Core.TestFramework; using Azure.Data.SchemaRegistry; using Azure.Identity; using NUnit.Framework; using System.IO; using System.Threading; +using Azure.Messaging.EventHubs; using TestSchema; namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests.Samples @@ -16,7 +18,6 @@ public class Sample01_ReadmeSnippets : SamplesBase 0); - _memoryStreamBytes = memoryStream.ToArray(); - } - - [Test] - [Order(2)] - public void Deserialize() - { - using var memoryStream = new MemoryStream(_memoryStreamBytes); - string groupName = TestEnvironment.SchemaRegistryGroup; - - #region Snippet:SchemaRegistryAvroDeserialize - var serializer = new SchemaRegistryAvroObjectSerializer(schemaRegistryClient, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); - memoryStream.Position = 0; - Employee employee = (Employee)serializer.Deserialize(memoryStream, typeof(Employee), CancellationToken.None); - #endregion - - Assert.AreEqual(42, employee.Age); - Assert.AreEqual("John Doe", employee.Name); - } } } diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SchemaRegistryAvroObjectSerializerLiveTests.cs b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SchemaRegistryAvroObjectSerializerLiveTests.cs index 062cafeaf265b..52aa5d543b032 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SchemaRegistryAvroObjectSerializerLiveTests.cs +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SchemaRegistryAvroObjectSerializerLiveTests.cs @@ -10,6 +10,9 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using Azure.Messaging; +using Azure.Messaging.EventHubs; +using Azure.Messaging.ServiceBus; using TestSchema; namespace Microsoft.Azure.Data.SchemaRegistry.ApacheAvro.Tests @@ -35,12 +38,10 @@ public async Task CanSerializeAndDeserialize() var groupName = TestEnvironment.SchemaRegistryGroup; var employee = new Employee { Age = 42, Name = "Caketown" }; - using var memoryStream = new MemoryStream(); - var serializer = new SchemaRegistryAvroObjectSerializer(client, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); - await serializer.SerializeAsync(memoryStream, employee, typeof(Employee), CancellationToken.None); + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions() { AutoRegisterSchemas = true }); + (string schemaId, BinaryData data) = await encoder.EncodeAsync(employee, typeof(Employee), CancellationToken.None); - memoryStream.Position = 0; - var deserializedObject = await serializer.DeserializeAsync(memoryStream, typeof(Employee), CancellationToken.None); + var deserializedObject = await encoder.DecodeAsync(data, schemaId, typeof(Employee), CancellationToken.None); var readEmployee = deserializedObject as Employee; Assert.IsNotNull(readEmployee); Assert.AreEqual("Caketown", readEmployee.Name); @@ -56,12 +57,10 @@ public async Task CanSerializeAndDeserializeGenericRecord() record.Add("Name", "Caketown"); record.Add("Age", 42); - using var memoryStream = new MemoryStream(); - var serializer = new SchemaRegistryAvroObjectSerializer(client, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); - await serializer.SerializeAsync(memoryStream, record, typeof(GenericRecord), CancellationToken.None); + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + (string schemaId, BinaryData data) = await encoder.EncodeAsync(record, typeof(GenericRecord), CancellationToken.None); - memoryStream.Position = 0; - var deserializedObject = await serializer.DeserializeAsync(memoryStream, typeof(GenericRecord), CancellationToken.None); + var deserializedObject = await encoder.DecodeAsync(data, schemaId, typeof(GenericRecord), CancellationToken.None); var readRecord = deserializedObject as GenericRecord; Assert.IsNotNull(readRecord); Assert.AreEqual("Caketown", readRecord.GetValue(0)); @@ -75,9 +74,8 @@ public async Task CannotSerializeUnsupportedType() var groupName = TestEnvironment.SchemaRegistryGroup; var timeZoneInfo = TimeZoneInfo.Utc; - using var memoryStream = new MemoryStream(); - var serializer = new SchemaRegistryAvroObjectSerializer(client, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); - Assert.ThrowsAsync(async () => await serializer.SerializeAsync(memoryStream, timeZoneInfo, typeof(TimeZoneInfo), CancellationToken.None)); + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + Assert.ThrowsAsync(async () => await encoder.EncodeAsync(timeZoneInfo, typeof(TimeZoneInfo), CancellationToken.None)); await Task.CompletedTask; } @@ -87,10 +85,165 @@ public async Task CannotDeserializeUnsupportedType() var client = CreateClient(); var groupName = TestEnvironment.SchemaRegistryGroup; - using var memoryStream = new MemoryStream(); - var serializer = new SchemaRegistryAvroObjectSerializer(client, groupName, new SchemaRegistryAvroObjectSerializerOptions { AutoRegisterSchemas = true }); - Assert.ThrowsAsync(async () => await serializer.DeserializeAsync(memoryStream, typeof(TimeZoneInfo), CancellationToken.None)); + var serializer = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + Assert.ThrowsAsync(async () => await serializer.DecodeAsync(new BinaryData(Array.Empty()), "fakeSchemaId", typeof(TimeZoneInfo), CancellationToken.None)); await Task.CompletedTask; } + + [RecordedTest] + public async Task CannotDeserializeWithNullSchemaId() + { + var client = CreateClient(); + var groupName = TestEnvironment.SchemaRegistryGroup; + + var serializer = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + Assert.ThrowsAsync(async () => await serializer.DecodeAsync(new BinaryData(Array.Empty()), null, typeof(TimeZoneInfo), CancellationToken.None)); + await Task.CompletedTask; + } + + [RecordedTest] + public async Task CanUseEncoderWithEventData() + { + var client = CreateClient(); + var groupName = TestEnvironment.SchemaRegistryGroup; + + #region Snippet:SchemaRegistryAvroEncodeEventData + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + + var employee = new Employee { Age = 42, Name = "Caketown" }; + EventData eventData = await encoder.EncodeMessageDataAsync(employee); + +#if SNIPPET + // the schema Id will be included as a parameter of the content type + Console.WriteLine(eventData.ContentType); + + // the serialized Avro data will be stored in the EventBody + Console.WriteLine(eventData.EventBody); +#endif + #endregion + + Assert.IsFalse(((MessageWithMetadata) eventData).IsReadOnly); + string[] contentType = eventData.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + #region Snippet:SchemaRegistryAvroDecodeEventData + Employee deserialized = (Employee)await encoder.DecodeMessageDataAsync(eventData, typeof(Employee)); +#if SNIPPET + Console.WriteLine(deserialized.Age); + Console.WriteLine(deserialized.Name); +#endif + #endregion + + // decoding should not alter the message + contentType = eventData.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + // verify the payload was decoded correctly + Assert.IsNotNull(deserialized); + Assert.AreEqual("Caketown", deserialized.Name); + Assert.AreEqual(42, deserialized.Age); + } + + [RecordedTest] + public async Task CanUseEncoderWithEventDataUsingFunc() + { + var client = CreateClient(); + var groupName = TestEnvironment.SchemaRegistryGroup; + + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + + var employee = new Employee { Age = 42, Name = "Caketown" }; + EventData message = await encoder.EncodeMessageDataAsync(employee, messageFactory: bd => new EventData(bd)); + + string[] contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + Employee deserialized = (Employee)await encoder.DecodeMessageDataAsync(message, typeof(Employee)); + + // decoding should not alter the message + contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + // verify the payload was decoded correctly + Assert.IsNotNull(deserialized); + Assert.AreEqual("Caketown", deserialized.Name); + Assert.AreEqual(42, deserialized.Age); + } + + [RecordedTest] + public async Task CanUseEncoderWithServiceBusMessage() + { + var client = CreateClient(); + var groupName = TestEnvironment.SchemaRegistryGroup; + + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + + var employee = new Employee { Age = 42, Name = "Caketown" }; + ServiceBusMessage message = await encoder.EncodeMessageDataAsync(employee); + Assert.IsFalse(((MessageWithMetadata) message).IsReadOnly); + + string[] contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + ServiceBusReceivedMessage receivedMessage = + ServiceBusModelFactory.ServiceBusReceivedMessage(body: message.Body, contentType: message.ContentType); + Assert.IsTrue(((MessageWithMetadata) receivedMessage).IsReadOnly); + + Employee deserialized = (Employee)await encoder.DecodeMessageDataAsync(receivedMessage, typeof(Employee)); + + // decoding should not alter the message + contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + // verify the payload was decoded correctly + Assert.IsNotNull(deserialized); + Assert.AreEqual("Caketown", deserialized.Name); + Assert.AreEqual(42, deserialized.Age); + } + + [RecordedTest] + public async Task CanUseEncoderWithServiceBusMessageUsingFunc() + { + var client = CreateClient(); + var groupName = TestEnvironment.SchemaRegistryGroup; + + var encoder = new SchemaRegistryAvroEncoder(client, groupName, new SchemaRegistryAvroObjectEncoderOptions { AutoRegisterSchemas = true }); + + var employee = new Employee { Age = 42, Name = "Caketown" }; + ServiceBusMessage message = await encoder.EncodeMessageDataAsync(employee, messageFactory: bd => new ServiceBusMessage(bd)); + + string[] contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + ServiceBusReceivedMessage receivedMessage = + ServiceBusModelFactory.ServiceBusReceivedMessage(body: message.Body, contentType: message.ContentType); + + Employee deserialized = (Employee)await encoder.DecodeMessageDataAsync(receivedMessage, typeof(Employee)); + + // decoding should not alter the message + contentType = message.ContentType.Split('+'); + Assert.AreEqual(2, contentType.Length); + Assert.AreEqual("avro/binary", contentType[0]); + Assert.IsNotEmpty(contentType[1]); + + // verify the payload was decoded correctly + Assert.IsNotNull(deserialized); + Assert.AreEqual("Caketown", deserialized.Name); + Assert.AreEqual(42, deserialized.Age); + } } } diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventData.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventData.json index e69de29bb2d1d..17c58283f998c 100644 --- a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventData.json +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventData.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": "azsdk-net-Data.SchemaRegistry/1.0.0-alpha.20211105.1 (.NET Framework 4.8.4420.0; Microsoft Windows 10.0.22000 )", + "x-ms-client-request-id": "81973e0e587452774435f602cb03e604", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Sat, 06 Nov 2021 00:59:13 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "45d496c4535a44e293de07484672cbe6", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/45d496c4535a44e293de07484672cbe6?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "349656055", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataAsync.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataAsync.json new file mode 100644 index 0000000000000..934f9bb2b049e --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataAsync.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": "azsdk-net-Data.SchemaRegistry/1.0.0-alpha.20211105.1 (.NET Framework 4.8.4420.0; Microsoft Windows 10.0.22000 )", + "x-ms-client-request-id": "bd6754185f077a0b0da99407ffea0671", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Sat, 06 Nov 2021 00:59:15 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "45d496c4535a44e293de07484672cbe6", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/45d496c4535a44e293de07484672cbe6?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "533134342", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFunc.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFunc.json new file mode 100644 index 0000000000000..2f6c634071eaf --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFunc.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1 (.NET Framework 4.8.4420.0; Microsoft Windows 10.0.22000 )", + "x-ms-client-request-id": "63f26cbe7b57753aee9f2b29a06d502a", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 00:23:11 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "822817808", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFuncAsync.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFuncAsync.json new file mode 100644 index 0000000000000..80026390d3a9f --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithEventDataUsingFuncAsync.json @@ -0,0 +1,52 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1 (.NET Framework 4.8.4420.0; Microsoft Windows 10.0.22000 )", + "x-ms-client-request-id": "0d15d1dcbbffe08f0834b0a18fffdcd6", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 00:23:57 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "1093756092", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessage.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessage.json new file mode 100644 index 0000000000000..b0a09db125f33 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessage.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": [ + "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1", + "(.NET Core 3.1.21; Microsoft Windows 10.0.22000)" + ], + "x-ms-client-request-id": "1b2a58488352ab38675dde76f1cb5061", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 01:12:57 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "1905931033", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageAsync.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageAsync.json new file mode 100644 index 0000000000000..9a4586b8e85fc --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageAsync.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": [ + "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1", + "(.NET Core 3.1.21; Microsoft Windows 10.0.22000)" + ], + "x-ms-client-request-id": "a13002d44b162dc0ab8cdaee93264afb", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 01:12:58 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "1063570874", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFunc.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFunc.json new file mode 100644 index 0000000000000..c7d5be53555c1 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFunc.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": [ + "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1", + "(.NET Core 3.1.21; Microsoft Windows 10.0.22000)" + ], + "x-ms-client-request-id": "c7798168c10dc0828a8c6bb785e5b4a1", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 01:12:58 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "462918451", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFuncAsync.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFuncAsync.json new file mode 100644 index 0000000000000..2075b990a7ae6 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CanUseEncoderWithServiceBusMessageUsingFuncAsync.json @@ -0,0 +1,55 @@ +{ + "Entries": [ + { + "RequestUri": "https://jolovschemaregistry.servicebus.windows.net/$schemaGroups/azsdk_net_test_group/schemas/TestSchema.Employee?api-version=2021-10", + "RequestMethod": "PUT", + "RequestHeaders": { + "Accept": "application/json", + "Authorization": "Sanitized", + "Content-Length": "131", + "Content-Type": "application/json; serialization=Avro", + "User-Agent": [ + "azsdk-net-Data.SchemaRegistry/1.1.0-alpha.20211129.1", + "(.NET Core 3.1.21; Microsoft Windows 10.0.22000)" + ], + "x-ms-client-request-id": "24845f4b598e9b74e5e36e9a077bc01f", + "x-ms-return-client-request-id": "true" + }, + "RequestBody": { + "type": "record", + "name": "Employee", + "namespace": "TestSchema", + "fields": [ + { + "name": "Name", + "type": "string" + }, + { + "name": "Age", + "type": "int" + } + ] + }, + "StatusCode": 204, + "ResponseHeaders": { + "Content-Length": "0", + "Date": "Tue, 30 Nov 2021 01:13:00 GMT", + "Location": "https://jolovschemaregistry.servicebus.windows.net/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions/1?api-version=2021-10", + "Schema-Group-Name": "azsdk_net_test_group", + "Schema-Id": "a2a03a64e7b6441fa09abc8891469504", + "Schema-Id-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/$schemas/a2a03a64e7b6441fa09abc8891469504?api-version=2021-10", + "Schema-Name": "TestSchema.Employee", + "Schema-Version": "1", + "Schema-Versions-Location": "https://jolovschemaregistry.servicebus.windows.net:443/$schemagroups/azsdk_net_test_group/schemas/TestSchema.Employee/versions?api-version=2021-10", + "Server": "Microsoft-HTTPAPI/2.0", + "Strict-Transport-Security": "max-age=31536000" + }, + "ResponseBody": [] + } + ], + "Variables": { + "RandomSeed": "2078853961", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaId.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaId.json new file mode 100644 index 0000000000000..aa134b54a5b17 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaId.json @@ -0,0 +1,8 @@ +{ + "Entries": [], + "Variables": { + "RandomSeed": "699710809", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaIdAsync.json b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaIdAsync.json new file mode 100644 index 0000000000000..738c29458ba98 --- /dev/null +++ b/sdk/schemaregistry/Microsoft.Azure.Data.SchemaRegistry.ApacheAvro/tests/SessionRecords/SchemaRegistryAvroObjectSerializerLiveTests/CannotDeserializeWithNullSchemaIdAsync.json @@ -0,0 +1,8 @@ +{ + "Entries": [], + "Variables": { + "RandomSeed": "41747669", + "SCHEMAREGISTRY_ENDPOINT": "jolovschemaregistry.servicebus.windows.net", + "SCHEMAREGISTRY_GROUP": "azsdk_net_test_group" + } +} \ No newline at end of file diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/Azure.Messaging.ServiceBus.sln b/sdk/servicebus/Azure.Messaging.ServiceBus/Azure.Messaging.ServiceBus.sln index 915aafa294258..0fed414342f8f 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/Azure.Messaging.ServiceBus.sln +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/Azure.Messaging.ServiceBus.sln @@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Messaging.ServiceBus. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Test.Perf", "..\..\..\common\Perf\Azure.Test.Perf\Azure.Test.Perf.csproj", "{A6A7CEAE-C1F9-4B9B-A19F-4AA38ADF5209}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Core.Experimental", "..\..\core\Azure.Core.Experimental\src\Azure.Core.Experimental.csproj", "{B42578D2-76CD-4F4E-9999-CDB466477488}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,6 +82,10 @@ Global {A6A7CEAE-C1F9-4B9B-A19F-4AA38ADF5209}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6A7CEAE-C1F9-4B9B-A19F-4AA38ADF5209}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6A7CEAE-C1F9-4B9B-A19F-4AA38ADF5209}.Release|Any CPU.Build.0 = Release|Any CPU + {B42578D2-76CD-4F4E-9999-CDB466477488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B42578D2-76CD-4F4E-9999-CDB466477488}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B42578D2-76CD-4F4E-9999-CDB466477488}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B42578D2-76CD-4F4E-9999-CDB466477488}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs index 286d05358816c..02be6add985fd 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs @@ -152,7 +152,7 @@ public enum ServiceBusFailureReason SessionLockLost = 11, MessagingEntityAlreadyExists = 12, } - public partial class ServiceBusMessage + public partial class ServiceBusMessage : Azure.Messaging.MessageWithMetadata { public ServiceBusMessage() { } public ServiceBusMessage(Azure.Messaging.ServiceBus.ServiceBusReceivedMessage receivedMessage) { } @@ -161,8 +161,12 @@ public ServiceBusMessage(System.ReadOnlyMemory body) { } public ServiceBusMessage(string body) { } public System.Collections.Generic.IDictionary ApplicationProperties { get { throw null; } } public System.BinaryData Body { get { throw null; } set { } } - public string ContentType { get { throw null; } set { } } + public override string ContentType { get { throw null; } set { } } public string CorrelationId { get { throw null; } set { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.BinaryData Data { get { throw null; } set { } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool IsReadOnly { get { throw null; } } public string MessageId { get { throw null; } set { } } public string PartitionKey { get { throw null; } set { } } public string ReplyTo { get { throw null; } set { } } @@ -255,13 +259,15 @@ public ServiceBusProcessorOptions() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } } - public partial class ServiceBusReceivedMessage + public partial class ServiceBusReceivedMessage : Azure.Messaging.MessageWithMetadata { internal ServiceBusReceivedMessage() { } public System.Collections.Generic.IReadOnlyDictionary ApplicationProperties { get { throw null; } } public System.BinaryData Body { get { throw null; } } - public string ContentType { get { throw null; } } + public override string ContentType { get { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] set { } } public string CorrelationId { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override System.BinaryData Data { get { throw null; } set { } } public string DeadLetterErrorDescription { get { throw null; } } public string DeadLetterReason { get { throw null; } } public string DeadLetterSource { get { throw null; } } @@ -269,6 +275,8 @@ internal ServiceBusReceivedMessage() { } public long EnqueuedSequenceNumber { get { throw null; } } public System.DateTimeOffset EnqueuedTime { get { throw null; } } public System.DateTimeOffset ExpiresAt { get { throw null; } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool IsReadOnly { get { throw null; } } public System.DateTimeOffset LockedUntil { get { throw null; } } public string LockToken { get { throw null; } } public string MessageId { get { throw null; } } diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Azure.Messaging.ServiceBus.csproj b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Azure.Messaging.ServiceBus.csproj index e5d4defdb7309..091ab2602d1d6 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Azure.Messaging.ServiceBus.csproj +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Azure.Messaging.ServiceBus.csproj @@ -19,7 +19,11 @@ - + @@ -57,5 +61,8 @@ Resources.Designer.cs + + + diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusMessage.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusMessage.cs index 8cb14cadabbba..ea0323c37bce7 100755 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusMessage.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusMessage.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.Text; using Azure.Core; @@ -19,7 +20,7 @@ namespace Azure.Messaging.ServiceBus /// The message structure is discussed in detail in the /// product documentation. /// - public class ServiceBusMessage + public class ServiceBusMessage : MessageWithMetadata { /// /// Creates a new message. @@ -157,6 +158,17 @@ public BinaryData Body } } + /// + /// Hidden property that shadows the property. This is added + /// in order to inherit from . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override BinaryData Data + { + get => Body; + set => Body = value; + } + /// /// Gets or sets the MessageId to identify the message. /// @@ -349,7 +361,7 @@ public string To /// Optionally describes the payload of the message, with a descriptor following the format of /// RFC2045, Section 5, for example "application/json". /// - public string ContentType + public override string ContentType { get { @@ -361,6 +373,13 @@ public string ContentType } } + /// + /// Hidden property that indicates that the is not read-only. This is part of + /// the abstraction. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool IsReadOnly => false; + /// Gets or sets the address of an entity to send replies to. /// The reply entity address. /// @@ -440,4 +459,4 @@ public override string ToString() return string.Format(CultureInfo.CurrentCulture, "{{MessageId:{0}}}", MessageId); } } -} +} \ No newline at end of file diff --git a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusReceivedMessage.cs b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusReceivedMessage.cs index ec663cdf6a187..00f0809a64975 100644 --- a/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusReceivedMessage.cs +++ b/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusReceivedMessage.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Globalization; using Azure.Core; using Azure.Core.Amqp; @@ -19,7 +20,7 @@ namespace Azure.Messaging.ServiceBus /// The message structure is discussed in detail in the /// product documentation. /// - public class ServiceBusReceivedMessage + public class ServiceBusReceivedMessage : MessageWithMetadata { /// /// Creates a new message from the specified payload. @@ -67,6 +68,17 @@ internal ServiceBusReceivedMessage(): this(body: default) /// public BinaryData Body => AmqpMessage.GetBody(); + /// + /// Hidden property that shadows the property. This is added + /// in order to inherit from . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override BinaryData Data + { + get => Body; + set => throw new NotImplementedException("Data cannot be set on a ServiceBusReceivedMessage"); + } + /// /// Gets the MessageId to identify the message. /// @@ -166,7 +178,19 @@ internal ServiceBusReceivedMessage(): this(body: default) /// Optionally describes the payload of the message, with a descriptor following the format of /// RFC2045, Section 5, for example "application/json". /// - public string ContentType => AmqpMessage.Properties.ContentType; + public override string ContentType + { + get => AmqpMessage.Properties.ContentType; + [EditorBrowsable(EditorBrowsableState.Never)] + set => throw new NotImplementedException("Content type cannot be set on a ServiceBusReceivedMessage"); + } + + /// + /// Hidden property that indicates that the is read-only. This is part of + /// the abstraction. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool IsReadOnly => true; /// Gets the address of an entity to send replies to. /// The reply entity address.