From 473753f0d1c8a9c1cb4b11c19fad579b4786e849 Mon Sep 17 00:00:00 2001 From: Kerri Lee <52222716+kerri-lee@users.noreply.github.com> Date: Mon, 10 Aug 2020 13:27:29 -0500 Subject: [PATCH] Design and Implement Consumer for EventGrid (#14048) * Added new EventGridConsumer and EventGridConsumerOptions classes * Added unit tests for consuming events * Renamed publish events methods to SendEvents and SendEventsAsync (with overloads for EventGridEvent, CloudEvent, and object) --- ...zure.Messaging.EventGrid.netstandard2.0.cs | 80 +- .../src/Customization/CloudEvent.cs | 5 +- .../src/Customization/CloudEventInternal.cs | 2 + .../Customization/CustomModelSerializer.cs | 49 + .../src/Customization/EventGridConsumer.cs | 295 ++++ .../Customization/EventGridConsumerOptions.cs | 27 + .../src/Customization/EventGridEvent.cs | 5 +- .../Customization/EventGridEventInternal.cs | 3 +- .../Customization/EventGridPublisherClient.cs | 77 +- .../EventGridPublisherClientOptions.cs | 17 +- .../src/Customization/EventGridSerializer.cs | 34 - .../src/Customization/EventTypes.cs | 118 ++ .../Customization/ServiceVersionExtensions.cs | 20 + .../Customization/SystemEventTypeMappings.cs | 119 ++ .../CloudEventInternal.Serialization.cs | 8 +- .../Generated/Models/CloudEventInternal.cs | 5 +- .../EventGridEventInternal.Serialization.cs | 6 +- .../Models/EventGridEventInternal.cs | 13 +- .../tests/ConsumeEventTests.cs | 1418 +++++++++++++++++ .../tests/ContosoItemReceivedEventData.cs | 12 + .../tests/ContosoItemSentEventData.cs | 10 + ...ridClientTests.cs => DroneShippingInfo.cs} | 10 +- .../tests/EventGridClientLiveTests.cs | 24 +- .../tests/RocketShippingInfo.cs | 11 + .../tests/ShippingInfo.cs | 13 + .../tests/TestEvent.cs | 1 - 26 files changed, 2215 insertions(+), 167 deletions(-) create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CustomModelSerializer.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumer.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumerOptions.cs delete mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridSerializer.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventTypes.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/ServiceVersionExtensions.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/SystemEventTypeMappings.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/tests/ConsumeEventTests.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemReceivedEventData.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemSentEventData.cs rename sdk/eventgrid/Azure.Messaging.EventGrid/tests/{EventGridClientTests.cs => DroneShippingInfo.cs} (54%) create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/tests/RocketShippingInfo.cs create mode 100644 sdk/eventgrid/Azure.Messaging.EventGrid/tests/ShippingInfo.cs diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/api/Azure.Messaging.EventGrid.netstandard2.0.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/api/Azure.Messaging.EventGrid.netstandard2.0.cs index 46e6d6cb0009..bd7aca956dbe 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/api/Azure.Messaging.EventGrid.netstandard2.0.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/api/Azure.Messaging.EventGrid.netstandard2.0.cs @@ -1,37 +1,4 @@ namespace Azure.Messaging.EventGrid -{ - public partial class EventGridPublisherClient - { - protected EventGridPublisherClient() { } - public EventGridPublisherClient(System.Uri endpoint, Azure.AzureKeyCredential credential) { } - public EventGridPublisherClient(System.Uri endpoint, Azure.AzureKeyCredential credential, Azure.Messaging.EventGrid.EventGridPublisherClientOptions options) { } - public EventGridPublisherClient(System.Uri endpoint, Azure.Messaging.EventGrid.EventGridSharedAccessSignatureCredential credential) { } - public EventGridPublisherClient(System.Uri endpoint, Azure.Messaging.EventGrid.EventGridSharedAccessSignatureCredential credential, Azure.Messaging.EventGrid.EventGridPublisherClientOptions options) { } - public static string BuildSharedAccessSignature(System.Uri endpoint, System.DateTimeOffset expirationUtc, Azure.AzureKeyCredential key, string apiVersion = "2018-01-01") { throw null; } - public virtual Azure.Response PublishCloudEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task PublishCloudEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual Azure.Response PublishCustomEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task PublishCustomEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual Azure.Response PublishEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task PublishEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - } - public partial class EventGridPublisherClientOptions : Azure.Core.ClientOptions - { - public EventGridPublisherClientOptions(Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion version = Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion.V2018_01_01) { } - public Azure.Core.Serialization.ObjectSerializer Serializer { get { throw null; } set { } } - public enum ServiceVersion - { - V2018_01_01 = 1, - } - } - public partial class EventGridSharedAccessSignatureCredential - { - public EventGridSharedAccessSignatureCredential(string signature) { } - public string Signature { get { throw null; } } - public void Update(string signature) { } - } -} -namespace Azure.Messaging.EventGrid.Models { public partial class CloudEvent { @@ -42,11 +9,25 @@ public CloudEvent(string source, string type) { } public System.Collections.Generic.Dictionary ExtensionAttributes { get { throw null; } } public string Id { get { throw null; } set { } } public string Source { get { throw null; } set { } } - public string SpecVersion { get { throw null; } set { } } public string Subject { get { throw null; } set { } } public System.DateTimeOffset? Time { get { throw null; } set { } } public string Type { get { throw null; } set { } } } + public partial class EventGridConsumer + { + public EventGridConsumer() { } + public EventGridConsumer(Azure.Messaging.EventGrid.EventGridConsumerOptions options) { } + public virtual Azure.Messaging.EventGrid.CloudEvent[] DeserializeCloudEvents(string requestContent, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task DeserializeCloudEventsAsync(string requestContent, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Messaging.EventGrid.EventGridEvent[] DeserializeEventGridEvents(string requestContent, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task DeserializeEventGridEventsAsync(string requestContent, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public partial class EventGridConsumerOptions + { + public EventGridConsumerOptions() { } + public System.Collections.Generic.IDictionary CustomEventTypeMappings { get { throw null; } } + public Azure.Core.Serialization.ObjectSerializer DataSerializer { get { throw null; } set { } } + } public partial class EventGridEvent { public EventGridEvent(string subject, object data, string eventType, string dataVersion) { } @@ -55,10 +36,39 @@ public EventGridEvent(string subject, object data, string eventType, string data public System.DateTimeOffset EventTime { get { throw null; } set { } } public string EventType { get { throw null; } set { } } public string Id { get { throw null; } set { } } - public string MetadataVersion { get { throw null; } set { } } public string Subject { get { throw null; } set { } } public string Topic { get { throw null; } set { } } } + public partial class EventGridPublisherClient + { + protected EventGridPublisherClient() { } + public EventGridPublisherClient(System.Uri endpoint, Azure.AzureKeyCredential credential) { } + public EventGridPublisherClient(System.Uri endpoint, Azure.AzureKeyCredential credential, Azure.Messaging.EventGrid.EventGridPublisherClientOptions options) { } + public EventGridPublisherClient(System.Uri endpoint, Azure.Messaging.EventGrid.EventGridSharedAccessSignatureCredential credential) { } + public EventGridPublisherClient(System.Uri endpoint, Azure.Messaging.EventGrid.EventGridSharedAccessSignatureCredential credential, Azure.Messaging.EventGrid.EventGridPublisherClientOptions options) { } + public static string BuildSharedAccessSignature(System.Uri endpoint, System.DateTimeOffset expirationUtc, Azure.AzureKeyCredential key, Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion apiVersion = Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion.V2018_01_01) { throw null; } + public virtual Azure.Response SendEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response SendEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response SendEvents(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SendEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SendEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SendEventsAsync(System.Collections.Generic.IEnumerable events, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public partial class EventGridPublisherClientOptions : Azure.Core.ClientOptions + { + public EventGridPublisherClientOptions(Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion version = Azure.Messaging.EventGrid.EventGridPublisherClientOptions.ServiceVersion.V2018_01_01) { } + public Azure.Core.Serialization.ObjectSerializer DataSerializer { get { throw null; } set { } } + public enum ServiceVersion + { + V2018_01_01 = 1, + } + } + public partial class EventGridSharedAccessSignatureCredential + { + public EventGridSharedAccessSignatureCredential(string signature) { } + public string Signature { get { throw null; } } + public void Update(string signature) { } + } } namespace Azure.Messaging.EventGrid.SystemEvents { diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEvent.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEvent.cs index c663813d3fdd..4fe7a49fce08 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEvent.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEvent.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using Azure.Core; -namespace Azure.Messaging.EventGrid.Models +namespace Azure.Messaging.EventGrid { /// Properties of an event published to an Event Grid topic using the CloudEvent 1.0 Schema. public class CloudEvent @@ -39,9 +39,6 @@ public CloudEvent(string source, string type) /// The time (in UTC) the event was generated, in RFC3339 format. public DateTimeOffset? Time { get; set; } = DateTimeOffset.UtcNow; - /// The version of the CloudEvents specification which the event uses. - public string SpecVersion { get; set; } = "1.0"; - /// Identifies the schema that data adheres to. public string DataSchema { get; set; } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEventInternal.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEventInternal.cs index 0238f1be28c4..e5f994e1f908 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEventInternal.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CloudEventInternal.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Text.Json; using Azure.Core; namespace Azure.Messaging.EventGrid.Models @@ -11,5 +12,6 @@ namespace Azure.Messaging.EventGrid.Models [CodeGenModel("CloudEvent")] internal partial class CloudEventInternal { + public JsonElement? Data { get; set; } } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CustomModelSerializer.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CustomModelSerializer.cs new file mode 100644 index 000000000000..1a32ab7deeed --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/CustomModelSerializer.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Threading; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Messaging.EventGrid +{ + /// + /// UTF-8 JSON-serializable wrapper for objects such as custom schema events. + /// Takes a custom ObjectSerializer to use when writing the object as JSON text. + /// + internal class CustomModelSerializer : IUtf8JsonSerializable + { + public object _payload; + public CancellationToken _cancellationToken; + public ObjectSerializer _serializer; + + /// + /// Initializes an instance of the CustomModelSerializer class. + /// + /// + /// Object that can represent an event with a custom schema, or additional properties + /// added to the event envelope. + /// + /// + /// Custom ObjectSerializer to use when writing the object as JSON text. + /// + /// The cancellation token to use. + public CustomModelSerializer(object payload, ObjectSerializer serializer, CancellationToken cancellationToken) + { + _payload = payload; + _serializer = serializer; + _cancellationToken = cancellationToken; + } + public void Write(Utf8JsonWriter writer) + { + var stream = new MemoryStream(); + _serializer.Serialize(stream, _payload, _payload.GetType(), _cancellationToken); + stream.Seek(0, SeekOrigin.Begin); + JsonDocument.Parse(stream).WriteTo(writer); + } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumer.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumer.cs new file mode 100644 index 000000000000..dfe48de819c3 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumer.cs @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Core.Pipeline; +using Azure.Core.Serialization; +using Azure.Messaging.EventGrid.Models; + +namespace Azure.Messaging.EventGrid +{ + /// + /// Class used to decode events from the Event Grid service. + /// + public class EventGridConsumer + { + private readonly ObjectSerializer _dataSerializer; + private readonly IDictionary _customEventTypeMappings; + + /// + /// Initializes a new instance of the class. + /// + public EventGridConsumer() : + this(new EventGridConsumerOptions()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Options for configuring deserialization. + public EventGridConsumer(EventGridConsumerOptions options) + { + Argument.AssertNotNull(options, nameof(options)); + _dataSerializer = options.DataSerializer; + _customEventTypeMappings = new Dictionary(); + foreach (KeyValuePair kvp in options.CustomEventTypeMappings) + { + _customEventTypeMappings.Add(kvp); + } + } + + /// + /// Deserializes JSON encoded events and returns an array of events encoded in the EventGrid event schema. + /// + /// + /// The JSON encoded representation of either a single event or an array or events, encoded in the EventGrid event schema. + /// + /// The cancellation token to use. + /// A list of EventGrid Events. + public virtual EventGridEvent[] DeserializeEventGridEvents(string requestContent, CancellationToken cancellationToken = default) + => DeserializeEventGridEventsInternal(requestContent, false /*async*/, cancellationToken).EnsureCompleted(); + + /// + /// Deserializes JSON encoded events and returns an array of events encoded in the EventGrid event schema. + /// + /// + /// The JSON encoded representation of either a single event or an array or events, encoded in the EventGrid event schema. + /// + /// The cancellation token to use. + /// A list of EventGrid Events. + public virtual async Task DeserializeEventGridEventsAsync(string requestContent, CancellationToken cancellationToken = default) + => await DeserializeEventGridEventsInternal(requestContent, true /*async*/, cancellationToken).ConfigureAwait(false); + + private async Task DeserializeEventGridEventsInternal(string requestContent, bool async, CancellationToken cancellationToken = default) + { + List egInternalEvents = new List(); + List egEvents = new List(); + + // Deserialize raw JSON string into separate events, deserialize event envelope properties + JsonDocument requestDocument = await ParseJsonToDocument(requestContent, async, cancellationToken).ConfigureAwait(false); + foreach (JsonElement property in requestDocument.RootElement.EnumerateArray()) + { + egInternalEvents.Add(EventGridEventInternal.DeserializeEventGridEventInternal(property)); + } + + // Deserialize 'Data' property from JsonElement for each event + foreach (EventGridEventInternal egEventInternal in egInternalEvents) + { + JsonElement dataElement = egEventInternal.Data; + object egEventData = null; + + // Reserialize JsonElement to stream + MemoryStream dataStream = SerializePayloadToStream(dataElement, cancellationToken); + + // First, let's attempt to find the mapping for the event type in the custom event mapping. + if (_customEventTypeMappings.TryGetValue(egEventInternal.EventType, out Type typeOfEventData)) + { + if (!TryGetPrimitiveFromJsonElement(dataElement, out egEventData)) + { + if (async) + { + egEventData = await _dataSerializer.DeserializeAsync(dataStream, typeOfEventData, cancellationToken).ConfigureAwait(false); + } + else + { + egEventData = _dataSerializer.Deserialize(dataStream, typeOfEventData, cancellationToken); + } + } + } + // If a custom mapping doesn't exist, let's attempt to find the mapping for the deserialization function in the system event type mapping. + else if (SystemEventTypeMappings.SystemEventDeserializers.TryGetValue(egEventInternal.EventType, out Func systemDeserializationFunction)) + { + egEventData = systemDeserializationFunction(dataElement); + } + else + { + // If event data is not a primitive/string, return as BinaryData + if (!TryGetPrimitiveFromJsonElement(dataElement, out egEventData)) + { + egEventData = BinaryData.FromStream(dataStream); + } + } + + egEvents.Add(new EventGridEvent( + egEventInternal.Subject, + egEventData, + egEventInternal.EventType, + egEventInternal.DataVersion) + { + Id = egEventInternal.Id, + EventTime = egEventInternal.EventTime + }); + } + + return egEvents.ToArray(); + } + + /// + /// Deserializes JSON encoded events and returns an array of events encoded in the CloudEvent schema. + /// + /// + /// The JSON encoded representation of either a single event or an array or events, encoded in the CloudEvent schema. + /// + /// The cancellation token to use. + /// A list of CloudEvents. + public virtual CloudEvent[] DeserializeCloudEvents(string requestContent, CancellationToken cancellationToken = default) + => DeserializeCloudEventsInternal(requestContent, false /*async*/, cancellationToken).EnsureCompleted(); + + /// + /// Deserializes JSON encoded events and returns an array of events encoded in the CloudEvent schema. + /// + /// + /// The JSON encoded representation of either a single event or an array or events, encoded in the CloudEvent schema. + /// + /// The cancellation token to use. + /// A list of CloudEvents. + public virtual async Task DeserializeCloudEventsAsync(string requestContent, CancellationToken cancellationToken = default) + => await DeserializeCloudEventsInternal(requestContent, true /*async*/, cancellationToken).ConfigureAwait(false); + + private async Task DeserializeCloudEventsInternal(string requestContent, bool async, CancellationToken cancellationToken = default) + { + List cloudEventsInternal = new List(); + List cloudEvents = new List(); + + // Deserialize raw JSON string into separate events, deserialize event envelope properties + JsonDocument requestDocument = await ParseJsonToDocument(requestContent, async, cancellationToken).ConfigureAwait(false); + foreach (JsonElement property in requestDocument.RootElement.EnumerateArray()) + { + cloudEventsInternal.Add(CloudEventInternal.DeserializeCloudEventInternal(property)); + } + + // Deserialize 'Data' property from JsonElement for each event + foreach (CloudEventInternal cloudEventInternal in cloudEventsInternal) + { + object cloudEventData = null; + if (cloudEventInternal.DataBase64 != null) + { + cloudEventData = Convert.FromBase64String(cloudEventInternal.DataBase64); + } + else + { + JsonElement? dataElement = cloudEventInternal.Data; + + if (dataElement.HasValue && dataElement.Value.ValueKind != JsonValueKind.Null) + { + // Reserialize JsonElement to stream + MemoryStream dataStream = SerializePayloadToStream(dataElement, cancellationToken); + + // First, let's attempt to find the mapping for the event type in the custom event mapping. + if (_customEventTypeMappings.TryGetValue(cloudEventInternal.Type, out Type typeOfEventData)) + { + if (!TryGetPrimitiveFromJsonElement(dataElement.Value, out cloudEventData)) + { + if (async) + { + cloudEventData = await _dataSerializer.DeserializeAsync(dataStream, typeOfEventData, cancellationToken).ConfigureAwait(false); + } + else + { + cloudEventData = _dataSerializer.Deserialize(dataStream, typeOfEventData, cancellationToken); + } + } + } + // If a custom mapping doesn't exist, let's attempt to find the mapping for the deserialization function in the system event type mapping. + else if (SystemEventTypeMappings.SystemEventDeserializers.TryGetValue(cloudEventInternal.Type, out Func systemDeserializationFunction)) + { + cloudEventData = systemDeserializationFunction(dataElement.Value); + } + // If no custom mapping was added, either return a primitive/string, or an object wrapped as BinaryData + else + { + // If event data is not a primitive/string, return as BinaryData + if (!TryGetPrimitiveFromJsonElement(dataElement.Value, out cloudEventData)) + { + cloudEventData = BinaryData.FromStream(dataStream); + } + } + } + else // Event has null data + { + cloudEventData = null; + cloudEventInternal.Type = ""; + } + } + + cloudEvents.Add(new CloudEvent( + cloudEventInternal.Source, + cloudEventInternal.Type) + { + Id = cloudEventInternal.Id, + Data = cloudEventData, + Time = cloudEventInternal.Time, + DataSchema = cloudEventInternal.Dataschema, + DataContentType = cloudEventInternal.Datacontenttype, + Subject = cloudEventInternal.Subject + }); + } + + return cloudEvents.ToArray(); + } + + private static bool TryGetPrimitiveFromJsonElement(JsonElement jsonElement, out object elementValue) + { + elementValue = null; + if (jsonElement.ValueKind == JsonValueKind.True || jsonElement.ValueKind == JsonValueKind.False) + { + elementValue = jsonElement.GetBoolean(); + } + else if (jsonElement.ValueKind == JsonValueKind.Number) + { + if (jsonElement.TryGetInt32(out var vali)) + { + elementValue = vali; + } + if (jsonElement.TryGetInt64(out var vall)) + { + elementValue = vall; + } + if (jsonElement.TryGetDouble(out var val)) + { + elementValue = val; + } + } + else if (jsonElement.ValueKind == JsonValueKind.String) + { + elementValue = jsonElement.GetString(); + } + else if (jsonElement.ValueKind == JsonValueKind.Undefined) + { + elementValue = ""; + } + + return elementValue != null; + } + + private static async Task ParseJsonToDocument(string requestContent, bool async, CancellationToken cancellationToken) + { + MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(requestContent)); + if (async) + { + return await JsonDocument.ParseAsync(stream, default, cancellationToken).ConfigureAwait(false); + } + else + { + return JsonDocument.Parse(stream, default); + } + } + + private static MemoryStream SerializePayloadToStream(JsonElement? dataElement, CancellationToken cancellationToken) + { + MemoryStream dataStream = new MemoryStream(); + JsonObjectSerializer serializer = new JsonObjectSerializer(); + serializer.Serialize(dataStream, dataElement, dataElement.GetType(), cancellationToken); + dataStream.Position = 0; + return dataStream; + } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumerOptions.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumerOptions.cs new file mode 100644 index 000000000000..85ece279e3af --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridConsumerOptions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Messaging.EventGrid +{ + /// + /// Options for configuring deserialization in EventGridConsumer. + /// + public class EventGridConsumerOptions + { + /// + /// Serializer used to decode custom payloads from JSON. + /// + public ObjectSerializer DataSerializer { get; set; } = new JsonObjectSerializer(); + + /// + /// Contains the mappings for custom event types. For example, + /// "Contoso.Items.ItemReceived" mapping to type ContosoItemReceivedEventData. + /// + public IDictionary CustomEventTypeMappings { get; private set; } = new Dictionary(); + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEvent.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEvent.cs index 8abd1456a9cb..2abb07ce9788 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEvent.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEvent.cs @@ -4,7 +4,7 @@ using System; using Azure.Core; -namespace Azure.Messaging.EventGrid.Models +namespace Azure.Messaging.EventGrid { /// Properties of an event published to an Event Grid topic using the EventGrid Schema. public class EventGridEvent @@ -45,9 +45,6 @@ public EventGridEvent(string subject, object data, string eventType, string data /// The time (in UTC) the event was generated. public DateTimeOffset EventTime { get; set; } = DateTimeOffset.UtcNow; - /// The schema version of the event metadata. - public string MetadataVersion { get; set; } - /// The schema version of the data object. public string DataVersion { get; set; } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEventInternal.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEventInternal.cs index 38274184a8d5..b09f2a68d59b 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEventInternal.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridEventInternal.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Text.Json; using Azure.Core; namespace Azure.Messaging.EventGrid.Models @@ -9,6 +10,6 @@ namespace Azure.Messaging.EventGrid.Models [CodeGenModel("EventGridEvent")] internal partial class EventGridEventInternal { - + public JsonElement Data { get; set; } } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClient.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClient.cs index e8e7a0c96db0..b4ca49db79f9 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClient.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClient.cs @@ -4,9 +4,11 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -18,7 +20,7 @@ namespace Azure.Messaging.EventGrid { /// - /// Client used to interact with the Event Grid service + /// Client used to interact with the Event Grid service. /// public class EventGridPublisherClient { @@ -27,10 +29,10 @@ public class EventGridPublisherClient private string _hostName => _endpoint.Host; private readonly Uri _endpoint; private readonly AzureKeyCredential _key; - private string _apiVersion; - private ObjectSerializer _serializer; + private readonly string _apiVersion; + private readonly ObjectSerializer _dataSerializer; - /// Initalizes an instance of EventGridClient + /// Initalizes an instance of EventGridClient. protected EventGridPublisherClient() { } @@ -59,12 +61,12 @@ public EventGridPublisherClient(Uri endpoint, AzureKeyCredential credential, Eve { Argument.AssertNotNull(credential, nameof(credential)); options ??= new EventGridPublisherClientOptions(); - _serializer = options.Serializer ?? new JsonObjectSerializer(); - _apiVersion = options.GetVersionString(); + _dataSerializer = options.DataSerializer ?? new JsonObjectSerializer(); + _apiVersion = options.Version.GetVersionString(); _endpoint = endpoint; _key = credential; HttpPipeline pipeline = HttpPipelineBuilder.Build(options, new AzureKeyCredentialPolicy(credential, Constants.SasKeyName)); - _serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), pipeline, options.GetVersionString()); + _serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), pipeline, options.Version.GetVersionString()); _clientDiagnostics = new ClientDiagnostics(options); } @@ -78,32 +80,32 @@ public EventGridPublisherClient(Uri endpoint, EventGridSharedAccessSignatureCred { Argument.AssertNotNull(credential, nameof(credential)); options ??= new EventGridPublisherClientOptions(); - _serializer = options.Serializer ?? new JsonObjectSerializer(); + _dataSerializer = options.DataSerializer ?? new JsonObjectSerializer(); _endpoint = endpoint; HttpPipeline pipeline = HttpPipelineBuilder.Build(options, new EventGridSharedAccessSignatureCredentialPolicy(credential)); - _serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), pipeline, options.GetVersionString()); + _serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), pipeline, options.Version.GetVersionString()); _clientDiagnostics = new ClientDiagnostics(options); } /// Publishes a batch of EventGridEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual async Task PublishEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) + public virtual async Task SendEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) => await PublishEventsInternal(events, true /*async*/, cancellationToken).ConfigureAwait(false); /// Publishes a batch of EventGridEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual Response PublishEvents(IEnumerable events, CancellationToken cancellationToken = default) + public virtual Response SendEvents(IEnumerable events, CancellationToken cancellationToken = default) => PublishEventsInternal(events, false /*async*/, cancellationToken).EnsureCompleted(); /// Publishes a batch of EventGridEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. - /// Whether to invoke the operation asynchronously + /// Whether to invoke the operation asynchronously. /// The cancellation token to use. private async Task PublishEventsInternal(IEnumerable events, bool async, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(PublishEvents)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(SendEvents)}"); scope.Start(); try @@ -117,13 +119,15 @@ private async Task PublishEventsInternal(IEnumerable e // Individual events cannot be null Argument.AssertNotNull(egEvent, nameof(egEvent)); + MemoryStream stream = new MemoryStream(); + _dataSerializer.Serialize(stream, egEvent.Data, egEvent.Data.GetType(), cancellationToken); + stream.Position = 0; + JsonDocument data = JsonDocument.Parse(stream); + EventGridEventInternal newEGEvent = new EventGridEventInternal( egEvent.Id, egEvent.Subject, - new EventGridSerializer( - egEvent.Data, - _serializer, - cancellationToken), + data.RootElement, egEvent.EventType, egEvent.EventTime, egEvent.DataVersion) @@ -159,22 +163,22 @@ private async Task PublishEventsInternal(IEnumerable e /// Publishes a batch of CloudEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual async Task PublishCloudEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) + public virtual async Task SendEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) => await PublishCloudEventsInternal(events, true /*async*/, cancellationToken).ConfigureAwait(false); /// Publishes a batch of CloudEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual Response PublishCloudEvents(IEnumerable events, CancellationToken cancellationToken = default) + public virtual Response SendEvents(IEnumerable events, CancellationToken cancellationToken = default) => PublishCloudEventsInternal(events, false /*async*/, cancellationToken).EnsureCompleted(); /// Publishes a batch of CloudEvents to an Azure Event Grid topic. /// An array of events to be published to Event Grid. - /// Whether to invoke the operation asynchronously + /// Whether to invoke the operation asynchronously. /// The cancellation token to use. private async Task PublishCloudEventsInternal(IEnumerable events, bool async, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(PublishCloudEvents)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(SendEvents)}"); scope.Start(); try @@ -192,7 +196,7 @@ private async Task PublishCloudEventsInternal(IEnumerable cloudEvent.Id, cloudEvent.Source, cloudEvent.Type, - cloudEvent.SpecVersion) + "1.0") { Time = cloudEvent.Time, Datacontenttype = cloudEvent.DataContentType, @@ -202,7 +206,7 @@ private async Task PublishCloudEventsInternal(IEnumerable foreach (KeyValuePair kvp in cloudEvent.ExtensionAttributes) { - newCloudEvent.Add(kvp.Key, new EventGridSerializer(kvp.Value, _serializer, cancellationToken)); + newCloudEvent.Add(kvp.Key, new CustomModelSerializer(kvp.Value, _dataSerializer, cancellationToken)); } // The 'Data' property is optional for CloudEvents @@ -218,10 +222,11 @@ private async Task PublishCloudEventsInternal(IEnumerable } else { - newCloudEvent.Data = new EventGridSerializer( - cloudEvent.Data, - _serializer, - cancellationToken); + MemoryStream stream = new MemoryStream(); + _dataSerializer.Serialize(stream, cloudEvent.Data, cloudEvent.Data.GetType(), cancellationToken); + stream.Position = 0; + JsonDocument data = JsonDocument.Parse(stream); + newCloudEvent.Data = data.RootElement; } } eventsWithSerializedPayloads.Add(newCloudEvent); @@ -252,29 +257,29 @@ private async Task PublishCloudEventsInternal(IEnumerable /// Publishes a batch of custom events to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual async Task PublishCustomEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) + public virtual async Task SendEventsAsync(IEnumerable events, CancellationToken cancellationToken = default) => await PublishCustomEventsInternal(events, true /*async*/, cancellationToken).ConfigureAwait(false); /// Publishes a batch of custom events to an Azure Event Grid topic. /// An array of events to be published to Event Grid. /// The cancellation token to use. - public virtual Response PublishCustomEvents(IEnumerable events, CancellationToken cancellationToken = default) + public virtual Response SendEvents(IEnumerable events, CancellationToken cancellationToken = default) => PublishCustomEventsInternal(events, false /*async*/, cancellationToken).EnsureCompleted(); private async Task PublishCustomEventsInternal(IEnumerable events, bool async, CancellationToken cancellationToken = default) { - using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(PublishCustomEvents)}"); + using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(EventGridPublisherClient)}.{nameof(SendEvents)}"); scope.Start(); try { - List serializedEvents = new List(); + List serializedEvents = new List(); foreach (object customEvent in events) { serializedEvents.Add( - new EventGridSerializer( + new CustomModelSerializer( customEvent, - _serializer, + _dataSerializer, cancellationToken)); } if (async) @@ -307,7 +312,7 @@ private async Task PublishCustomEventsInternal(IEnumerable eve /// Key credential used to generate the token. /// Service version to use when handling requests made with the SAS token. /// Returns the generated SAS token string. - public static string BuildSharedAccessSignature(Uri endpoint, DateTimeOffset expirationUtc, AzureKeyCredential key, string apiVersion = "2018-01-01") + public static string BuildSharedAccessSignature(Uri endpoint, DateTimeOffset expirationUtc, AzureKeyCredential key, EventGridPublisherClientOptions.ServiceVersion apiVersion = EventGridPublisherClientOptions.LatestVersion) { const char Resource = 'r'; const char Expiration = 'e'; @@ -315,8 +320,8 @@ public static string BuildSharedAccessSignature(Uri endpoint, DateTimeOffset exp var uriBuilder = new RequestUriBuilder(); uriBuilder.Reset(endpoint); - uriBuilder.AppendQuery("api-version", apiVersion, true); - string encodedResource = HttpUtility.UrlEncode(endpoint.ToString()); + uriBuilder.AppendQuery("api-version", apiVersion.GetVersionString(), true); + string encodedResource = HttpUtility.UrlEncode(uriBuilder.ToString()); var culture = CultureInfo.CreateSpecificCulture("en-US"); var encodedExpirationUtc = HttpUtility.UrlEncode(expirationUtc.ToString(culture)); diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClientOptions.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClientOptions.cs index 334872f2140d..f1633a4f5bf7 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClientOptions.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridPublisherClientOptions.cs @@ -38,9 +38,9 @@ public enum ServiceVersion internal ServiceVersion Version { get; } /// - /// Used to serialize the given events to UTF-8 encoded JSON. + /// Used to serialize the payloads of given events to UTF-8 encoded JSON. /// - public ObjectSerializer Serializer { get; set; } + public ObjectSerializer DataSerializer { get; set; } /// /// Initializes a new instance of the @@ -54,18 +54,5 @@ public EventGridPublisherClientOptions(ServiceVersion version = LatestVersion) { Version = version; } - - - internal string GetVersionString() - { - switch (Version) - { - case ServiceVersion.V2018_01_01: - return "2018-01-01"; - - default: - throw new ArgumentException($"Version {Version.ToString()} not supported."); - } - } } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridSerializer.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridSerializer.cs deleted file mode 100644 index 18fef52dc96f..000000000000 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventGridSerializer.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using System.Threading; -using Azure.Core; -using Azure.Core.Serialization; - -namespace Azure.Messaging.EventGrid -{ - internal class EventGridSerializer : IUtf8JsonSerializable - { - public object _customEvent; - public CancellationToken _cancellationToken; - public ObjectSerializer _serializer; - - public EventGridSerializer(object customEvent, ObjectSerializer serializer, CancellationToken cancellationToken) - { - _customEvent = customEvent; - _serializer = serializer; - _cancellationToken = cancellationToken; - } - public void Write(Utf8JsonWriter writer) - { - var stream = new MemoryStream(); - _serializer.Serialize(stream, _customEvent, _customEvent.GetType(), _cancellationToken); - stream.Seek(0, SeekOrigin.Begin); - JsonDocument.Parse(stream).WriteTo(writer); - } - } -} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventTypes.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventTypes.cs new file mode 100644 index 000000000000..e249ca928f77 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/EventTypes.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Azure.Messaging.EventGrid +{ + /// + /// Represents the names of the various event types for the system events published to + /// Azure Event Grid. + /// + internal static class EventTypes + { + // Keep this sorted by the name of the service publishing the events. + + // AppConfiguration events + public const string AppConfigurationKeyValueDeletedEvent = "Microsoft.AppConfiguration.KeyValueDeleted"; + public const string AppConfigurationKeyValueModifiedEvent = "Microsoft.AppConfiguration.KeyValueModified"; + + // ContainerRegistry events + public const string ContainerRegistryImagePushedEvent = "Microsoft.ContainerRegistry.ImagePushed"; + public const string ContainerRegistryImageDeletedEvent = "Microsoft.ContainerRegistry.ImageDeleted"; + public const string ContainerRegistryChartDeletedEvent = "Microsoft.ContainerRegistry.ChartDeleted"; + public const string ContainerRegistryChartPushedEvent = "Microsoft.ContainerRegistry.ChartPushed"; + + // Device events + public const string IoTHubDeviceCreatedEvent = "Microsoft.Devices.DeviceCreated"; + public const string IoTHubDeviceDeletedEvent = "Microsoft.Devices.DeviceDeleted"; + public const string IoTHubDeviceConnectedEvent = "Microsoft.Devices.DeviceConnected"; + public const string IoTHubDeviceDisconnectedEvent = "Microsoft.Devices.DeviceDisconnected"; + public const string IotHubDeviceTelemetryEvent = "Microsoft.Devices.DeviceTelemetry"; + + // EventGrid events + public const string EventGridSubscriptionValidationEvent = "Microsoft.EventGrid.SubscriptionValidationEvent"; + public const string EventGridSubscriptionDeletedEvent = "Microsoft.EventGrid.SubscriptionDeletedEvent"; + + // Event Hub Events + public const string EventHubCaptureFileCreatedEvent = "Microsoft.EventHub.CaptureFileCreated"; + + // MachineLearningServices events + public const string MachineLearningServicesDatasetDriftDetectedEvent = "Microsoft.MachineLearningServices.DatasetDriftDetected"; + public const string MachineLearningServicesModelDeployedEvent = "Microsoft.MachineLearningServices.ModelDeployed"; + public const string MachineLearningServicesModelRegisteredEvent = "Microsoft.MachineLearningServices.ModelRegistered"; + public const string MachineLearningServicesRunCompletedEvent = "Microsoft.MachineLearningServices.RunCompleted"; + public const string MachineLearningServicesRunStatusChangedEvent = "Microsoft.MachineLearningServices.RunStatusChanged"; + + // Maps events + public const string MapsGeofenceEnteredEvent = "Microsoft.Maps.GeofenceEntered"; + public const string MapsGeofenceExitedEvent = "Microsoft.Maps.GeofenceExited"; + public const string MapsGeofenceResultEvent = "Microsoft.Maps.GeofenceResult"; + + // Media Services events + public const string MediaJobStateChangeEvent = "Microsoft.Media.JobStateChange"; + public const string MediaJobOutputStateChangeEvent = "Microsoft.Media.JobOutputStateChange"; + public const string MediaJobScheduledEvent = "Microsoft.Media.JobScheduled"; + public const string MediaJobProcessingEvent = "Microsoft.Media.JobProcessing"; + public const string MediaJobCancelingEvent = "Microsoft.Media.JobCanceling"; + public const string MediaJobFinishedEvent = "Microsoft.Media.JobFinished"; + public const string MediaJobCanceledEvent = "Microsoft.Media.JobCanceled"; + public const string MediaJobErroredEvent = "Microsoft.Media.JobErrored"; + public const string MediaJobOutputCanceledEvent = "Microsoft.Media.JobOutputCanceled"; + public const string MediaJobOutputCancelingEvent = "Microsoft.Media.JobOutputCanceling"; + public const string MediaJobOutputErroredEvent = "Microsoft.Media.JobOutputErrored"; + public const string MediaJobOutputFinishedEvent = "Microsoft.Media.JobOutputFinished"; + public const string MediaJobOutputProcessingEvent = "Microsoft.Media.JobOutputProcessing"; + public const string MediaJobOutputScheduledEvent = "Microsoft.Media.JobOutputScheduled"; + public const string MediaJobOutputProgressEvent = "Microsoft.Media.JobOutputProgress"; + public const string MediaLiveEventEncoderConnectedEvent = "Microsoft.Media.LiveEventEncoderConnected"; + public const string MediaLiveEventConnectionRejectedEvent = "Microsoft.Media.LiveEventConnectionRejected"; + public const string MediaLiveEventEncoderDisconnectedEvent = "Microsoft.Media.LiveEventEncoderDisconnected"; + public const string MediaLiveEventIncomingStreamReceivedEvent = "Microsoft.Media.LiveEventIncomingStreamReceived"; + public const string MediaLiveEventIncomingStreamsOutOfSyncEvent = "Microsoft.Media.LiveEventIncomingStreamsOutOfSync"; + public const string MediaLiveEventIncomingVideoStreamsOutOfSyncEvent = "Microsoft.Media.LiveEventIncomingVideoStreamsOutOfSync"; + public const string MediaLiveEventIncomingChunkDroppedEvent = "Microsoft.Media.LiveEventIncomingDataChunkDropped"; + public const string MediaLiveEventIngestHeartbeatEvent = "Microsoft.Media.LiveEventIngestHeartbeat"; + public const string MediaLiveEventTrackDiscontinuityDetectedEvent = "Microsoft.Media.LiveEventTrackDiscontinuityDetected"; + + // Resource Manager (Azure Subscription/Resource Group) events + public const string ResourceWriteSuccessEvent = "Microsoft.Resources.ResourceWriteSuccess"; + public const string ResourceWriteFailureEvent = "Microsoft.Resources.ResourceWriteFailure"; + public const string ResourceWriteCancelEvent = "Microsoft.Resources.ResourceWriteCancel"; + public const string ResourceDeleteSuccessEvent = "Microsoft.Resources.ResourceDeleteSuccess"; + public const string ResourceDeleteFailureEvent = "Microsoft.Resources.ResourceDeleteFailure"; + public const string ResourceDeleteCancelEvent = "Microsoft.Resources.ResourceDeleteCancel"; + public const string ResourceActionSuccessEvent = "Microsoft.Resources.ResourceActionSuccess"; + public const string ResourceActionFailureEvent = "Microsoft.Resources.ResourceActionFailure"; + public const string ResourceActionCancelEvent = "Microsoft.Resources.ResourceActionCancel"; + + // ServiceBus events + public const string ServiceBusActiveMessagesAvailableWithNoListenersEvent = "Microsoft.ServiceBus.ActiveMessagesAvailableWithNoListeners"; + public const string ServiceBusDeadletterMessagesAvailableWithNoListenerEvent = "Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener"; + + // Storage events + public const string StorageBlobCreatedEvent = "Microsoft.Storage.BlobCreated"; + public const string StorageBlobDeletedEvent = "Microsoft.Storage.BlobDeleted"; + public const string StorageBlobRenamedEvent = "Microsoft.Storage.BlobRenamed"; + public const string StorageDirectoryCreatedEvent = "Microsoft.Storage.DirectoryCreated"; + public const string StorageDirectoryDeletedEvent = "Microsoft.Storage.DirectoryDeleted"; + public const string StorageDirectoryRenamedEvent = "Microsoft.Storage.DirectoryRenamed"; + + // App Service + public const string WebAppUpdated = "Microsoft.Web.AppUpdated"; + public const string WebBackupOperationStarted = "Microsoft.Web.BackupOperationStarted"; + public const string WebBackupOperationCompleted = "Microsoft.Web.BackupOperationCompleted"; + public const string WebBackupOperationFailed = "Microsoft.Web.BackupOperationFailed"; + public const string WebRestoreOperationStarted = "Microsoft.Web.RestoreOperationStarted"; + public const string WebRestoreOperationCompleted = "Microsoft.Web.RestoreOperationCompleted"; + public const string WebRestoreOperationFailed = "Microsoft.Web.RestoreOperationFailed"; + public const string WebSlotSwapStarted = "Microsoft.Web.SlotSwapStarted"; + public const string WebSlotSwapCompleted = "Microsoft.Web.SlotSwapCompleted"; + public const string WebSlotSwapFailed = "Microsoft.Web.SlotSwapFailed"; + public const string WebSlotSwapWithPreviewStarted = "Microsoft.Web.SlotSwapWithPreviewStarted"; + public const string WebSlotSwapWithPreviewCancelled = "Microsoft.Web.SlotSwapWithPreviewCancelled"; + public const string WebAppServicePlanUpdated = "Microsoft.Web.AppServicePlanUpdated"; + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/ServiceVersionExtensions.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/ServiceVersionExtensions.cs new file mode 100644 index 000000000000..ad6999ae9453 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/ServiceVersionExtensions.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using static Azure.Messaging.EventGrid.EventGridPublisherClientOptions; + +namespace Azure.Messaging.EventGrid +{ + internal static class ServiceVersionExtensions + { + internal static string GetVersionString(this ServiceVersion version) + { + return version switch + { + ServiceVersion.V2018_01_01 => "2018-01-01", + _ => throw new ArgumentException($"Version {version.ToString()} not supported."), + }; + } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/SystemEventTypeMappings.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/SystemEventTypeMappings.cs new file mode 100644 index 000000000000..f2a701e81640 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Customization/SystemEventTypeMappings.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Messaging.EventGrid.SystemEvents; + +namespace Azure.Messaging.EventGrid +{ + internal class SystemEventTypeMappings + { + public static readonly IReadOnlyDictionary> SystemEventDeserializers = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + // KEEP THIS SORTED BY THE NAME OF THE PUBLISHING SERVICE + // Add handling for additional event types here. + + // AppConfiguration events + { EventTypes.AppConfigurationKeyValueDeletedEvent, AppConfigurationKeyValueDeletedEventData.DeserializeAppConfigurationKeyValueDeletedEventData }, + { EventTypes.AppConfigurationKeyValueModifiedEvent, AppConfigurationKeyValueModifiedEventData.DeserializeAppConfigurationKeyValueModifiedEventData }, + + // ContainerRegistry events + { EventTypes.ContainerRegistryImagePushedEvent, ContainerRegistryImagePushedEventData.DeserializeContainerRegistryImagePushedEventData }, + { EventTypes.ContainerRegistryImageDeletedEvent, ContainerRegistryImageDeletedEventData.DeserializeContainerRegistryImageDeletedEventData }, + { EventTypes.ContainerRegistryChartDeletedEvent, ContainerRegistryChartDeletedEventData.DeserializeContainerRegistryChartDeletedEventData }, + { EventTypes.ContainerRegistryChartPushedEvent, ContainerRegistryChartPushedEventData.DeserializeContainerRegistryChartPushedEventData }, + + // IoTHub Device events + { EventTypes.IoTHubDeviceCreatedEvent, IotHubDeviceCreatedEventData.DeserializeIotHubDeviceCreatedEventData }, + { EventTypes.IoTHubDeviceDeletedEvent, IotHubDeviceDeletedEventData.DeserializeIotHubDeviceDeletedEventData }, + { EventTypes.IoTHubDeviceConnectedEvent, IotHubDeviceConnectedEventData.DeserializeIotHubDeviceConnectedEventData }, + { EventTypes.IoTHubDeviceDisconnectedEvent, IotHubDeviceDisconnectedEventData.DeserializeIotHubDeviceDisconnectedEventData }, + { EventTypes.IotHubDeviceTelemetryEvent, IotHubDeviceTelemetryEventData.DeserializeIotHubDeviceTelemetryEventData }, + + // EventGrid events + { EventTypes.EventGridSubscriptionValidationEvent, SubscriptionValidationEventData.DeserializeSubscriptionValidationEventData }, + { EventTypes.EventGridSubscriptionDeletedEvent, SubscriptionDeletedEventData.DeserializeSubscriptionDeletedEventData }, + + // Event Hub events + { EventTypes.EventHubCaptureFileCreatedEvent, EventHubCaptureFileCreatedEventData.DeserializeEventHubCaptureFileCreatedEventData }, + + // MachineLearningServices events + { EventTypes.MachineLearningServicesDatasetDriftDetectedEvent, MachineLearningServicesDatasetDriftDetectedEventData.DeserializeMachineLearningServicesDatasetDriftDetectedEventData }, + { EventTypes.MachineLearningServicesModelDeployedEvent, MachineLearningServicesModelDeployedEventData.DeserializeMachineLearningServicesModelDeployedEventData }, + { EventTypes.MachineLearningServicesModelRegisteredEvent, MachineLearningServicesModelRegisteredEventData.DeserializeMachineLearningServicesModelRegisteredEventData }, + { EventTypes.MachineLearningServicesRunCompletedEvent, MachineLearningServicesRunCompletedEventData.DeserializeMachineLearningServicesRunCompletedEventData }, + { EventTypes.MachineLearningServicesRunStatusChangedEvent, MachineLearningServicesRunStatusChangedEventData.DeserializeMachineLearningServicesRunStatusChangedEventData }, + + // Maps events + { EventTypes.MapsGeofenceEnteredEvent, MapsGeofenceEnteredEventData.DeserializeMapsGeofenceEnteredEventData }, + { EventTypes.MapsGeofenceExitedEvent, MapsGeofenceExitedEventData.DeserializeMapsGeofenceExitedEventData }, + { EventTypes.MapsGeofenceResultEvent, MapsGeofenceResultEventData.DeserializeMapsGeofenceResultEventData }, + + // Media Services events + { EventTypes.MediaJobStateChangeEvent, MediaJobStateChangeEventData.DeserializeMediaJobStateChangeEventData }, + { EventTypes.MediaJobOutputStateChangeEvent, MediaJobOutputStateChangeEventData.DeserializeMediaJobOutputStateChangeEventData }, + { EventTypes.MediaJobScheduledEvent, MediaJobScheduledEventData.DeserializeMediaJobScheduledEventData }, + { EventTypes.MediaJobProcessingEvent, MediaJobProcessingEventData.DeserializeMediaJobProcessingEventData }, + { EventTypes.MediaJobCancelingEvent, MediaJobCancelingEventData.DeserializeMediaJobCancelingEventData }, + { EventTypes.MediaJobFinishedEvent, MediaJobFinishedEventData.DeserializeMediaJobFinishedEventData }, + { EventTypes.MediaJobCanceledEvent, MediaJobCanceledEventData.DeserializeMediaJobCanceledEventData }, + { EventTypes.MediaJobErroredEvent, MediaJobErroredEventData.DeserializeMediaJobErroredEventData }, + { EventTypes.MediaJobOutputCanceledEvent, MediaJobOutputCanceledEventData.DeserializeMediaJobOutputCanceledEventData }, + { EventTypes.MediaJobOutputCancelingEvent, MediaJobOutputCancelingEventData.DeserializeMediaJobOutputCancelingEventData }, + { EventTypes.MediaJobOutputErroredEvent, MediaJobOutputErroredEventData.DeserializeMediaJobOutputErroredEventData }, + { EventTypes.MediaJobOutputFinishedEvent, MediaJobOutputFinishedEventData.DeserializeMediaJobOutputFinishedEventData }, + { EventTypes.MediaJobOutputProcessingEvent, MediaJobOutputProcessingEventData.DeserializeMediaJobOutputProcessingEventData }, + { EventTypes.MediaJobOutputScheduledEvent, MediaJobOutputScheduledEventData.DeserializeMediaJobOutputScheduledEventData }, + { EventTypes.MediaJobOutputProgressEvent, MediaJobOutputProgressEventData.DeserializeMediaJobOutputProgressEventData }, + { EventTypes.MediaLiveEventEncoderConnectedEvent, MediaLiveEventEncoderConnectedEventData.DeserializeMediaLiveEventEncoderConnectedEventData }, + { EventTypes.MediaLiveEventConnectionRejectedEvent, MediaLiveEventConnectionRejectedEventData.DeserializeMediaLiveEventConnectionRejectedEventData }, + { EventTypes.MediaLiveEventEncoderDisconnectedEvent, MediaLiveEventEncoderDisconnectedEventData.DeserializeMediaLiveEventEncoderDisconnectedEventData }, + { EventTypes.MediaLiveEventIncomingStreamReceivedEvent, MediaLiveEventIncomingStreamReceivedEventData.DeserializeMediaLiveEventIncomingStreamReceivedEventData }, + { EventTypes.MediaLiveEventIncomingStreamsOutOfSyncEvent, MediaLiveEventIncomingStreamsOutOfSyncEventData.DeserializeMediaLiveEventIncomingStreamsOutOfSyncEventData }, + { EventTypes.MediaLiveEventIncomingVideoStreamsOutOfSyncEvent, MediaLiveEventIncomingVideoStreamsOutOfSyncEventData.DeserializeMediaLiveEventIncomingVideoStreamsOutOfSyncEventData }, + { EventTypes.MediaLiveEventIncomingChunkDroppedEvent, MediaLiveEventIncomingDataChunkDroppedEventData.DeserializeMediaLiveEventIncomingDataChunkDroppedEventData }, + { EventTypes.MediaLiveEventIngestHeartbeatEvent, MediaLiveEventIngestHeartbeatEventData.DeserializeMediaLiveEventIngestHeartbeatEventData }, + { EventTypes.MediaLiveEventTrackDiscontinuityDetectedEvent, MediaLiveEventTrackDiscontinuityDetectedEventData.DeserializeMediaLiveEventTrackDiscontinuityDetectedEventData }, + + // Resource Manager (Azure Subscription/Resource Group) events + { EventTypes.ResourceWriteSuccessEvent, ResourceWriteSuccessData.DeserializeResourceWriteSuccessData }, + { EventTypes.ResourceWriteFailureEvent, ResourceWriteFailureData.DeserializeResourceWriteFailureData }, + { EventTypes.ResourceWriteCancelEvent, ResourceWriteCancelData.DeserializeResourceWriteCancelData }, + { EventTypes.ResourceDeleteSuccessEvent, ResourceDeleteSuccessData.DeserializeResourceDeleteSuccessData }, + { EventTypes.ResourceDeleteFailureEvent, ResourceDeleteFailureData.DeserializeResourceDeleteFailureData }, + { EventTypes.ResourceDeleteCancelEvent, ResourceDeleteCancelData.DeserializeResourceDeleteCancelData }, + { EventTypes.ResourceActionSuccessEvent, ResourceActionSuccessData.DeserializeResourceActionSuccessData }, + { EventTypes.ResourceActionFailureEvent, ResourceActionFailureData.DeserializeResourceActionFailureData }, + { EventTypes.ResourceActionCancelEvent, ResourceActionCancelData.DeserializeResourceActionCancelData }, + + // ServiceBus events + { EventTypes.ServiceBusActiveMessagesAvailableWithNoListenersEvent, ServiceBusActiveMessagesAvailableWithNoListenersEventData.DeserializeServiceBusActiveMessagesAvailableWithNoListenersEventData }, + { EventTypes.ServiceBusDeadletterMessagesAvailableWithNoListenerEvent, ServiceBusDeadletterMessagesAvailableWithNoListenersEventData.DeserializeServiceBusDeadletterMessagesAvailableWithNoListenersEventData }, + + // Storage events + { EventTypes.StorageBlobCreatedEvent, StorageBlobCreatedEventData.DeserializeStorageBlobCreatedEventData }, + { EventTypes.StorageBlobDeletedEvent, StorageBlobDeletedEventData.DeserializeStorageBlobDeletedEventData }, + { EventTypes.StorageBlobRenamedEvent, StorageBlobRenamedEventData.DeserializeStorageBlobRenamedEventData }, + { EventTypes.StorageDirectoryCreatedEvent, StorageDirectoryCreatedEventData.DeserializeStorageDirectoryCreatedEventData }, + { EventTypes.StorageDirectoryDeletedEvent, StorageDirectoryDeletedEventData.DeserializeStorageDirectoryDeletedEventData }, + { EventTypes.StorageDirectoryRenamedEvent, StorageDirectoryRenamedEventData.DeserializeStorageDirectoryRenamedEventData }, + + // App Service + { EventTypes.WebAppUpdated, WebAppUpdatedEventData.DeserializeWebAppUpdatedEventData }, + { EventTypes.WebBackupOperationStarted, WebBackupOperationStartedEventData.DeserializeWebBackupOperationStartedEventData }, + { EventTypes.WebBackupOperationCompleted, WebBackupOperationCompletedEventData.DeserializeWebBackupOperationCompletedEventData }, + { EventTypes.WebBackupOperationFailed, WebBackupOperationFailedEventData.DeserializeWebBackupOperationFailedEventData }, + { EventTypes.WebRestoreOperationStarted, WebRestoreOperationStartedEventData.DeserializeWebRestoreOperationStartedEventData }, + { EventTypes.WebRestoreOperationCompleted, WebRestoreOperationCompletedEventData.DeserializeWebRestoreOperationCompletedEventData }, + { EventTypes.WebRestoreOperationFailed, WebRestoreOperationFailedEventData.DeserializeWebRestoreOperationFailedEventData }, + { EventTypes.WebSlotSwapStarted, WebSlotSwapStartedEventData.DeserializeWebSlotSwapStartedEventData }, + { EventTypes.WebSlotSwapCompleted, WebSlotSwapCompletedEventData.DeserializeWebSlotSwapCompletedEventData }, + { EventTypes.WebSlotSwapFailed, WebSlotSwapFailedEventData.DeserializeWebSlotSwapFailedEventData }, + { EventTypes.WebSlotSwapWithPreviewStarted, WebSlotSwapWithPreviewStartedEventData.DeserializeWebSlotSwapWithPreviewStartedEventData }, + { EventTypes.WebSlotSwapWithPreviewCancelled, WebSlotSwapWithPreviewCancelledEventData.DeserializeWebSlotSwapWithPreviewCancelledEventData }, + { EventTypes.WebAppServicePlanUpdated, WebAppServicePlanUpdatedEventData.DeserializeWebAppServicePlanUpdatedEventData } + }; + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.Serialization.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.Serialization.cs index 3e08afa06b5d..a3c64864150e 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.Serialization.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.Serialization.cs @@ -24,7 +24,7 @@ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) if (Optional.IsDefined(Data)) { writer.WritePropertyName("data"); - writer.WriteObjectValue(Data); + Data.Value.WriteTo(writer); } if (Optional.IsDefined(DataBase64)) { @@ -67,7 +67,7 @@ internal static CloudEventInternal DeserializeCloudEventInternal(JsonElement ele { string id = default; string source = default; - Optional data = default; + Optional data = default; Optional dataBase64 = default; string type = default; Optional time = default; @@ -91,7 +91,7 @@ internal static CloudEventInternal DeserializeCloudEventInternal(JsonElement ele } if (property.NameEquals("data")) { - data = property.Value.GetObject(); + data = property.Value.Clone(); continue; } if (property.NameEquals("data_base64")) @@ -133,7 +133,7 @@ internal static CloudEventInternal DeserializeCloudEventInternal(JsonElement ele additionalPropertiesDictionary.Add(property.Name, property.Value.GetObject()); } additionalProperties = additionalPropertiesDictionary; - return new CloudEventInternal(id, source, data.Value, dataBase64.Value, type, Optional.ToNullable(time), specversion, dataschema.Value, datacontenttype.Value, subject.Value, additionalProperties); + return new CloudEventInternal(id, source, Optional.ToNullable(data), dataBase64.Value, type, Optional.ToNullable(time), specversion, dataschema.Value, datacontenttype.Value, subject.Value, additionalProperties); } } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.cs index d35a1411a6ac..463329b3e124 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/CloudEventInternal.cs @@ -8,6 +8,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text.Json; using Azure.Core; namespace Azure.Messaging.EventGrid.Models @@ -59,7 +60,7 @@ public CloudEventInternal(string id, string source, string type, string specvers /// Content type of data value. /// This describes the subject of the event in the context of the event producer (identified by source). /// . - internal CloudEventInternal(string id, string source, object data, string dataBase64, string type, DateTimeOffset? time, string specversion, string dataschema, string datacontenttype, string subject, IDictionary additionalProperties) + internal CloudEventInternal(string id, string source, JsonElement? data, string dataBase64, string type, DateTimeOffset? time, string specversion, string dataschema, string datacontenttype, string subject, IDictionary additionalProperties) { Id = id; Source = source; @@ -78,8 +79,6 @@ internal CloudEventInternal(string id, string source, object data, string dataBa public string Id { get; set; } /// Identifies the context in which an event happened. The combination of id and source must be unique for each distinct event. public string Source { get; set; } - /// Event data specific to the event type. - public object Data { get; set; } /// Event data specific to the event type, encoded as a base64 string. public string DataBase64 { get; set; } /// Type of event related to the originating occurrence. diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.Serialization.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.Serialization.cs index cc381a79e844..f73d8578c802 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.Serialization.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.Serialization.cs @@ -26,7 +26,7 @@ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) writer.WritePropertyName("subject"); writer.WriteStringValue(Subject); writer.WritePropertyName("data"); - writer.WriteObjectValue(Data); + Data.WriteTo(writer); writer.WritePropertyName("eventType"); writer.WriteStringValue(EventType); writer.WritePropertyName("eventTime"); @@ -41,7 +41,7 @@ internal static EventGridEventInternal DeserializeEventGridEventInternal(JsonEle string id = default; Optional topic = default; string subject = default; - object data = default; + JsonElement data = default; string eventType = default; DateTimeOffset eventTime = default; Optional metadataVersion = default; @@ -65,7 +65,7 @@ internal static EventGridEventInternal DeserializeEventGridEventInternal(JsonEle } if (property.NameEquals("data")) { - data = property.Value.GetObject(); + data = property.Value.Clone(); continue; } if (property.NameEquals("eventType")) diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.cs index dfce763b0bc3..dc1f495ca362 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/src/Generated/Models/EventGridEventInternal.cs @@ -6,6 +6,7 @@ #nullable disable using System; +using System.Text.Json; namespace Azure.Messaging.EventGrid.Models { @@ -19,8 +20,8 @@ internal partial class EventGridEventInternal /// The type of the event that occurred. /// The time (in UTC) the event was generated. /// The schema version of the data object. - /// , , , , or is null. - public EventGridEventInternal(string id, string subject, object data, string eventType, DateTimeOffset eventTime, string dataVersion) + /// , , , or is null. + public EventGridEventInternal(string id, string subject, JsonElement data, string eventType, DateTimeOffset eventTime, string dataVersion) { if (id == null) { @@ -30,10 +31,6 @@ public EventGridEventInternal(string id, string subject, object data, string eve { throw new ArgumentNullException(nameof(subject)); } - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } if (eventType == null) { throw new ArgumentNullException(nameof(eventType)); @@ -60,7 +57,7 @@ public EventGridEventInternal(string id, string subject, object data, string eve /// The time (in UTC) the event was generated. /// The schema version of the event metadata. /// The schema version of the data object. - internal EventGridEventInternal(string id, string topic, string subject, object data, string eventType, DateTimeOffset eventTime, string metadataVersion, string dataVersion) + internal EventGridEventInternal(string id, string topic, string subject, JsonElement data, string eventType, DateTimeOffset eventTime, string metadataVersion, string dataVersion) { Id = id; Topic = topic; @@ -78,8 +75,6 @@ internal EventGridEventInternal(string id, string topic, string subject, object public string Topic { get; set; } /// A resource path relative to the topic path. public string Subject { get; set; } - /// Event data specific to the event type. - public object Data { get; set; } /// The type of the event that occurred. public string EventType { get; set; } /// The time (in UTC) the event was generated. diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ConsumeEventTests.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ConsumeEventTests.cs new file mode 100644 index 000000000000..1b3dec5cfe0a --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ConsumeEventTests.cs @@ -0,0 +1,1418 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core; +using System.Text.Json; +using Azure.Messaging.EventGrid.SystemEvents; +using NUnit.Framework; +using System.Collections; +using Azure.Core.Serialization; + +namespace Azure.Messaging.EventGrid.Tests +{ + public class ConsumeEventTests + { + public readonly EventGridConsumer _eventGridConsumer; + + public ConsumeEventTests() + { + _eventGridConsumer = new EventGridConsumer(); + } + + #region EventGridEvent tests + + #region AppConfiguration events + [Test] + public void ConsumeAppConfigurationKeyValueDeletedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.Maps/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.AppConfiguration.KeyValueDeleted\",\"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"key\":\"key1\",\"label\":\"label1\",\"etag\":\"etag1\"}, \"dataVersion\": \"\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is AppConfigurationKeyValueDeletedEventData); + AppConfigurationKeyValueDeletedEventData eventData = (AppConfigurationKeyValueDeletedEventData)events[0].Data; + Assert.AreEqual("key1", eventData.Key); + } + + [Test] + public void ConsumeAppConfigurationKeyValueModifiedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.Maps/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.AppConfiguration.KeyValueModified\",\"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"key\":\"key1\",\"label\":\"label1\",\"etag\":\"etag1\"}, \"dataVersion\": \"\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is AppConfigurationKeyValueModifiedEventData); + AppConfigurationKeyValueModifiedEventData eventData = (AppConfigurationKeyValueModifiedEventData)events[0].Data; + Assert.AreEqual("key1", eventData.Key); + } + #endregion + + [Test] + public void ConsumeStorageBlobDeletedEventWithExtraProperty() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testfile.txt\", \"eventType\": \"Microsoft.Storage.BlobDeleted\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"brandNewProperty\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageBlobDeletedEventData); + StorageBlobDeletedEventData eventData = (StorageBlobDeletedEventData)events[0].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testfile.txt", eventData.Url); + } + + [Test] + public void ConsumeMultipleEventsInSameBatch() + { + string requestContent = "[ " + + "{ \"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Storage/storageAccounts/myaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/file1.txt\", \"eventType\": \"Microsoft.Storage.BlobCreated\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\", \"id\": \"602a88ef-0001-00e6-1233-1646070610ea\", \"data\": { \"api\": \"PutBlockList\", \"clientRequestId\": \"799304a4-bbc5-45b6-9849-ec2c66be800a\", \"requestId\": \"602a88ef-0001-00e6-1233-164607000000\", \"eTag\": \"0x8D4E44A24ABE7F1\", \"contentType\": \"text/plain\", \"contentLength\": 447, \"blobType\": \"BlockBlob\", \"url\": \"https://myaccount.blob.core.windows.net/testcontainer/file1.txt\", \"sequencer\": \"00000000000000EB000000000000C65A\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}, " + + "{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testfile.txt\", \"eventType\": \"Microsoft.Storage.BlobDeleted\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}, " + + "{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testfile.txt\", \"eventType\": \"Microsoft.Storage.BlobDeleted\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.AreEqual(3, events.Length); + Assert.True(events[0].Data is StorageBlobCreatedEventData); + Assert.True(events[1].Data is StorageBlobDeletedEventData); + Assert.True(events[2].Data is StorageBlobDeletedEventData); + StorageBlobDeletedEventData eventData = (StorageBlobDeletedEventData)events[2].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testfile.txt", eventData.Url); + } + + #region Custom event tests + [Test] + public void ConsumeCustomEvents() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": { \"itemSku\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\", \"itemUri\": \"https://rp-eastus2.eventgrid.azure.net:553/eventsubscriptions/estest/validate?id=B2E34264-7D71-453A-B5FB-B62D0FDC85EE&t=2018-04-26T20:30:54.4538837Z&apiVersion=2018-05-01-preview&token=1BNqCxBBSSE9OnNSfZM4%2b5H9zDegKMY6uJ%2fO2DFRkwQ%3d\" }, \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData)); + consumerOptions.DataSerializer = new JsonObjectSerializer( + new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + + Assert.True(events[0].Data is ContosoItemReceivedEventData); + ContosoItemReceivedEventData eventData = (ContosoItemReceivedEventData)events[0].Data; + Assert.AreEqual("512d38b6-c7b8-40c8-89fe-f46f9e9622b6", eventData.ItemSku); + } + + [Test] + public void ConsumeCustomEventWithArrayData() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": [{ \"itemSku\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\", \"itemUri\": \"https://rp-eastus2.eventgrid.azure.net:553\" }], \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData[])); + consumerOptions.DataSerializer = new JsonObjectSerializer( + new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ContosoItemReceivedEventData[]); + ContosoItemReceivedEventData[] eventData = (ContosoItemReceivedEventData[])events[0].Data; + Assert.AreEqual("512d38b6-c7b8-40c8-89fe-f46f9e9622b6", eventData[0].ItemSku); + } + #endregion + + #region Primitive/string data tests + [Test] + public void ConsumeCustomEventWithBooleanData() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": true, \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(bool)); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is bool); + bool eventData = (bool)events[0].Data; + Assert.True(eventData); + } + + [Test] + public void ConsumeCustomEventWithStringData() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": \"stringdata\", \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(string)); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is string); + string eventData = (string)events[0].Data; + Assert.AreEqual("stringdata", eventData); + } + #endregion + + #region Testing custom events with no mappings added + [Test] + public void ConsumeCustomEventWithNoMappingAndObjectData() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": [{ \"itemSku\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\", \"itemUri\": \"https://rp-eastus2.eventgrid.azure.net:553\" }], \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(); + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + if (events[0].Data is BinaryData) // test if no custom mapping was added + { + BinaryData binaryData = (BinaryData)events[0].Data; + ContosoItemReceivedEventData[] data = binaryData.Deserialize( + new JsonObjectSerializer( + new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + })); + + Assert.AreEqual("512d38b6-c7b8-40c8-89fe-f46f9e9622b6", data[0].ItemSku); + } + } + + [Test] + public void ConsumeCustomEventWithNoMappingAndStringData() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": \"stringdata\", \"eventType\": \"Contoso.Items.ItemReceived\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(); + + EventGridEvent[] events = eventGridConsumer2.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is string); + string eventData = (string)events[0].Data; + Assert.AreEqual("stringdata", eventData); + } + #endregion + + #region ContainerRegistry events + [Test] + public void ConsumeContainerRegistryImagePushedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.ContainerRegistry/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.ContainerRegistry.ImagePushed\", \"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"id\":\"eventID\",\"timestamp\":\"2018-06-20T12:00:33.6125843-07:00\",\"action\":\"testaction\",\"target\":{\"mediaType\":\"test\",\"size\":20,\"digest\":\"digest1\",\"length\":20,\"repository\":\"test\",\"url\":\"url1\",\"tag\":\"test\"},\"request\":{\"id\":\"id\",\"addr\":\"127.0.0.1\",\"host\":\"test\",\"method\":\"method1\",\"useragent\":\"useragent1\"},\"actor\":{\"name\":\"testactor\"},\"source\":{\"addr\":\"127.0.0.1\",\"instanceID\":\"id\"}}, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ContainerRegistryImagePushedEventData); + ContainerRegistryImagePushedEventData eventData = (ContainerRegistryImagePushedEventData)events[0].Data; + Assert.AreEqual("127.0.0.1", eventData.Request.Addr); + } + + [Test] + public void ConsumeContainerRegistryImageDeletedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.ContainerRegistry/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.ContainerRegistry.ImageDeleted\", \"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"id\":\"eventID\",\"timestamp\":\"2018-06-20T12:00:33.6125843-07:00\",\"action\":\"testaction\",\"target\":{\"mediaType\":\"test\",\"size\":20,\"digest\":\"digest1\",\"length\":20,\"repository\":\"test\",\"url\":\"url1\",\"tag\":\"test\"},\"request\":{\"id\":\"id\",\"addr\":\"127.0.0.1\",\"host\":\"test\",\"method\":\"method1\",\"useragent\":\"useragent1\"},\"actor\":{\"name\":\"testactor\"},\"source\":{\"addr\":\"127.0.0.1\",\"instanceID\":\"id\"}}, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ContainerRegistryImageDeletedEventData); + ContainerRegistryImageDeletedEventData eventData = (ContainerRegistryImageDeletedEventData)events[0].Data; + Assert.AreEqual("testactor", eventData.Actor.Name); + } + + [Test] + public void ConsumeContainerRegistryChartDeletedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.ContainerRegistry/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.ContainerRegistry.ChartDeleted\", \"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"id\":\"id\",\"timestamp\":\"2018-06-20T12:00:33.6125843-07:00\",\"action\":\"action1\",\"target\":{\"mediaType\":\"mediatype1\",\"size\":20,\"digest\":\"digest1\",\"repository\":null,\"tag\":null,\"name\":\"name1\",\"version\":null}}, \"dataVersion\":\"\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ContainerRegistryChartDeletedEventData); + ContainerRegistryChartDeletedEventData eventData = (ContainerRegistryChartDeletedEventData)events[0].Data; + Assert.AreEqual("mediatype1", eventData.Target.MediaType); + } + + [Test] + public void ConsumeContainerRegistryChartPushedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.ContainerRegistry/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.ContainerRegistry.ChartPushed\", \"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"id\":\"id\",\"timestamp\":\"2018-06-20T12:00:33.6125843-07:00\",\"action\":\"action1\",\"target\":{\"mediaType\":\"mediatype1\",\"size\":40,\"digest\":\"digest1\",\"repository\":null,\"tag\":null,\"name\":\"name1\",\"version\":null}}, \"dataVersion\":\"\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ContainerRegistryChartPushedEventData); + ContainerRegistryChartPushedEventData eventData = (ContainerRegistryChartPushedEventData)events[0].Data; + Assert.AreEqual("mediatype1", eventData.Target.MediaType); + } + #endregion + + #region IoTHub Device events + [Test] + public void ConsumeIoTHubDeviceCreatedEvent() + { + string requestContent = "[{ \"id\": \"2da5e9b4-4e38-04c1-cc58-9da0b37230c0\", \"topic\": \"/SUBSCRIPTIONS/BDF55CDD-8DAB-4CF4-9B2F-C21E8A780472/RESOURCEGROUPS/EGTESTRG/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/EGTESTHUB1\", \"subject\": \"devices/48e44e11-1437-4907-83b1-4a8d7e89859e\", \"eventType\": \"Microsoft.Devices.DeviceCreated\", \"eventTime\": \"2018-07-03T23:20:07.6532054Z\", \"data\": { \"twin\": { \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\", \"etag\": \"AAAAAAAAAAE=\", \"deviceEtag\": null, \"status\": \"enabled\", \"statusUpdateTime\": \"0001-01-01T00:00:00\", \"connectionState\": \"Disconnected\", \"lastActivityTime\": \"0001-01-01T00:00:00\", \"cloudToDeviceMessageCount\": 0, \"authenticationType\": \"sas\", \"x509Thumbprint\": { \"primaryThumbprint\": null, \"secondaryThumbprint\": null }, \"version\": 2, \"properties\": { \"desired\": { \"$metadata\": { \"$lastUpdated\": \"2018-07-03T23:20:07.6532054Z\" }, \"$version\": 1 }, \"reported\": { \"$metadata\": { \"$lastUpdated\": \"2018-07-03T23:20:07.6532054Z\" }, \"$version\": 1 } } }, \"hubName\": \"EGTESTHUB1\", \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is IotHubDeviceCreatedEventData); + IotHubDeviceCreatedEventData eventData = (IotHubDeviceCreatedEventData)events[0].Data; + Assert.AreEqual("enabled", eventData.Twin.Status); + } + + [Test] + public void ConsumeIoTHubDeviceDeletedEvent() + { + string requestContent = "[ { \"id\": \"aaaf95c6-ed99-b307-e321-81d8e4f731a6\", \"topic\": \"/SUBSCRIPTIONS/BDF55CDD-8DAB-4CF4-9B2F-C21E8A780472/RESOURCEGROUPS/EGTESTRG/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/EGTESTHUB1\", \"subject\": \"devices/48e44e11-1437-4907-83b1-4a8d7e89859e\", \"eventType\": \"Microsoft.Devices.DeviceDeleted\", \"eventTime\": \"2018-07-03T23:21:33.2753956Z\", \"data\": { \"twin\": { \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\", \"etag\": \"AAAAAAAAAAI=\", \"deviceEtag\": null, \"status\": \"enabled\", \"statusUpdateTime\": \"0001-01-01T00:00:00\", \"connectionState\": \"Disconnected\", \"lastActivityTime\": \"0001-01-01T00:00:00\", \"cloudToDeviceMessageCount\": 0, \"authenticationType\": \"sas\", \"x509Thumbprint\": { \"primaryThumbprint\": null, \"secondaryThumbprint\": null }, \"version\": 3, \"tags\": { \"testKey\": \"testValue\" }, \"properties\": { \"desired\": { \"$metadata\": { \"$lastUpdated\": \"2018-07-03T23:20:07.6532054Z\" }, \"$version\": 1 }, \"reported\": { \"$metadata\": { \"$lastUpdated\": \"2018-07-03T23:20:07.6532054Z\" }, \"$version\": 1 } } }, \"hubName\": \"EGTESTHUB1\", \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is IotHubDeviceDeletedEventData); + IotHubDeviceDeletedEventData eventData = (IotHubDeviceDeletedEventData)events[0].Data; + Assert.AreEqual("AAAAAAAAAAI=", eventData.Twin.Etag); + } + + [Test] + public void ConsumeIoTHubDeviceConnectedEvent() + { + string requestContent = "[ { \"id\": \"fbfd8ee1-cf78-74c6-dbcf-e1c58638ccbd\", \"topic\": \"/SUBSCRIPTIONS/BDF55CDD-8DAB-4CF4-9B2F-C21E8A780472/RESOURCEGROUPS/EGTESTRG/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/EGTESTHUB1\", \"subject\": \"devices/48e44e11-1437-4907-83b1-4a8d7e89859e\", \"eventType\": \"Microsoft.Devices.DeviceConnected\", \"eventTime\": \"2018-07-03T23:20:11.6921933+00:00\", \"data\": { \"deviceConnectionStateEventInfo\": { \"sequenceNumber\": \"000000000000000001D4132452F67CE200000002000000000000000000000001\" }, \"hubName\": \"EGTESTHUB1\", \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\", \"moduleId\": \"\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is IotHubDeviceConnectedEventData); + IotHubDeviceConnectedEventData eventData = (IotHubDeviceConnectedEventData)events[0].Data; + Assert.AreEqual("EGTESTHUB1", eventData.HubName); + } + + [Test] + public void ConsumeIoTHubDeviceDisconnectedEvent() + { + string requestContent = "[ { \"id\": \"877f0b10-a086-98ec-27b8-6ae2dfbf5f67\", \"topic\": \"/SUBSCRIPTIONS/BDF55CDD-8DAB-4CF4-9B2F-C21E8A780472/RESOURCEGROUPS/EGTESTRG/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/EGTESTHUB1\", \"subject\": \"devices/48e44e11-1437-4907-83b1-4a8d7e89859e\", \"eventType\": \"Microsoft.Devices.DeviceDisconnected\", \"eventTime\": \"2018-07-03T23:20:52.646434+00:00\", \"data\": { \"deviceConnectionStateEventInfo\": { \"sequenceNumber\": \"000000000000000001D4132452F67CE200000002000000000000000000000002\" }, \"hubName\": \"EGTESTHUB1\", \"deviceId\": \"48e44e11-1437-4907-83b1-4a8d7e89859e\", \"moduleId\": \"\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is IotHubDeviceDisconnectedEventData); + IotHubDeviceDisconnectedEventData eventData = (IotHubDeviceDisconnectedEventData)events[0].Data; + Assert.AreEqual("000000000000000001D4132452F67CE200000002000000000000000000000002", eventData.DeviceConnectionStateEventInfo.SequenceNumber); + } + + [Test] + public void ConsumeIoTHubDeviceTelemetryEvent() + { + string requestContent = "[{ \"id\": \"877f0b10-a086-98ec-27b8-6ae2dfbf5f67\", \"topic\": \"/SUBSCRIPTIONS/BDF55CDD-8DAB-4CF4-9B2F-C21E8A780472/RESOURCEGROUPS/EGTESTRG/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/EGTESTHUB1\", \"subject\": \"devices/48e44e11-1437-4907-83b1-4a8d7e89859e\", \"eventType\": \"Microsoft.Devices.DeviceTelemetry\", \"eventTime\": \"2018-07-03T23:20:52.646434+00:00\", \"data\": { \"body\": { \"Weather\": { \"Temperature\": 900 }, \"Location\": \"USA\" }, \"properties\": { \"Status\": \"Active\" }, \"systemProperties\": { \"iothub-content-type\": \"application/json\", \"iothub-content-encoding\": \"utf-8\" } }, \"dataVersion\": \"\"} ]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is IotHubDeviceTelemetryEventData); + IotHubDeviceTelemetryEventData eventData = (IotHubDeviceTelemetryEventData)events[0].Data; + Assert.AreEqual("Active", eventData.Properties["Status"]); + } + #endregion + + #region EventGrid events + [Test] + public void ConsumeEventGridSubscriptionValidationEvent() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": { \"validationCode\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\", \"validationUrl\": \"https://rp-eastus2.eventgrid.azure.net:553/eventsubscriptions/estest/validate?id=B2E34264-7D71-453A-B5FB-B62D0FDC85EE&t=2018-04-26T20:30:54.4538837Z&apiVersion=2018-05-01-preview&token=1BNqCxBBSSE9OnNSfZM4%2b5H9zDegKMY6uJ%2fO2DFRkwQ%3d\" }, \"eventType\": \"Microsoft.EventGrid.SubscriptionValidationEvent\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is SubscriptionValidationEventData); + SubscriptionValidationEventData eventData = (SubscriptionValidationEventData)events[0].Data; + Assert.AreEqual("512d38b6-c7b8-40c8-89fe-f46f9e9622b6", eventData.ValidationCode); + } + + [Test] + public void ConsumeEventGridSubscriptionDeletedEvent() + { + string requestContent = "[{ \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\", \"topic\": \"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", \"subject\": \"\", \"data\": { \"eventSubscriptionId\": \"/subscriptions/id/resourceGroups/rg/providers/Microsoft.EventGrid/topics/topic1/providers/Microsoft.EventGrid/eventSubscriptions/eventsubscription1\" }, \"eventType\": \"Microsoft.EventGrid.SubscriptionDeletedEvent\", \"eventTime\": \"2018-01-25T22:12:19.4556811Z\", \"metadataVersion\": \"1\", \"dataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is SubscriptionDeletedEventData); + SubscriptionDeletedEventData eventData = (SubscriptionDeletedEventData)events[0].Data; + Assert.AreEqual("/subscriptions/id/resourceGroups/rg/providers/Microsoft.EventGrid/topics/topic1/providers/Microsoft.EventGrid/eventSubscriptions/eventsubscription1", eventData.EventSubscriptionId); + } + #endregion + + #region Event Hub Events + [Test] + public void ConsumeEventHubCaptureFileCreatedEvent() + { + string requestContent = "[ { \"topic\": \"/subscriptions/guid/resourcegroups/rgDataMigrationSample/providers/Microsoft.EventHub/namespaces/tfdatamigratens\", \"subject\": \"eventhubs/hubdatamigration\", \"eventType\": \"microsoft.EventHUB.CaptureFileCreated\", \"eventTime\": \"2017-08-31T19:12:46.0498024Z\", \"id\": \"14e87d03-6fbf-4bb2-9a21-92bd1281f247\", \"data\": { \"fileUrl\": \"https://tf0831datamigrate.blob.core.windows.net/windturbinecapture/tfdatamigratens/hubdatamigration/1/2017/08/31/19/11/45.avro\", \"fileType\": \"AzureBlockBlob\", \"partitionId\": \"1\", \"sizeInBytes\": 249168, \"eventCount\": 1500, \"firstSequenceNumber\": 2400, \"lastSequenceNumber\": 3899, \"firstEnqueueTime\": \"2017-08-31T19:12:14.674Z\", \"lastEnqueueTime\": \"2017-08-31T19:12:44.309Z\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is EventHubCaptureFileCreatedEventData); + EventHubCaptureFileCreatedEventData eventData = (EventHubCaptureFileCreatedEventData)events[0].Data; + Assert.AreEqual("AzureBlockBlob", eventData.FileType); + } + #endregion + + #region MachineLearningServices events + [Test] + public void ConsumeMachineLearningServicesModelRegisteredEvent() + { + string requestContent = "[{\"topic\":\"/subscriptions/a5fe3bc5-98f0-4c84-affc-a589f54d9b23/resourceGroups/jenns/providers/Microsoft.MachineLearningServices/workspaces/jenns-canary\",\"eventType\":\"Microsoft.MachineLearningServices.ModelRegistered\",\"subject\":\"models/sklearn_regression_model:3\",\"eventTime\":\"2019-10-17T22:23:57.5350054+00:00\",\"id\":\"3b73ee51-bbf4-480d-9112-cfc23b41bfdb\",\"data\":{\"modelName\":\"sklearn_regression_model\",\"modelVersion\":\"3\",\"modelTags\":{\"area\":\"diabetes\",\"type\":\"regression\"},\"modelProperties\":{\"area\":\"test\"}},\"dataVersion\":\"2\",\"metadataVersion\":\"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MachineLearningServicesModelRegisteredEventData); + MachineLearningServicesModelRegisteredEventData eventData = (MachineLearningServicesModelRegisteredEventData)events[0].Data; + Assert.AreEqual("sklearn_regression_model", eventData.ModelName); + Assert.AreEqual("3", eventData.ModelVersion); + + Type type = eventData.ModelTags.GetType(); + Assert.True(eventData.ModelTags is IDictionary); + IDictionary tags = (IDictionary)eventData.ModelTags; + Assert.AreEqual("regression", tags["type"]); + + Assert.True(eventData.ModelProperties is IDictionary); + IDictionary properties = (IDictionary)eventData.ModelProperties; + Assert.AreEqual("test", properties["area"]); + } + + [Test] + public void ConsumeMachineLearningServicesModelDeployedEvent() + { + string requestContent = "[{\"topic\":\"/subscriptions/a5fe3bc5-98f0-4c84-affc-a589f54d9b23/resourceGroups/jenns/providers/Microsoft.MachineLearningServices/workspaces/jenns-canary\",\"eventType\":\"Microsoft.MachineLearningServices.ModelDeployed\",\"subject\":\"endpoints/aciservice1\",\"eventTime\":\"2019-10-23T18:20:08.8824474+00:00\",\"id\":\"40d0b167-be44-477b-9d23-a2befba7cde0\",\"data\":{\"serviceName\":\"aciservice1\",\"serviceComputeType\":\"ACI\",\"serviceTags\":{\"mytag\":\"test tag\"},\"serviceProperties\":{\"myprop\":\"test property\"},\"modelIds\":\"my_first_model:1,my_second_model:1\"},\"dataVersion\":\"2\",\"metadataVersion\":\"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MachineLearningServicesModelDeployedEventData); + MachineLearningServicesModelDeployedEventData eventData = (MachineLearningServicesModelDeployedEventData)events[0].Data; + Assert.AreEqual("aciservice1", eventData.ServiceName); + Assert.AreEqual(2, eventData.ModelIds.Split(',').Length); + } + + [Test] + public void ConsumeMachineLearningServicesRunCompletedEvent() + { + string requestContent = "[{\"topic\":\"/subscriptions/a5fe3bc5-98f0-4c84-affc-a589f54d9b23/resourceGroups/jenns/providers/Microsoft.MachineLearningServices/workspaces/jenns-canary\",\"eventType\":\"Microsoft.MachineLearningServices.RunCompleted\",\"subject\":\"experiments/0fa9dfaa-cba3-4fa7-b590-23e48548f5c1/runs/AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc\",\"eventTime\":\"2019-10-18T19:29:55.8856038+00:00\",\"id\":\"044ac44d-462c-4043-99eb-d9e01dc760ab\",\"data\":{\"experimentId\":\"0fa9dfaa-cba3-4fa7-b590-23e48548f5c1\",\"experimentName\":\"automl-local-regression\",\"runId\":\"AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc\",\"runType\":\"automl\",\"RunTags\":{\"experiment_status\":\"ModelSelection\",\"experiment_status_descr\":\"Beginning model selection.\"},\"runProperties\":{\"num_iterations\":\"10\",\"target\":\"local\"}},\"dataVersion\":\"2\",\"metadataVersion\":\"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MachineLearningServicesRunCompletedEventData); + MachineLearningServicesRunCompletedEventData eventData = (MachineLearningServicesRunCompletedEventData)events[0].Data; + Assert.AreEqual("AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc", eventData.RunId); + Assert.AreEqual("automl-local-regression", eventData.ExperimentName); + } + + [Test] + public void ConsumeMachineLearningServicesRunStatusChangedEvent() + { + string requestContent = "[{\"topic\":\"/subscriptions/a5fe3bc5-98f0-4c84-affc-a589f54d9b23/resourceGroups/jenns/providers/Microsoft.MachineLearningServices/workspaces/jenns-canary\",\"eventType\":\"Microsoft.MachineLearningServices.RunStatusChanged\",\"subject\":\"experiments/0fa9dfaa-cba3-4fa7-b590-23e48548f5c1/runs/AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc\",\"eventTime\":\"2020-03-09T23:53:04.4579724Z\",\"id\":\"aa8cd7df-fe28-5d5d-9b40-3342dbc2a887\",\"data\":{\"runStatus\": \"Running\",\"experimentId\":\"0fa9dfaa-cba3-4fa7-b590-23e48548f5c1\",\"experimentName\":\"automl-local-regression\",\"runId\":\"AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc\",\"runType\":\"automl\",\"runTags\":{\"experiment_status\":\"ModelSelection\",\"experiment_status_descr\":\"Beginning model selection.\"},\"runProperties\":{\"num_iterations\":\"10\",\"target\":\"local\"}},\"dataVersion\":\"2\",\"metadataVersion\":\"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MachineLearningServicesRunStatusChangedEventData); + MachineLearningServicesRunStatusChangedEventData eventData = (MachineLearningServicesRunStatusChangedEventData)events[0].Data; + Assert.AreEqual("AutoML_ad912b2d-6467-4f32-a616-dbe4af6dd8fc", eventData.RunId); + Assert.AreEqual("automl-local-regression", eventData.ExperimentName); + Assert.AreEqual("Running", eventData.RunStatus); + Assert.AreEqual("automl", eventData.RunType); + } + + [Test] + public void ConsumeMachineLearningServicesDatasetDriftDetectedEvent() + { + string requestContent = "[{\"topic\":\"/subscriptions/60582a10-b9fd-49f1-a546-c4194134bba8/resourceGroups/copetersRG/providers/Microsoft.MachineLearningServices/workspaces/driftDemoWS\",\"eventType\":\"Microsoft.MachineLearningServices.DatasetDriftDetected\",\"subject\":\"datadrift/01d29aa4-e6a4-470a-9ef3-66660d21f8ef/run/01d29aa4-e6a4-470a-9ef3-66660d21f8ef_1571590300380\",\"eventTime\":\"2019-10-20T17:08:08.467191+00:00\",\"id\":\"2684de79-b145-4dcf-ad2e-6a1db798585f\",\"data\":{\"dataDriftId\":\"01d29aa4-e6a4-470a-9ef3-66660d21f8ef\",\"dataDriftName\":\"copetersDriftMonitor3\",\"runId\":\"01d29aa4-e6a4-470a-9ef3-66660d21f8ef_1571590300380\",\"baseDatasetId\":\"3c56d136-0f64-4657-a0e8-5162089a88a3\",\"targetDatasetId\":\"d7e74d2e-c972-4266-b5fb-6c9c182d2a74\",\"driftCoefficient\":0.8350349068479208,\"startTime\":\"2019-07-04T00:00:00+00:00\",\"endTime\":\"2019-07-05T00:00:00+00:00\"},\"dataVersion\":\"2\",\"metadataVersion\":\"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MachineLearningServicesDatasetDriftDetectedEventData); + MachineLearningServicesDatasetDriftDetectedEventData eventData = (MachineLearningServicesDatasetDriftDetectedEventData)events[0].Data; + Assert.AreEqual("copetersDriftMonitor3", eventData.DataDriftName); + } + #endregion + + #region Maps events + [Test] + public void ConsumeMapsGeofenceEnteredEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.Maps/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.Maps.GeofenceEntered\",\"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"expiredGeofenceGeometryId\":[\"id1\",\"id2\"],\"geometries\":[{\"deviceId\":\"id1\",\"distance\":1.0,\"geometryId\":\"gid1\",\"nearestLat\":72.4,\"nearestLon\":100.4,\"udId\":\"id22\"}],\"invalidPeriodGeofenceGeometryId\":[\"id1\",\"id2\"],\"isEventPublished\":true}, \"dataVersion\":\"\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MapsGeofenceEnteredEventData); + MapsGeofenceEnteredEventData eventData = (MapsGeofenceEnteredEventData)events[0].Data; + Assert.AreEqual(1.0, eventData.Geometries[0].Distance); + } + + [Test] + public void ConsumeMapsGeofenceExitedEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.Maps/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.Maps.GeofenceExited\",\"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"expiredGeofenceGeometryId\":[\"id1\",\"id2\"],\"geometries\":[{\"deviceId\":\"id1\",\"distance\":1.0,\"geometryId\":\"gid1\",\"nearestLat\":72.4,\"nearestLon\":100.4,\"udId\":\"id22\"}],\"invalidPeriodGeofenceGeometryId\":[\"id1\",\"id2\"],\"isEventPublished\":true}, \"dataVersion\":\"\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MapsGeofenceExitedEventData); + MapsGeofenceExitedEventData eventData = (MapsGeofenceExitedEventData)events[0].Data; + Assert.AreEqual(1.0, eventData.Geometries[0].Distance); + } + + [Test] + public void ConsumeMapsGeofenceResultEvent() + { + string requestContent = "[{ \"id\": \"56afc886-767b-d359-d59e-0da7877166b2\", \"topic\": \"/SUBSCRIPTIONS/ID/RESOURCEGROUPS/rg/PROVIDERS/MICROSOFT.Maps/test1\", \"subject\": \"test1\", \"eventType\": \"Microsoft.Maps.GeofenceResult\",\"eventTime\": \"2018-01-02T19:17:44.4383997Z\", \"data\": {\"expiredGeofenceGeometryId\":[\"id1\",\"id2\"],\"geometries\":[{\"deviceId\":\"id1\",\"distance\":1.0,\"geometryId\":\"gid1\",\"nearestLat\":72.4,\"nearestLon\":100.4,\"udId\":\"id22\"}],\"invalidPeriodGeofenceGeometryId\":[\"id1\",\"id2\"],\"isEventPublished\":true}, \"dataVersion\":\"\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MapsGeofenceResultEventData); + MapsGeofenceResultEventData eventData = (MapsGeofenceResultEventData)events[0].Data; + Assert.AreEqual(1.0, eventData.Geometries[0].Distance); + } + #endregion + + #region Media Services events + [Test] + public void ConsumeMediaMediaJobStateChangeEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobStateChange\", \"eventTime\": \"2018-10-12T15:14:20.2412317\", \"id\": \"341520d0-dac0-4930-97dd-3085538c624f\", \"data\": { \"previousState\": \"Scheduled\", \"state\": \"Processing\", \"correlationData\": {} }, \"dataVersion\": \"2.0\", \"metadataVersion\": \"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobStateChangeEventData); + MediaJobStateChangeEventData eventData = (MediaJobStateChangeEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Scheduled, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Processing, eventData.State); + } + + [Test] + public void ConsumeMediaJobOutputStateChangeEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobOutputStateChange\", \"eventTime\": \"2018-10-12T15:14:17.8962704\", \"id\": \"8d0305c0-28c0-4cc9-b613-776e4dd31e9a\", \"data\": { \"previousState\": \"Scheduled\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"error\": {\"code\":\"ServiceError\", \"message\":\"error message\", \"category\":\"Service\", \"retry\":\"DoNotRetry\", \"details\":[{\"code\":\"code\", \"message\":\"Service Error Message\"}]}, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 0, \"state\": \"Processing\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputStateChangeEventData); + MediaJobOutputStateChangeEventData eventData = (MediaJobOutputStateChangeEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Scheduled, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Processing, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + MediaJobOutputAsset outputAsset = (MediaJobOutputAsset)eventData.Output; + Assert.AreEqual("output-2ac2fe75-6557-4de5-ab25-5713b74a6901", outputAsset.AssetName); + } + + [Test] + public void ConsumeMediaJobScheduledEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobScheduled\", \"eventTime\": \"2018-10-12T15:14:11.3028183\", \"id\": \"9b17dbf0-355d-4fb0-9a73-e76b150858c8\", \"data\": { \"previousState\": \"Queued\", \"state\": \"Scheduled\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobScheduledEventData); + MediaJobScheduledEventData eventData = (MediaJobScheduledEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Queued, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Scheduled, eventData.State); + } + + [Test] + public void ConsumeMediaJobProcessingEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobProcessing\", \"eventTime\": \"2018-10-12T15:14:20.2412317\", \"id\": \"72162c44-c7f4-437a-9592-48b83cec2d18\", \"data\": { \"previousState\": \"Scheduled\", \"state\": \"Processing\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobProcessingEventData); + MediaJobProcessingEventData eventData = (MediaJobProcessingEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Scheduled, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Processing, eventData.State); + } + + [Test] + public void ConsumeMediaJobCancelingEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"eventType\": \"Microsoft.Media.JobCanceling\", \"eventTime\": \"2018-10-12T15:41:50.5513295\", \"id\": \"1f9a488b-abe3-4fca-80b8-aae59bf7f123\", \"data\": { \"previousState\": \"Processing\", \"state\": \"Canceling\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobCancelingEventData); + MediaJobCancelingEventData eventData = (MediaJobCancelingEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Canceling, eventData.State); + } + + [Test] + public void ConsumeMediaJobFinishedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-298338bb-f8d1-4d0f-9fde-544e0ac4d983\", \"eventType\": \"Microsoft.Media.JobFinished\", \"eventTime\": \"2018-10-01T20:58:26.7886175\", \"id\": \"83f8464d-be94-48e5-b67b-46c6199fe28e\", \"data\": { \"outputs\": [ { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-298338bb-f8d1-4d0f-9fde-544e0ac4d983\", \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 100, \"state\": \"Finished\" } ], \"previousState\": \"Processing\", \"state\": \"Finished\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\" }]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobFinishedEventData); + MediaJobFinishedEventData eventData = (MediaJobFinishedEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Finished, eventData.State); + Assert.AreEqual(1, eventData.Outputs.Count); + Assert.True(eventData.Outputs[0] is MediaJobOutputAsset); + MediaJobOutputAsset outputAsset = (MediaJobOutputAsset)eventData.Outputs[0]; + + Assert.AreEqual(MediaJobState.Finished, outputAsset.State); + Assert.Null(outputAsset.Error); + Assert.AreEqual(100, outputAsset.Progress); + Assert.AreEqual("output-298338bb-f8d1-4d0f-9fde-544e0ac4d983", outputAsset.AssetName); + } + + [Test] + public void ConsumeMediaJobCanceledEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"eventType\": \"Microsoft.Media.JobCanceled\", \"eventTime\": \"2018-10-12T15:42:05.6519929\", \"id\": \"3fef7871-f916-4980-8a45-e79a2675808b\", \"data\": { \"outputs\": [ { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"error\": {\"code\":\"ServiceError\", \"message\":\"error message\", \"category\":\"Service\", \"retry\":\"DoNotRetry\", \"details\":[{\"code\":\"code\", \"message\":\"Service Error Message\"}]}, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 83, \"state\": \"Canceled\" } ], \"previousState\": \"Canceling\", \"state\": \"Canceled\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobCanceledEventData); + MediaJobCanceledEventData eventData = (MediaJobCanceledEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Canceling, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Canceled, eventData.State); + Assert.AreEqual(1, eventData.Outputs.Count); + Assert.True(eventData.Outputs[0] is MediaJobOutputAsset); + + MediaJobOutputAsset outputAsset = (MediaJobOutputAsset)eventData.Outputs[0]; + + Assert.AreEqual(MediaJobState.Canceled, outputAsset.State); + Assert.AreNotEqual(100, outputAsset.Progress); + Assert.AreEqual("output-7a8215f9-0f8d-48a6-82ed-1ead772bc221", outputAsset.AssetName); + } + + [Test] + public void ConsumeMediaJobErroredEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobErrored\", \"eventTime\": \"2018-10-12T15:29:20.9954767\", \"id\": \"2749e9cf-4095-4723-9bc5-df8e15289135\", \"data\": { \"outputs\": [ { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"error\": { \"category\": \"Service\", \"code\": \"ServiceError\", \"details\": [ { \"code\": \"Internal\", \"message\": \"Internal error in initializing the task for processing\" } ], \"message\": \"Fatal service error, please contact support.\", \"retry\": \"DoNotRetry\" }, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 83, \"state\": \"Error\" } ], \"previousState\": \"Processing\", \"state\": \"Error\", \"correlationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobErroredEventData); + MediaJobErroredEventData eventData = (MediaJobErroredEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Error, eventData.State); + Assert.AreEqual(1, eventData.Outputs.Count); + Assert.True(eventData.Outputs[0] is MediaJobOutputAsset); + + Assert.AreEqual(MediaJobState.Error, eventData.Outputs[0].State); + Assert.NotNull(eventData.Outputs[0].Error); + Assert.AreEqual(MediaJobErrorCategory.Service, eventData.Outputs[0].Error.Category); + Assert.AreEqual(MediaJobErrorCode.ServiceError, eventData.Outputs[0].Error.Code); + } + + [Test] + public void ConsumeMediaJobOutputCanceledEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"eventType\": \"Microsoft.Media.JobOutputCanceled\", \"eventTime\": \"2018-10-12T15:42:04.949555\", \"id\": \"9297cda2-4a50-4622-a679-c3785d27d512\", \"data\": { \"previousState\": \"Canceling\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"error\": {\"code\":\"ServiceError\", \"message\":\"error message\", \"category\":\"Service\", \"retry\":\"DoNotRetry\", \"details\":[{\"code\":\"code\", \"message\":\"Service Error Message\"}]}, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 83, \"state\": \"Canceled\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputCanceledEventData); + MediaJobOutputCanceledEventData eventData = (MediaJobOutputCanceledEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Canceling, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Canceled, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + } + + [Test] + public void ConsumeMediaJobOutputCancelingEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"eventType\": \"Microsoft.Media.JobOutputCanceling\", \"eventTime\": \"2018-10-12T15:42:04.949555\", \"id\": \"9297cda2-4a50-4622-a679-c3785d27d512\", \"data\": { \"previousState\": \"Processing\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-7a8215f9-0f8d-48a6-82ed-1ead772bc221\", \"error\": { \"category\": \"Service\", \"code\": \"ServiceError\", \"details\": [ { \"code\": \"Internal\", \"message\": \"Internal error in initializing the task for processing\" } ], \"message\": \"Fatal service error, please contact support.\", \"retry\": \"DoNotRetry\" }, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 83, \"state\": \"Canceling\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputCancelingEventData); + MediaJobOutputCancelingEventData eventData = (MediaJobOutputCancelingEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Canceling, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + } + + [Test] + public void ConsumeMediaJobOutputErroredEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobOutputErrored\", \"eventTime\": \"2018-10-12T15:29:20.2621252\", \"id\": \"bc9e6342-f081-49c2-a579-92f506a622c2\", \"data\": { \"previousState\": \"Processing\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"error\": { \"category\": \"Service\", \"code\": \"ServiceError\", \"details\": [ { \"code\": \"Internal\", \"message\": \"Internal error in initializing the task for processing\" } ], \"message\": \"Fatal service error, please contact support.\", \"retry\": \"DoNotRetry\" }, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 83, \"state\": \"Error\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputErroredEventData); + MediaJobOutputErroredEventData eventData = (MediaJobOutputErroredEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Error, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + Assert.NotNull(eventData.Output.Error); + Assert.AreEqual(MediaJobErrorCategory.Service, eventData.Output.Error.Category); + Assert.AreEqual(MediaJobErrorCode.ServiceError, eventData.Output.Error.Code); + } + + [Test] + public void ConsumeMediaJobOutputFinishedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobOutputFinished\", \"eventTime\": \"2018-10-12T15:29:20.2621252\", \"id\": \"bc9e6342-f081-49c2-a579-92f506a622c2\", \"data\": { \"previousState\": \"Processing\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 100, \"state\": \"Finished\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputFinishedEventData); + MediaJobOutputFinishedEventData eventData = (MediaJobOutputFinishedEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Processing, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Finished, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + Assert.AreEqual(100, eventData.Output.Progress); + + MediaJobOutputAsset outputAsset = (MediaJobOutputAsset)eventData.Output; + Assert.AreEqual("output-2ac2fe75-6557-4de5-ab25-5713b74a6901", outputAsset.AssetName); + } + + [Test] + public void ConsumeMediaJobOutputProcessingEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobOutputProcessing\", \"eventTime\": \"2018-10-12T15:14:17.8962704\", \"id\": \"d48eeb0b-2bfa-4265-a2f8-624654c3781c\", \"data\": { \"previousState\": \"Scheduled\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"error\": { \"category\": \"Service\", \"code\": \"ServiceError\", \"details\": [ { \"code\": \"Internal\", \"message\": \"Internal error in initializing the task for processing\" } ], \"message\": \"Fatal service error, please contact support.\", \"retry\": \"DoNotRetry\" }, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 0, \"state\": \"Processing\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputProcessingEventData); + MediaJobOutputProcessingEventData eventData = (MediaJobOutputProcessingEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Scheduled, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Processing, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + } + + [Test] + public void ConsumeMediaJobOutputScheduledEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"eventType\": \"Microsoft.Media.JobOutputScheduled\", \"eventTime\": \"2018-10-12T15:14:11.2244618\", \"id\": \"635ca6ea-5306-4590-b2e1-22f172759336\", \"data\": { \"previousState\": \"Queued\", \"output\": { \"@odata.type\": \"#Microsoft.Media.JobOutputAsset\", \"assetName\": \"output-2ac2fe75-6557-4de5-ab25-5713b74a6901\", \"error\": { \"category\": \"Service\", \"code\": \"ServiceError\", \"details\": [ { \"code\": \"Internal\", \"message\": \"Internal error in initializing the task for processing\" } ], \"message\": \"Fatal service error, please contact support.\", \"retry\": \"DoNotRetry\" }, \"label\": \"VideoAnalyzerPreset_0\", \"progress\": 0, \"state\": \"Scheduled\" }, \"jobCorrelationData\": {} }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputScheduledEventData); + MediaJobOutputScheduledEventData eventData = (MediaJobOutputScheduledEventData)events[0].Data; + Assert.AreEqual(MediaJobState.Queued, eventData.PreviousState); + Assert.AreEqual(MediaJobState.Scheduled, eventData.Output.State); + Assert.True(eventData.Output is MediaJobOutputAsset); + } + + [Test] + public void ConsumeMediaJobOutputProgressEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"transforms/VideoAnalyzerTransform/jobs/job-2ac2fe75-6557-4de5-ab25-5713b74a6981\", \"eventType\": \"Microsoft.Media.JobOutputProgress\", \"eventTime\": \"2018-10-12T15:14:11.2244618\", \"id\": \"635ca6ea-5306-4590-b2e1-22f172759336\", \"data\": { \"jobCorrelationData\": { \"Field1\": \"test1\", \"Field2\": \"test2\" }, \"label\": \"TestLabel\", \"progress\": 50 }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaJobOutputProgressEventData); + MediaJobOutputProgressEventData eventData = (MediaJobOutputProgressEventData)events[0].Data; + Assert.AreEqual("TestLabel", eventData.Label); + Assert.AreEqual(50, eventData.Progress); + Assert.True(eventData.JobCorrelationData.ContainsKey("Field1")); + Assert.AreEqual("test1", eventData.JobCorrelationData["Field1"]); + Assert.True(eventData.JobCorrelationData.ContainsKey("Field2")); + Assert.AreEqual("test2", eventData.JobCorrelationData["Field2"]); + } + + [Test] + public void ConsumeMediaLiveEventEncoderConnectedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventEncoderConnected\", \"eventTime\": \"2018-10-12T15:52:04.2013501\", \"id\": \"3d1f5b26-c466-47e7-927b-900985e0c5d5\", \"data\": { \"ingestUrl\": \"rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59\", \"streamId\": \"Mystream1\", \"encoderIp\": \"\", \"encoderPort\": \"3557\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventEncoderConnectedEventData); + MediaLiveEventEncoderConnectedEventData eventData = (MediaLiveEventEncoderConnectedEventData)events[0].Data; + Assert.AreEqual("rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59", eventData.IngestUrl); + Assert.AreEqual("Mystream1", eventData.StreamId); + Assert.AreEqual("", eventData.EncoderIp); + Assert.AreEqual("3557", eventData.EncoderPort); + } + + + [Test] + public void ConsumeMediaLiveEventConnectionRejectedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventConnectionRejected\", \"eventTime\": \"2018-10-12T15:52:04.2013501\", \"id\": \"3d1f5b26-c466-47e7-927b-900985e0c5d5\", \"data\": { \"ingestUrl\": \"rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59\", \"streamId\": \"Mystream1\", \"encoderIp\": \"\", \"encoderPort\": \"3557\", \"resultCode\": \"MPE_INGEST_CODEC_NOT_SUPPORTED\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventConnectionRejectedEventData); + MediaLiveEventConnectionRejectedEventData eventData = (MediaLiveEventConnectionRejectedEventData)events[0].Data; + } + + [Test] + public void ConsumeMediaLiveEventEncoderDisconnectedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventEncoderDisconnected\", \"eventTime\": \"2018-10-12T15:52:19.8982128\", \"id\": \"e4b55140-42d2-4c24-b08e-9aa12f1587fc\", \"data\": { \"ingestUrl\": \"rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59\", \"streamId\": \"Mystream1\", \"encoderIp\": \"\", \"encoderPort\": \"3557\", \"resultCode\": \"MPE_CLIENT_TERMINATED_SESSION\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventEncoderDisconnectedEventData); + MediaLiveEventEncoderDisconnectedEventData eventData = (MediaLiveEventEncoderDisconnectedEventData)events[0].Data; + Assert.AreEqual("MPE_CLIENT_TERMINATED_SESSION", eventData.ResultCode); + + Assert.AreEqual("rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59", eventData.IngestUrl); + Assert.AreEqual("Mystream1", eventData.StreamId); + Assert.AreEqual("", eventData.EncoderIp); + Assert.AreEqual("3557", eventData.EncoderPort); + } + + [Test] + public void ConsumeMediaLiveEventIncomingStreamReceivedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventIncomingStreamReceived\", \"eventTime\": \"2018-10-12T15:52:16.5726463Z\", \"id\": \"eb688fa1-5a19-4703-8aeb-6a65a09790da\", \"data\": { \"ingestUrl\": \"rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59\", \"trackType\": \"audio\", \"trackName\": \"audio_160000\", \"bitrate\": 160000, \"encoderIp\": \"\", \"encoderPort\": \"3557\", \"timestamp\": \"66\", \"duration\": \"1950\", \"timescale\": \"1000\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventIncomingStreamReceivedEventData); + MediaLiveEventIncomingStreamReceivedEventData eventData = (MediaLiveEventIncomingStreamReceivedEventData)events[0].Data; + + Assert.AreEqual("rtmp://liveevent-ec9d26a8.channel.media.azure.net:1935/live/cb5540b10a5646218c1328be95050c59", eventData.IngestUrl); + Assert.AreEqual("", eventData.EncoderIp); + Assert.AreEqual("3557", eventData.EncoderPort); + Assert.AreEqual("audio", eventData.TrackType); + Assert.AreEqual("audio_160000", eventData.TrackName); + Assert.AreEqual(160000, eventData.Bitrate); + Assert.AreEqual("66", eventData.Timestamp); + Assert.AreEqual("1950", eventData.Duration); + Assert.AreEqual("1000", eventData.Timescale); + } + + + [Test] + public void ConsumeMediaLiveEventIncomingStreamsOutOfSyncEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventIncomingStreamsOutOfSync\", \"eventTime\": \"2018-10-12T15:52:37.3710102\", \"id\": \"d84727e2-d9c0-4a21-a66b-8d23f06b3e06\", \"data\": { \"minLastTimestamp\": \"10999\", \"typeOfStreamWithMinLastTimestamp\": \"video\", \"maxLastTimestamp\": \"100999\", \"typeOfStreamWithMaxLastTimestamp\": \"audio\", \"timescaleOfMinLastTimestamp\": \"1000\", \"timescaleOfMaxLastTimestamp\": \"1000\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventIncomingStreamsOutOfSyncEventData); + MediaLiveEventIncomingStreamsOutOfSyncEventData eventData = (MediaLiveEventIncomingStreamsOutOfSyncEventData)events[0].Data; + Assert.AreEqual("10999", eventData.MinLastTimestamp); + Assert.AreEqual("video", eventData.TypeOfStreamWithMinLastTimestamp); + Assert.AreEqual("100999", eventData.MaxLastTimestamp); + Assert.AreEqual("audio", eventData.TypeOfStreamWithMaxLastTimestamp); + Assert.AreEqual("1000", eventData.TimescaleOfMinLastTimestamp); + Assert.AreEqual("1000", eventData.TimescaleOfMaxLastTimestamp); + } + + [Test] + public void ConsumeMediaLiveEventIncomingVideoStreamsOutOfSyncEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventIncomingVideoStreamsOutOfSync\", \"eventTime\": \"2018-10-12T15:52:37.3710102\", \"id\": \"d84727e2-d9c0-4a21-a66b-8d23f06b3e06\", \"data\": { \"firstTimestamp\": \"10999\", \"firstDuration\": \"2000\", \"secondTimestamp\": \"100999\", \"secondDuration\": \"2000\", \"timescale\": \"1000\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventIncomingVideoStreamsOutOfSyncEventData); + MediaLiveEventIncomingVideoStreamsOutOfSyncEventData eventData = (MediaLiveEventIncomingVideoStreamsOutOfSyncEventData)events[0].Data; + Assert.AreEqual("10999", eventData.FirstTimestamp); + Assert.AreEqual("2000", eventData.FirstDuration); + Assert.AreEqual("100999", eventData.SecondTimestamp); + Assert.AreEqual("2000", eventData.SecondDuration); + Assert.AreEqual("1000", eventData.Timescale); + } + + [Test] + public void ConsumeMediaLiveEventIncomingDataChunkDroppedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventIncomingDataChunkDropped\", \"eventTime\": \"2018-10-12T15:52:37.3710102\", \"id\": \"d84727e2-d9c0-4a21-a66b-8d23f06b3e06\", \"data\": { \"timestamp\": \"8999\", \"trackType\": \"video\", \"trackName\": \"video1\", \"bitrate\": 2500000, \"timescale\": \"1000\", \"resultCode\": \"FragmentDrop_OverlapTimestamp\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventIncomingDataChunkDroppedEventData); + MediaLiveEventIncomingDataChunkDroppedEventData eventData = (MediaLiveEventIncomingDataChunkDroppedEventData)events[0].Data; + Assert.AreEqual("8999", eventData.Timestamp); + Assert.AreEqual("video", eventData.TrackType); + Assert.AreEqual("video1", eventData.TrackName); + Assert.AreEqual(2500000, eventData.Bitrate); + Assert.AreEqual("1000", eventData.Timescale); + Assert.AreEqual("FragmentDrop_OverlapTimestamp", eventData.ResultCode); + } + + [Test] + public void ConsumeMediaLiveEventIngestHeartbeatEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventIngestHeartbeat\", \"eventTime\": \"2018-10-12T15:52:37.3710102\", \"id\": \"d84727e2-d9c0-4a21-a66b-8d23f06b3e06\", \"data\": { \"trackType\": \"video\", \"trackName\": \"video\", \"bitrate\": 2500000, \"incomingBitrate\": 500726, \"lastTimestamp\": \"11999\", \"timescale\": \"1000\", \"overlapCount\": 0, \"discontinuityCount\": 0, \"nonincreasingCount\": 0, \"unexpectedBitrate\": true, \"state\": \"Running\", \"healthy\": false }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventIngestHeartbeatEventData); + MediaLiveEventIngestHeartbeatEventData eventData = (MediaLiveEventIngestHeartbeatEventData)events[0].Data; + Assert.AreEqual("video", eventData.TrackType); + Assert.AreEqual("video", eventData.TrackName); + Assert.AreEqual(2500000, eventData.Bitrate); + Assert.AreEqual(500726, eventData.IncomingBitrate); + Assert.AreEqual("11999", eventData.LastTimestamp); + Assert.AreEqual("1000", eventData.Timescale); + Assert.AreEqual(0, eventData.OverlapCount); + Assert.AreEqual(0, eventData.DiscontinuityCount); + Assert.AreEqual(0, eventData.NonincreasingCount); + Assert.True(eventData.UnexpectedBitrate); + Assert.AreEqual("Running", eventData.State); + Assert.False(eventData.Healthy); + } + + [Test] + public void ConsumeMediaLiveEventTrackDiscontinuityDetectedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/{subscription id}/resourceGroups/{resource group}/providers/Microsoft.Media/mediaservices/{account name}\", \"subject\": \"liveEvent/liveevent-ec9d26a8\", \"eventType\": \"Microsoft.Media.LiveEventTrackDiscontinuityDetected\", \"eventTime\": \"2018-10-12T15:52:37.3710102\", \"id\": \"d84727e2-d9c0-4a21-a66b-8d23f06b3e06\", \"data\": { \"trackType\": \"video\", \"trackName\": \"video\", \"bitrate\": 2500000, \"previousTimestamp\": \"10999\", \"newTimestamp\": \"14999\", \"timescale\": \"1000\", \"discontinuityGap\": \"4000\" }, \"dataVersion\": \"1.0\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is MediaLiveEventTrackDiscontinuityDetectedEventData); + MediaLiveEventTrackDiscontinuityDetectedEventData eventData = (MediaLiveEventTrackDiscontinuityDetectedEventData)events[0].Data; + Assert.AreEqual("video", eventData.TrackType); + Assert.AreEqual("video", eventData.TrackName); + Assert.AreEqual(2500000, eventData.Bitrate); + Assert.AreEqual("10999", eventData.PreviousTimestamp); + Assert.AreEqual("14999", eventData.NewTimestamp); + Assert.AreEqual("1000", eventData.Timescale); + Assert.AreEqual("4000", eventData.DiscontinuityGap); + } + #endregion + + #region Resource Manager (Azure Subscription/Resource Group) events + [Test] + public void ConsumeResourceWriteSuccessEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceWriteSuccess\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceWriteSuccessData); + ResourceWriteSuccessData eventData = (ResourceWriteSuccessData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceWriteFailureEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceWriteFailure\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceWriteFailureData); + ResourceWriteFailureData eventData = (ResourceWriteFailureData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceWriteCancelEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceWriteCancel\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceWriteCancelData); + ResourceWriteCancelData eventData = (ResourceWriteCancelData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceDeleteSuccessEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceDeleteSuccess\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceDeleteSuccessData); + ResourceDeleteSuccessData eventData = (ResourceDeleteSuccessData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceDeleteFailureEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceDeleteFailure\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceDeleteFailureData); + ResourceDeleteFailureData eventData = (ResourceDeleteFailureData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceDeleteCancelEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceDeleteCancel\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceDeleteCancelData); + ResourceDeleteCancelData eventData = (ResourceDeleteCancelData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceActionSuccessEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceActionSuccess\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceActionSuccessData); + ResourceActionSuccessData eventData = (ResourceActionSuccessData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceActionFailureEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceActionFailure\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceActionFailureData); + ResourceActionFailureData eventData = (ResourceActionFailureData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + + [Test] + public void ConsumeResourceActionCancelEvent() + { + string requestContent = "[ { \"topic\":\"/subscriptions/{subscription-id}\", \"subject\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"eventType\":\"Microsoft.Resources.ResourceActionCancel\", \"eventTime\":\"2017-08-16T03:54:38.2696833Z\", \"id\":\"25b3b0d0-d79b-44d5-9963-440d4e6a9bba\", \"data\": { \"authorization\":\"{azure_resource_manager_authorizations}\", \"claims\":\"{azure_resource_manager_claims}\", \"correlationId\":\"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6\", \"httpRequest\":\"{request-operation}\", \"resourceProvider\":\"Microsoft.EventGrid\", \"resourceUri\":\"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501\", \"operationName\":\"Microsoft.EventGrid/eventSubscriptions/write\", \"status\":\"Succeeded\", \"subscriptionId\":\"{subscription-id}\", \"tenantId\":\"72f988bf-86f1-41af-91ab-2d7cd011db47\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\" }]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ResourceActionCancelData); + ResourceActionCancelData eventData = (ResourceActionCancelData)events[0].Data; + Assert.AreEqual("72f988bf-86f1-41af-91ab-2d7cd011db47", eventData.TenantId); + } + #endregion + + #region ServiceBus events + [Test] + public void ConsumeServiceBusActiveMessagesAvailableWithNoListenersEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourcegroups/rg/providers/Microsoft.ServiceBus/namespaces/testns1\", \"subject\": \"topics/topic1/subscriptions/sub1\", \"eventType\": \"Microsoft.ServiceBus.ActiveMessagesAvailableWithNoListeners\", \"eventTime\": \"2018-02-14T05:12:53.4133526Z\", \"id\": \"dede87b0-3656-419c-acaf-70c95ddc60f5\", \"data\": { \"namespaceName\": \"testns1\", \"requestUri\": \"https://testns1.servicebus.windows.net/t1/subscriptions/sub1/messages/head\", \"entityType\": \"subscriber\", \"queueName\": \"queue1\", \"topicName\": \"topic1\", \"subscriptionName\": \"sub1\" }, \"dataVersion\": \"1\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ServiceBusActiveMessagesAvailableWithNoListenersEventData); + ServiceBusActiveMessagesAvailableWithNoListenersEventData eventData = (ServiceBusActiveMessagesAvailableWithNoListenersEventData)events[0].Data; + Assert.AreEqual("testns1", eventData.NamespaceName); + } + + [Test] + public void ConsumeServiceBusDeadletterMessagesAvailableWithNoListenersEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourcegroups/rg/providers/Microsoft.ServiceBus/namespaces/testns1\", \"subject\": \"topics/topic1/subscriptions/sub1\", \"eventType\": \"Microsoft.ServiceBus.DeadletterMessagesAvailableWithNoListener\", \"eventTime\": \"2018-02-14T05:12:53.4133526Z\", \"id\": \"dede87b0-3656-419c-acaf-70c95ddc60f5\", \"data\": { \"namespaceName\": \"testns1\", \"requestUri\": \"https://testns1.servicebus.windows.net/t1/subscriptions/sub1/messages/head\", \"entityType\": \"subscriber\", \"queueName\": \"queue1\", \"topicName\": \"topic1\", \"subscriptionName\": \"sub1\" }, \"dataVersion\": \"1\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is ServiceBusDeadletterMessagesAvailableWithNoListenersEventData); + ServiceBusDeadletterMessagesAvailableWithNoListenersEventData eventData = (ServiceBusDeadletterMessagesAvailableWithNoListenersEventData)events[0].Data; + Assert.AreEqual("testns1", eventData.NamespaceName); + } + #endregion + + #region Storage events + [Test] + public void ConsumeStorageBlobCreatedEvent() + { + string requestContent = "[ { \"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Storage/storageAccounts/myaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/file1.txt\", \"eventType\": \"Microsoft.Storage.BlobCreated\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\", \"id\": \"602a88ef-0001-00e6-1233-1646070610ea\", \"data\": { \"api\": \"PutBlockList\", \"clientRequestId\": \"799304a4-bbc5-45b6-9849-ec2c66be800a\", \"requestId\": \"602a88ef-0001-00e6-1233-164607000000\", \"eTag\": \"0x8D4E44A24ABE7F1\", \"contentType\": \"text/plain\", \"contentLength\": 447, \"blobType\": \"BlockBlob\", \"url\": \"https://myaccount.blob.core.windows.net/testcontainer/file1.txt\", \"sequencer\": \"00000000000000EB000000000000C65A\" }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageBlobCreatedEventData); + StorageBlobCreatedEventData eventData = (StorageBlobCreatedEventData)events[0].Data; + Assert.AreEqual("https://myaccount.blob.core.windows.net/testcontainer/file1.txt", eventData.Url); + } + + [Test] + public void ConsumeStorageBlobDeletedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testfile.txt\", \"eventType\": \"Microsoft.Storage.BlobDeleted\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageBlobDeletedEventData); + StorageBlobDeletedEventData eventData = (StorageBlobDeletedEventData)events[0].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testfile.txt", eventData.Url); + } + + [Test] + public void ConsumeStorageBlobRenamedEvent() + { + string requestContent = "[ { \"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Storage/storageAccounts/myaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testfile.txt\", \"eventType\": \"Microsoft.Storage.BlobRenamed\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\", \"id\": \"602a88ef-0001-00e6-1233-1646070610ea\", \"data\": { \"api\": \"RenameFile\", \"clientRequestId\": \"799304a4-bbc5-45b6-9849-ec2c66be800a\", \"requestId\": \"602a88ef-0001-00e6-1233-164607000000\", \"eTag\": \"0x8D4E44A24ABE7F1\", \"destinationUrl\": \"https://myaccount.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"00000000000000EB000000000000C65A\" }, \"dataVersion\": \"1\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageBlobRenamedEventData); + StorageBlobRenamedEventData eventData = (StorageBlobRenamedEventData)events[0].Data; + Assert.AreEqual("https://myaccount.blob.core.windows.net/testcontainer/testfile.txt", eventData.DestinationUrl); + } + + [Test] + public void ConsumeStorageDirectoryCreatedEvent() + { + string requestContent = "[ { \"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Storage/storageAccounts/myaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testDir\", \"eventType\": \"Microsoft.Storage.DirectoryCreated\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\", \"id\": \"602a88ef-0001-00e6-1233-1646070610ea\", \"data\": { \"api\": \"CreateDirectory\", \"clientRequestId\": \"799304a4-bbc5-45b6-9849-ec2c66be800a\", \"requestId\": \"602a88ef-0001-00e6-1233-164607000000\", \"eTag\": \"0x8D4E44A24ABE7F1\", \"url\": \"https://myaccount.blob.core.windows.net/testcontainer/testDir\", \"sequencer\": \"00000000000000EB000000000000C65A\" }, \"dataVersion\": \"2\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageDirectoryCreatedEventData); + StorageDirectoryCreatedEventData eventData = (StorageDirectoryCreatedEventData)events[0].Data; + Assert.AreEqual("https://myaccount.blob.core.windows.net/testcontainer/testDir", eventData.Url); + } + + [Test] + public void ConsumeStorageDirectoryDeletedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testDir\", \"eventType\": \"Microsoft.Storage.DirectoryDeleted\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"DeleteDirectory\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testDir\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"1\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageDirectoryDeletedEventData); + StorageDirectoryDeletedEventData eventData = (StorageDirectoryDeletedEventData)events[0].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testDir", eventData.Url); + } + + [Test] + public void ConsumeStorageDirectoryRenamedEvent() + { + string requestContent = "[{ \"topic\": \"/subscriptions/id/resourceGroups/Storage/providers/Microsoft.Storage/storageAccounts/xstoretestaccount\", \"subject\": \"/blobServices/default/containers/testcontainer/blobs/testDir\", \"eventType\": \"Microsoft.Storage.DirectoryRenamed\", \"eventTime\": \"2017-11-07T20:09:22.5674003Z\", \"id\": \"4c2359fe-001e-00ba-0e04-58586806d298\", \"data\": { \"api\": \"RenameDirectory\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"destinationUrl\": \"https://example.blob.core.windows.net/testcontainer/testDir\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"dataVersion\": \"1\", \"metadataVersion\": \"1\"}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageDirectoryRenamedEventData); + StorageDirectoryRenamedEventData eventData = (StorageDirectoryRenamedEventData)events[0].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testDir", eventData.DestinationUrl); + } + #endregion + + #region App Service events + [Test] + public void ConsumeWebAppUpdatedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.AppUpdated\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebAppUpdatedEventData); + WebAppUpdatedEventData eventData = (WebAppUpdatedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebBackupOperationStartedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.BackupOperationStarted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebBackupOperationStartedEventData); + WebBackupOperationStartedEventData eventData = (WebBackupOperationStartedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebBackupOperationCompletedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.BackupOperationCompleted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebBackupOperationCompletedEventData); + WebBackupOperationCompletedEventData eventData = (WebBackupOperationCompletedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebBackupOperationFailedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.BackupOperationFailed\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebBackupOperationFailedEventData); + WebBackupOperationFailedEventData eventData = (WebBackupOperationFailedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebRestoreOperationStartedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.RestoreOperationStarted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebRestoreOperationStartedEventData); + WebRestoreOperationStartedEventData eventData = (WebRestoreOperationStartedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebRestoreOperationCompletedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.RestoreOperationCompleted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebRestoreOperationCompletedEventData); + WebRestoreOperationCompletedEventData eventData = (WebRestoreOperationCompletedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebRestoreOperationFailedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.RestoreOperationFailed\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebRestoreOperationFailedEventData); + WebRestoreOperationFailedEventData eventData = (WebRestoreOperationFailedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebSlotSwapStartedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.SlotSwapStarted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebSlotSwapStartedEventData); + WebSlotSwapStartedEventData eventData = (WebSlotSwapStartedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebSlotSwapCompletedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.SlotSwapCompleted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebSlotSwapCompletedEventData); + WebSlotSwapCompletedEventData eventData = (WebSlotSwapCompletedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebSlotSwapFailedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.SlotSwapFailed\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebSlotSwapFailedEventData); + WebSlotSwapFailedEventData eventData = (WebSlotSwapFailedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebSlotSwapWithPreviewStartedEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.SlotSwapWithPreviewStarted\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebSlotSwapWithPreviewStartedEventData); + WebSlotSwapWithPreviewStartedEventData eventData = (WebSlotSwapWithPreviewStartedEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebSlotSwapWithPreviewCancelledEvent() + { + string siteName = "testSite01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/sites/testSite01\", \"subject\": \"/Microsoft.Web/sites/testSite01\",\"eventType\": \"Microsoft.Web.SlotSwapWithPreviewCancelled\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appEventTypeDetail\": {{ \"action\": \"Restarted\"}},\"name\": \"{siteName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebSlotSwapWithPreviewCancelledEventData); + WebSlotSwapWithPreviewCancelledEventData eventData = (WebSlotSwapWithPreviewCancelledEventData)events[0].Data; + Assert.AreEqual(siteName, eventData.Name); + } + + [Test] + public void ConsumeWebAppServicePlanUpdatedEvent() + { + string planName = "testPlan01"; + string requestContent = $"[{{\"topic\": \"/subscriptions/319a9601-1ec0-0000-aebc-8fe82724c81e/resourceGroups/testrg/providers/Microsoft.Web/serverfarms/testPlan01\", \"subject\": \"/Microsoft.Web/serverfarms/testPlan01\",\"eventType\": \"Microsoft.Web.AppServicePlanUpdated\", \"eventTime\": \"2017-08-16T01:57:26.005121Z\",\"id\": \"602a88ef-0001-00e6-1233-1646070610ea\",\"data\": {{ \"appServicePlanEventTypeDetail\": {{ \"stampKind\": \"Public\",\"action\": \"Updated\",\"status\": \"Started\" }},\"name\": \"{planName}\",\"clientRequestId\": \"ce636635-2b81-4981-a9d4-cec28fb5b014\",\"correlationRequestId\": \"61baa426-c91f-4e58-b9c6-d3852c4d88d\",\"requestId\": \"0a4d5b5e-7147-482f-8e21-4219aaacf62a\",\"address\": \"/subscriptions/ef90e930-9d7f-4a60-8a99-748e0eea69de/resourcegroups/egcanarytest/providers/Microsoft.Web/sites/egtestapp/restart?api-version=2016-03-01\",\"verb\": \"POST\"}},\"dataVersion\": \"2\",\"metadataVersion\": \"1\"}}]"; + + EventGridEvent[] events = _eventGridConsumer.DeserializeEventGridEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is WebAppServicePlanUpdatedEventData); + WebAppServicePlanUpdatedEventData eventData = (WebAppServicePlanUpdatedEventData)events[0].Data; + Assert.AreEqual(planName, eventData.Name); + } + #endregion + #endregion + + #region CloudEvent tests + [Test] + public void ConsumeStorageBlobDeletedCloudEventWithAdditionalProperties() + { + string requestContent = "[{\"key\": \"value\", \"id\":\"994bc3f8-c90c-6fc3-9e83-6783db2221d5\",\"source\":\"Subject-0\", \"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"brandNewProperty\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } }, \"type\":\"Microsoft.Storage.BlobDeleted\",\"specversion\":\"1.0\"}]"; + + CloudEvent[] events = _eventGridConsumer.DeserializeCloudEvents(requestContent); + + Assert.NotNull(events); + Assert.True(events[0].Data is StorageBlobDeletedEventData); + StorageBlobDeletedEventData eventData = (StorageBlobDeletedEventData)events[0].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testfile.txt", eventData.Url); + } + + [Test] + public void ConsumeCloudEventWithNoData() + { + string requestContent = "[{\"id\":\"994bc3f8-c90c-6fc3-9e83-6783db2221d5\",\"source\":\"Subject-0\",\"specversion\":\"1.0\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData)); + consumerOptions.DataSerializer = new JsonObjectSerializer( + new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + CloudEvent[] events = eventGridConsumer2.DeserializeCloudEvents(requestContent); + + Assert.AreEqual(events[0].Data, null); + Assert.AreEqual(events[0].Type, ""); + } + + [Test] + public void ConsumeCloudEventWithExplicitlyNullData() + { + string requestContent = "[{\"id\":\"994bc3f8-c90c-6fc3-9e83-6783db2221d5\",\"source\":\"Subject-0\", \"data\":null, \"specversion\":\"1.0\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData)); + consumerOptions.DataSerializer = new JsonObjectSerializer( + new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + EventGridConsumer eventGridConsumer2 = new EventGridConsumer(consumerOptions); + + CloudEvent[] events = eventGridConsumer2.DeserializeCloudEvents(requestContent); + + Assert.AreEqual(events[0].Data, null); + Assert.AreEqual(events[0].Type, ""); + } + + [Test] + public void ConsumeCloudEventWithBinaryDataPayload() + { + string requestContent = "[{\"id\":\"994bc3f8-c90c-6fc3-9e83-6783db2221d5\",\"source\":\"Subject-0\", \"data_base64\": \"ZGF0YQ==\", \"type\":\"BinaryDataType\",\"specversion\":\"1.0\"}]"; + + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("BinaryDataType", typeof(byte[])); + CloudEvent[] events = _eventGridConsumer.DeserializeCloudEvents(requestContent); + if (events[0].Data is byte[]) + { + byte[] data = (byte[])events[0].Data; + } + } + + [Test] + public void ConsumeMultipleCloudEventsInSameBatch() + { + string requestContent = "[" + + "{\"id\":\"994bc3f8-c90c-6fc3-9e83-6783db2221d5\",\"source\":\"Subject-0\",\"data\": { \"api\": \"PutBlockList\", \"clientRequestId\": \"799304a4-bbc5-45b6-9849-ec2c66be800a\", \"requestId\": \"602a88ef-0001-00e6-1233-164607000000\", \"eTag\": \"0x8D4E44A24ABE7F1\", \"contentType\": \"text/plain\", \"contentLength\": 447, \"blobType\": \"BlockBlob\", \"url\": \"https://myaccount.blob.core.windows.net/testcontainer/file1.txt\", \"sequencer\": \"00000000000000EB000000000000C65A\" },\"type\":\"Microsoft.Storage.BlobCreated\",\"specversion\":\"1.0\"}," + + "{\"id\":\"2947780a-356b-c5a5-feb4-f5261fb2f155\",\"source\":\"Subject-1\",\"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } },\"type\":\"Microsoft.Storage.BlobDeleted\",\"specversion\":\"1.0\"}," + + "{\"id\":\"cb14e05b-50c6-67dc-cafa-f4bcff3bf520\",\"source\":\"Subject-2\",\"data\": { \"api\": \"DeleteBlob\", \"requestId\": \"4c2359fe-001e-00ba-0e04-585868000000\", \"contentType\": \"text/plain\", \"blobType\": \"BlockBlob\", \"url\": \"https://example.blob.core.windows.net/testcontainer/testfile.txt\", \"sequencer\": \"0000000000000281000000000002F5CA\", \"storageDiagnostics\": { \"batchId\": \"b68529f3-68cd-4744-baa4-3c0498ec19f0\" } },\"type\":\"Microsoft.Storage.BlobDeleted\",\"specversion\":\"1.0\"}]"; + + CloudEvent[] events = _eventGridConsumer.DeserializeCloudEvents(requestContent); + + Assert.NotNull(events); + Assert.AreEqual(3, events.Length); + Assert.True(events[0].Data is StorageBlobCreatedEventData); + Assert.True(events[1].Data is StorageBlobDeletedEventData); + Assert.True(events[2].Data is StorageBlobDeletedEventData); + StorageBlobDeletedEventData eventData = (StorageBlobDeletedEventData)events[2].Data; + Assert.AreEqual("https://example.blob.core.windows.net/testcontainer/testfile.txt", eventData.Url); + } + + [Test] + public void TestCustomEventMappings() + { + EventGridConsumerOptions consumerOptions = new EventGridConsumerOptions(); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemSent", typeof(ContosoItemSentEventData)); + consumerOptions.CustomEventTypeMappings.Add("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData)); + + Assert.True(consumerOptions.CustomEventTypeMappings.TryGetValue("Contoso.Items.ItemSent", out Type retrievedType)); + Assert.AreEqual(typeof(ContosoItemSentEventData), retrievedType); + + Assert.True(consumerOptions.CustomEventTypeMappings.TryGetValue("Contoso.Items.ItemReceived", out retrievedType)); + Assert.AreEqual(typeof(ContosoItemReceivedEventData), retrievedType); + } + + #endregion + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemReceivedEventData.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemReceivedEventData.cs new file mode 100644 index 000000000000..8fdbeb99236c --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemReceivedEventData.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Messaging.EventGrid.Tests +{ + public class ContosoItemReceivedEventData + { + public string ItemSku { get; set; } + + public string ItemUri { get; set; } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemSentEventData.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemSentEventData.cs new file mode 100644 index 000000000000..bd6c0ef670f6 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ContosoItemSentEventData.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Messaging.EventGrid.Tests +{ + public class ContosoItemSentEventData + { + public ShippingInfo ShippingInfo { get; set; } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientTests.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/DroneShippingInfo.cs similarity index 54% rename from sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientTests.cs rename to sdk/eventgrid/Azure.Messaging.EventGrid/tests/DroneShippingInfo.cs index 4c8d01cb8518..d980f54712c0 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientTests.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/DroneShippingInfo.cs @@ -1,13 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Collections.Generic; -using System.Text; -using Azure.Core.TestFramework; -using NUnit.Framework; - namespace Azure.Messaging.EventGrid.Tests { + public class DroneShippingInfo : ShippingInfo + { + public string DroneId { get; set; } + } } diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientLiveTests.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientLiveTests.cs index 62d8fd0df6c0..7cddfa6e98a7 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientLiveTests.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/EventGridClientLiveTests.cs @@ -34,7 +34,7 @@ public async Task CanPublishEvent() new Uri(TestEnvironment.TopicHost), new AzureKeyCredential(TestEnvironment.TopicKey), options)); - await client.PublishEventsAsync(GetEventsList()); + await client.SendEventsAsync(GetEventsList()); } [Test] @@ -63,7 +63,7 @@ public async Task CanPublishEventWithCustomObjectPayload() }); } - await client.PublishEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -94,7 +94,7 @@ public async Task CanPublishEventToDomain() eventsList.Add(newEGEvent); } - await client.PublishEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -122,7 +122,7 @@ public async Task CanPublishCloudEvent() }); } - await client.PublishCloudEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -180,7 +180,7 @@ public async Task CanPublishCloudEventWithBinaryData() eventsList.Add(cloudEvent); } - await client.PublishCloudEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -209,7 +209,7 @@ public async Task CanPublishCloudEventWithRawJsonData() eventsList.Add(cloudEvent); } - await client.PublishCloudEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -238,7 +238,7 @@ public async Task CanPublishCloudEventWithCustomObjectPayload() eventsList.Add(cloudEvent); } - await client.PublishCloudEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -269,7 +269,7 @@ public async Task CanPublishCloudEventWithExtensionAttributes() eventsList.Add(cloudEvent); } - await client.PublishCloudEventsAsync(eventsList); + await client.SendEventsAsync(eventsList); } [Test] @@ -281,7 +281,7 @@ public async Task CanPublishCustomEvent() new Uri(TestEnvironment.CustomEventTopicHost), new AzureKeyCredential(TestEnvironment.CustomEventTopicKey), options)); - await client.PublishCustomEventsAsync(GetCustomEventsList()); + await client.SendEventsAsync(GetCustomEventsList()); } [Test] @@ -297,14 +297,14 @@ public async Task CanPublishEventUsingSAS() new Uri(TestEnvironment.TopicHost), new EventGridSharedAccessSignatureCredential(sasToken), Recording.InstrumentClientOptions(new EventGridPublisherClientOptions()))); - await sasTokenClient.PublishEventsAsync(GetEventsList()); + await sasTokenClient.SendEventsAsync(GetEventsList()); } [Test] public async Task CustomizeSerializedJSONPropertiesToCamelCase() { EventGridPublisherClientOptions options = Recording.InstrumentClientOptions(new EventGridPublisherClientOptions()); - options.Serializer = new JsonObjectSerializer( + options.DataSerializer = new JsonObjectSerializer( new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase @@ -315,7 +315,7 @@ public async Task CustomizeSerializedJSONPropertiesToCamelCase() new Uri(TestEnvironment.CustomEventTopicHost), new AzureKeyCredential(TestEnvironment.CustomEventTopicKey), options)); - await client.PublishCustomEventsAsync(GetCustomEventsList()); + await client.SendEventsAsync(GetCustomEventsList()); } private IList GetEventsList() diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/RocketShippingInfo.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/RocketShippingInfo.cs new file mode 100644 index 000000000000..73971d5ef10d --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/RocketShippingInfo.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Messaging.EventGrid.Tests +{ + + public class RocketShippingInfo : ShippingInfo + { + public int RocketNumber { get; set; } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ShippingInfo.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ShippingInfo.cs new file mode 100644 index 000000000000..a46f19857c99 --- /dev/null +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/ShippingInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Messaging.EventGrid.Tests +{ + + public class ShippingInfo + { + public string ShippingType { get; set; } + + public string ShipmentId { get; set; } + } +} diff --git a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/TestEvent.cs b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/TestEvent.cs index b93f29d8dcbf..8a87c08ca069 100644 --- a/sdk/eventgrid/Azure.Messaging.EventGrid/tests/TestEvent.cs +++ b/sdk/eventgrid/Azure.Messaging.EventGrid/tests/TestEvent.cs @@ -10,7 +10,6 @@ using Azure.Core.TestFramework; using Azure.Messaging.EventGrid; using Azure.Messaging.EventGrid.Models; -using Microsoft.Azure.Management.EventGrid.Models; using NUnit.Framework; namespace Azure.Messaging.EventGrid.Tests