Skip to content

Commit

Permalink
Add migration guide (#19456)
Browse files Browse the repository at this point in the history
* Add migration guide

* Fix deserialization snippet

* relative link

* Apply suggestions from code review

Co-authored-by: Jesse Squire <jesse.squire@gmail.com>

* PR FB

* explicit type

Co-authored-by: Jesse Squire <jesse.squire@gmail.com>
  • Loading branch information
JoshLove-msft and jsquire authored Mar 11, 2021
1 parent 236016b commit dc11d67
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 9 deletions.
5 changes: 3 additions & 2 deletions sdk/eventgrid/Azure.Messaging.EventGrid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,14 @@ Once events are delivered to the event handler, parse the JSON payload into list
Using `EventGridEvent`:
```C# Snippet:EGEventParseJson
// Parse the JSON payload into a list of events
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
```

Using `CloudEvent`:
```C# Snippet:CloudEventParseJson
var bytes = await httpContent.ReadAsByteArrayAsync();
// Parse the JSON payload into a list of events
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
```
From here, one can access the event data by deserializing to a specific type using `GetData<T>()`. Calling `GetData()` will either return the event data wrapped in `BinaryData`, which represents the serialized JSON event data as bytes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ Once events are delivered to the event handler, parse the JSON payload into list
Using `EventGridEvent`:
```C# Snippet:EGEventParseJson
// Parse the JSON payload into a list of events
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
```

Using `CloudEvent`:
```C# Snippet:CloudEventParseJson
var bytes = await httpContent.ReadAsByteArrayAsync();
// Parse the JSON payload into a list of events
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
```

## Deserialize Event Data
Expand Down
200 changes: 200 additions & 0 deletions sdk/eventgrid/Azure.Messaging.EventGrid/src/MigrationGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# Guide for migrating to Azure.Messaging.EventGrid from Microsoft.Azure.EventGrid

This guide is intended to assist in the migration to `Azure.Messaging.EventGrid` from `Microsoft.Azure.EventGrid`. It will focus on side-by-side comparisons for similar operations between the two packages.

We assume that you are familiar with `Microsoft.Azure.EventGrid`. If not, please refer to the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/eventgrid/Azure.Messaging.EventGrid/README.md) for `Azure.Messaging.EventGrid` rather than this guide.

## Table of contents
- [Migration benefits](#migration-benefits)
- [Cross Service SDK improvements](#cross-service-sdk-improvements)
- [New features](#new-features)
- [Important changes](#important-changes)
- [Package names and namespaces](#package-names-and-namespaces)
- [Client naming and constructors](#client-naming-and-constructors)
- [Publishing events to a topic](#publishing-events-to-a-topic)
- [Deserializing events and data](#deserializing-events-and-data)
- [Additional samples](#additional-samples)

## Migration benefits

As Azure has matured and been embraced by a more diverse group of developers, we have been focused on learning the patterns and practices to best support developer productivity and to understand the gaps that the .NET client libraries have.

There were several areas of consistent feedback expressed across the Azure client library ecosystem. One of the most important is that the client libraries for different Azure services have not had a consistent approach to organization, naming, and API structure. Additionally, many developers have felt that the learning curve was difficult, and the APIs did not offer a good, approachable, and consistent onboarding story for those learning Azure or exploring a specific Azure service.

To improve the development experience across Azure services, a set of uniform [design guidelines](https://azure.github.io/azure-sdk/general_introduction.html) was created for all languages to drive a consistent experience with established API patterns for all services. A set of [.NET design guidelines](https://azure.github.io/azure-sdk/dotnet_introduction.html) was also introduced to ensure that .NET clients have a natural and idiomatic feel with respect to the .NET ecosystem. The new Azure.Messaging.EventGrid library follows these guidelines.

### Cross Service SDK improvements

The modern `Azure.Messaging.EventGrid` client library also provides the ability to share in some of the cross-service improvements made to the Azure development experience, such as:

- Using the new Azure.Identity library to share a single authentication approach between clients
- A unified logging and diagnostics pipeline offering a common view of the activities across each of the client libraries

### New features

The new Azure.Messaging.EventGrid library includes several new features:
- Publishing and deserializing [CloudEvents](https://cloudevents.io/) using the Azure.Core `CloudEvent` type
- Methods that allow publishing a single event in addition to lists of events
- Ability to use a custom serializer for the Data of an event
- Ability to publish events with a custom schema
- Generate a shared access signature token using the `EventGridSasBuilder`

## Important changes

### Package names and namespaces

Package names and the namespace root for the modern Azure client libraries for .NET have changed. Each will follow the pattern `Azure.[Area].[Service]` where the legacy clients followed the pattern `Microsoft.Azure.[Service]`. This provides a quick and accessible means to help understand, at a glance, whether you are using the modern or legacy clients.

In the case of Event Grid, the modern client libraries have packages and namespaces that begin with Azure.Messaging.EventGrid and were released beginning with version 4. The legacy client libraries have packages and namespaces that begin with Microsoft.Azure.EventGrid and a version of 3.x.x or below.

### Client naming and constructors

In `Microsoft.Azure.EventGrid`, the `EventGridClient` was instantiated by passing the `TopicCredentials` into the constructor:
```C#
string topicEndpoint = "https://<topic-name>.<region>-1.eventgrid.azure.net/api/events";
string topicKey = "<topic-key>";

TopicCredentials topicCredentials = new TopicCredentials(topicKey);
EventGridClient client = new EventGridClient(topicCredentials);
```

In the `Azure.Messaging.EventGrid` library, the client used to interact with the service was renamed as the `EventGridPublisherClient` to highlight the fact that the client is only used to publish events and does not consume events from the service. The constructor takes the topic endpoint in addition to a credential parameter since a single client will only ever be able to publish to a single topic.
```C# Snippet:CreateClientWithOptions
EventGridPublisherClient client = new EventGridPublisherClient(
new Uri(topicEndpoint),
new AzureKeyCredential(topicAccessKey));
```

### Publishing events to a topic

In the `Microsoft.Azure.EventGrid` library, events were published as shown:
```C#
string topicEndpoint = "https://<topic-name>.<region>-1.eventgrid.azure.net/api/events";
string topicHostname = new Uri(topicEndpoint).Host;

List<EventGridEvent> eventsList = new List<EventGridEvent>();
for (int i = 0; i < 1; i++)
{
eventsList.Add(new EventGridEvent()
{
Id = Guid.NewGuid().ToString(),
EventType = "Contoso.Items.ItemReceivedEvent",
Data = new ContosoItemReceivedEventData()
{
ItemUri = "ContosoSuperItemUri"
},

EventTime = DateTime.Now,
Subject = "Door1",
DataVersion = "2.0"
});
}

await client.PublishEventsAsync(topicHostname, eventsList);
```

In `Azure.Messaging.EventGrid`, when publishing events it is no longer necessary to include the topic host name. Instead, this is passed as part of the `Uri` that is provided to the `EventGridPublisherClient` constructor. The `EventGridEvent` type now has a constructor that includes parameters for all required fields of the event grid schema:
```C# Snippet:SendEGEventsToTopic
// Add EventGridEvents to a list to publish to the topic
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"ExampleEventSubject",
"Example.EventType",
"1.0",
"This is the data for the first event"),
new EventGridEvent(
"ExampleEventSubject",
"Example.EventType",
"1.0",
"This is the data for the second event")
};

// Send the events
await client.SendEventsAsync(eventsList);
```
### Deserializing events and data

In `Microsoft.Azure.EventGrid`, there was the `EventGridSubscriber` type that was used to deserialize events:
```C#
string requestContent = await req.Content.ReadAsStringAsync();

EventGridSubscriber eventGridSubscriber = new EventGridSubscriber();

// Optionally add one or more custom event type mappings
eventGridSubscriber.AddOrUpdateCustomEventMapping("Contoso.Items.ItemReceived", typeof(ContosoItemReceivedEventData));

EventGridEvent[] events = eventGridSubscriber.DeserializeEventGridEvents(requestContent);

foreach (EventGridEvent receivedEvent in events)
{
if (receivedEvent.Data is SubscriptionValidationEventData)
{
SubscriptionValidationEventData eventData = (SubscriptionValidationEventData)receivedEvent.Data;
log.Info($"Got SubscriptionValidation event data, validationCode: {eventData.ValidationCode}, validationUrl: {eventData.ValidationUrl}, topic: {eventGridEvent.Topic}");
// Handle subscription validation
}
else if (receivedEvent.Data is StorageBlobCreatedEventData)
{
StorageBlobCreatedEventData eventData = (StorageBlobCreatedEventData)receivedEvent.Data;
log.Info($"Got BlobCreated event data, blob URI {eventData.Url}");
// Handle StorageBlobCreatedEventData
}
else if (receivedEvent.Data is ContosoItemReceivedEventData)
{
ContosoItemReceivedEventData eventData = (ContosoItemReceivedEventData)receivedEvent.Data;
}
}
```

In the `Azure.Messaging.EventGrid` library, there is no longer a separate type to deserialize events. Instead, you can use static factory methods on `EventGridEvent` to parse into an array of `EventGridEvents`.
```C# Snippet:EGEventParseJson
// Parse the JSON payload into a list of events
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
```

You can then iterate through your events and deserialize the data.
```C# Snippet:DeserializePayloadUsingAsSystemEventData
foreach (EventGridEvent egEvent in egEvents)
{
// If the event is a system event, TryGetSystemEventData will return the deserialized system event
if (egEvent.TryGetSystemEventData(out object systemEvent))
{
switch (systemEvent)
{
case SubscriptionValidationEventData subscriptionValidated:
Console.WriteLine(subscriptionValidated.ValidationCode);
break;
case StorageBlobCreatedEventData blobCreated:
Console.WriteLine(blobCreated.BlobType);
break;
// Handle any other system event type
default:
Console.WriteLine(egEvent.EventType);
// we can get the raw Json for the event using Data
Console.WriteLine(egEvent.Data.ToString());
break;
}
}
else
{
switch (egEvent.EventType)
{
case "MyApp.Models.CustomEventType":
TestPayload deserializedEventData = egEvent.Data.ToObjectFromJson<TestPayload>();
Console.WriteLine(deserializedEventData.Name);
break;
// Handle any other custom event type
default:
Console.Write(egEvent.EventType);
Console.WriteLine(egEvent.Data.ToString());
break;
}
}
}
```

## Additional samples

More examples can be found at:
- [Event Grid samples](https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/eventgrid/Azure.Messaging.EventGrid/samples)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the MIT License.

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Core.Serialization;
using Azure.Core.TestFramework;
using Azure.Messaging.EventGrid.SystemEvents;
Expand All @@ -13,17 +15,18 @@ namespace Azure.Messaging.EventGrid.Tests.Samples
public partial class EventGridSamples : SamplesBase<EventGridTestEnvironment>
{
// Example JSON payloads
private readonly BinaryData jsonPayloadSampleOne = new BinaryData("[{ \"id\": \"2d1781af-3a4c\", \"topic\": \"/examples/test/payload\", \"subject\": \"\", \"data\": { \"Name\": \"example\",\"Age\": 20 },\"eventType\": \"MyApp.Models.CustomEventType\",\"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\"dataVersion\": \"1\"}]");
private readonly string jsonPayloadSampleOne = "[{ \"id\": \"2d1781af-3a4c\", \"topic\": \"/examples/test/payload\", \"subject\": \"\", \"data\": { \"Name\": \"example\",\"Age\": 20 },\"eventType\": \"MyApp.Models.CustomEventType\",\"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\"dataVersion\": \"1\"}]";

private readonly BinaryData jsonPayloadSampleTwo = new BinaryData("[{ \"id\": \"2d1781af-3a4c\", \"source\": \"/examples/test/payload\", \"data\": { \"name\": \"example\",\"age\": 20 },\"type\": \"MyApp.Models.CustomEventType\",\"time\": \"2018-01-25T22:12:19.4556811Z\",\"specversion\": \"1.0\"}]");
private readonly string jsonPayloadSampleTwo = "[{ \"id\": \"2d1781af-3a4c\", \"source\": \"/examples/test/payload\", \"data\": { \"name\": \"example\",\"age\": 20 },\"type\": \"MyApp.Models.CustomEventType\",\"time\": \"2018-01-25T22:12:19.4556811Z\",\"specversion\": \"1.0\"}]";

// This sample demonstrates how to parse EventGridEvents from JSON and access event data using TryGetSystemEventData
[Test]
public void NonGenericReceiveAndDeserializeEventGridEvents()
{
var httpContent = new BinaryData(jsonPayloadSampleOne).ToStream();
#region Snippet:EGEventParseJson
// Parse the JSON payload into a list of events
EventGridEvent[] egEvents = EventGridEvent.ParseMany(jsonPayloadSampleOne);
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(httpContent));
#endregion

// Iterate over each event to access event properties and data
Expand Down Expand Up @@ -70,7 +73,7 @@ public void NonGenericReceiveAndDeserializeEventGridEvents()

// This sample demonstrates how to parse CloudEvents from JSON and access event data using ToObjectFromJson
[Test]
public void GenericReceiveAndDeserializeEventGridEvents()
public async Task GenericReceiveAndDeserializeEventGridEvents()
{
// Example of a custom ObjectSerializer used to deserialize the event payload
JsonObjectSerializer myCustomSerializer = new JsonObjectSerializer(
Expand All @@ -79,9 +82,11 @@ public void GenericReceiveAndDeserializeEventGridEvents()
PropertyNameCaseInsensitive = true
});

var httpContent = new StreamContent(new BinaryData(jsonPayloadSampleTwo).ToStream());
#region Snippet:CloudEventParseJson
var bytes = await httpContent.ReadAsByteArrayAsync();
// Parse the JSON payload into a list of events
CloudEvent[] cloudEvents = CloudEvent.ParseMany(jsonPayloadSampleTwo);
CloudEvent[] cloudEvents = CloudEvent.ParseMany(new BinaryData(bytes));
#endregion

// Iterate over each event to access event properties and data
Expand Down

0 comments on commit dc11d67

Please sign in to comment.