Click here to Skip to main content
15,867,911 members
Articles / Hosted Services / Azure

Azure Event Grid Tester

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
15 Dec 2018CPOL26 min read 23.4K   474   9   6
Design and implementation of small tool for exploring Azure Event Grid model on local machine
This article describes the design and implementation of the small tool for exploring Azure Event Grid model on the local machine.

Note: Article has been updated - see Appendix A - Version 1.1

Contents

Features

  • Exploring Azure Event Grid
  • Cloning any event subscription for localhost tester using a Hybrid Connection and/or ngrok tunnel channel
  • Exploring an event message on localhost
  • Exploring and managing any Event Subscription
  • Firing a Custom Topic
  • Support for event domains, advanced filtering, etc.
  • No SDK needed
  • Multi-tenants usage (opening multiple instances with multiple Azure subscriptions)
  • Azure Event Grid REST API Requests (templates)
  • REST Management APIs with an authorization Bearer token support
  • api-version=2018-09-15-preview

Introduction

The Azure Event Grid represents an eventing Pub/Sub model with pre-built publishers of the event interests in the Azure resources. The following picture shows this model with fundamental entities such as Event Sources, Topics, Event Subscriptions and Event Handlers:

Image 1

Basically, there are event sources with event interests and on the consumer side, there are subscribers that receive these interests. Each subscriber needs to subscribe to the event source for its specific event interest using an event subscription. Based on the subscriptions, the Event Grid service knows what, where and how to deliver the event interest when it occurs. The above picture shows a current version such as 2018-05-01-preview.

The Azure Event Grid is loosely decouple (push) eventing model. It's a PaaS "standalone service" per region as a part of the serverless cloud architecture. Its capability is to deliver millions of events per second to the destination endpoints (Event Sinks).

Image 2

The following picture can simplify this Pub/Sub model, where Publishers are the left side, for emitting events and on the other side are Subscribers for consuming the event messages represented by source event interest, for example: the blob has been created/deleted in the Azure storage.

Image 3

From the logical model point of the view, the Topic represents an input endpoint and the Subscription point represents an output point for this routing model. The following picture shows that:

Image 4

Based on the above description, we can say, that each subscription represents a Logical Connectivity between the event interest (topic) and consumer (subscriber) within the eventing model. The Azure Event Grid limitation for the eventing model is shown in the following screen snippet:

Image 5

Now, if you know a position of the Topic and Subscription in the Azure Event Grid Model, let's describe their connectivity patterns.

Basically, there are two patterns such as Fan-In and Fan-Out.

The following picture shows a Pattern Fan-In, where multiple Topics are aggregated into one Event Sink (subscriber). Note, that the Azure Event Grid model supports only single connectivity model between the Topic and Subscription, in other words, the subscription can only have one input (Topic) and one output (event handler) endpoint.

Image 6

The above Pattern Fan-In is using multiple topics but the same Event Sink (event handler).

The second pattern is the Pattern Fan-Out, where a single Topic is distributed to multiple Event Sinks. In other words, the same input Topic in the subscriptions, but different output endpoint for each destination endpoint (event handler):

Image 7

Note that the above Fan-Out pattern is used for the concept of this Tester tool, where the Tester Subscription with a "localhost event handler" is added to the exploration of the event source topic.

The following picture shows an Azure Event Grid Tester which allows exploring event messages, subscriptions, firing custom topics, etc. from the localhost machine.

Image 8

That's great. Let's describe its concept and design. I am assuming you have some knowledge of the Azure Event Grid.

Concept and Design

The concept of Azure Event Grid Tester is based on cloning an event subscription for a "localhost destination" in the event handler. In other words, for each interested Topic (Subscription), the Tester will create a new or clone subscription for its event message consuming.

Basically, there are two ways how to receive an event message from the Azure Event Grid on-premise local environment, such as using a Tunnel channel or Azure Hybrid Connection. The Azure Event Grid Tester has built-in support for both ways to subscribe an event subscription with these event handlers. Note, the Azure Hybrid Connection is still in the preview version in the time of writing this article and just recently has been added.

The following picture shows a scenario using the free and publicly-available tunnel tool called ngrok.

Image 9

As you can see, between the Azure and Tester on local machine is the ngrok tunnel for handling a secure connectivity via NAT or firewall on the HTTP ports (80, 443). For this tunnel, the ngrok proxy needs to be installed on your local machine for forwarding request to your destination application. The ngrok tunnel is accessible via the public internet address, which is generated by ngrok for you when the tunnel is created.

This ngrok address, for instance: https://e88ab6b4.ngrok.io/AzureEventGridTester is used for WebHook endpoint handler at the Subscription-Tester.

The second way to subscribe the local Tester to the Azure Event Grid topic is using the Azure Relay Hybrid Connection. The Tester has a built-in listener for one selected Hybrid Connection, so via this event handler, we can see any event message from the azure event sources.

The ngrok and Hybrid Connection are fully equal to the Tester features. Notice, that the Hybrid Connection is billable.

As I mentioned earlier, there are two Topic-Subscription patterns. From the Tester point of the view, this pattern is Fan-In, where all event interests are targeted into the Tester subscriber.

Image 10

For existing an event subscription, we can use a Fan-Out pattern with a clone Subscription-Tester.

Image 11

Recently, the preview version added a new feature into the event subscription such as deadLetterDestination property. Today, we can select only one endpointType such as BlobStorage. If the event message fails on delivery to the event subscriber, the message is delivered as a deadLetter message (blob) into the specified container. Note, that this deadLetter container can be an event driven, so the Tester can also see this eventing.

Note, that the limit of the Subscriptions per Topic is 500 per region, which for our Tester is not a critical, besides that, there is a easy way to delete those Tester-Subscriptions after usage.

As a part of the concept, this is how we can manage the Azure Event Grid resources. The solution for this part is done using the REST Management APIs, see the following:

Event Grid REST API

The Azure Event Grid Tester communicates with Azure via the REST Management API calls. The following screen snippet shows an example of the REST API call to obtain list of custom topics created in the Azure Event Grid for specific scope (Azure SubscriptionId).

Image 12

Every REST call to the Management API requires an Authorization Bearer token header. This Bearer token is taken from the Azure Active Directory, see more details later. Once we have a valid Bearer token, the Tester can manage Azure Event Grid resources such as query providers, topics, subscriptions, etc. As a part of the Tester, there is a generic REST-API node in the treeView for REST client requests loaded from the template json file, located in the binaries folder. Note that the name of this template json file is the name of the Azure Subscription. You can customize this template json file based on your needs, the sample of this file is included in the package (rk20180618.json).

The Tester design is oriented around the ResourceGroups node with drilldown to the requested resource. The end of the selection is the Microsoft.EventGrid providers (blue color node) where the event subscriptions are stored. The event messages received by the Tester subscriber will be displayed below the selected provider, like is shown in the following screen snippet:

Image 13

The above example shows that the Tester subscriber received an event message delivered by changes in the resource group (TESTER-74902843) and six event messages from the custom topic rk20180818topic1 (TESTER-49433140). By clicking on the specific message node, we can see the message content, see the following screen snippet:

Image 14

The blue color node such as a Microsoft.EventGrid provider has the following context menu. In this node, we can manage an Event Subscription:

Image 15

OK, it's show time. Let's describe what this tiny tool can do for you. I am assuming you have some prior exposure to the Azure Event Grid.

Usage

Before you start using the Azure Event Grid Tester, I am assuming you have already read Azure Event Grid MSDN articles and you have an active Azure account.

Before you can use this Tester tool, it is necessary to register it within the Azure Active Directory (AAD). The tool cannot establish connection to your Azure account if you don't have it registered.

Register an EventGridTester in your Azure Directory (only first time)

Create your default AAD directory, if you don't have it. Then Add (Register) a new Application, see the following picture:

Image 16

Populate the following properties:

  • Name: EventGridTester
  • Application Type: Native
  • Redirect Uri: https://login.live.com/oauth20_desktop.srf

After pressing the button Create, we can get the ApplicationID for our Tester tool, save it for later step:

Image 17

Now, we have to Add API access, so select the Setting and the following API such as Windows Azure Service Management API:

Image 18

After the above Select and clicking on the Done (next page), we are done with this step. Our Tester has been registered in our AAD with a Management API access, so we can get a Bearer token for Authorization calls.

Adding more users to the AAD, the Tester tool can be used with each user's credential individually.

Step 1. Launch the Azure Event Grid Tester

In this step, when the Tester is launched, the following prompt dialog shows up. This is a warning dialog to notice, that the ngrok tunnel doesn't exist, so press the button OK to continue process.

Image 19

After that, the following form should show up on your screen:

Image 20

As I mentioned earlier, there are two ways how to subscribe the Tester such as using a ngrok tunnel or Hybrid Connection. If your Azure account has already Hybrid Connection or you are going to create one, the next step can be skipped and you may continue with the Step 3.

Step 2. Create the ngrok Tunnel

If you decided to use a ngrok tunnel for connectivity between the Azure and this Tester, the following steps must be completed every time, when your local machine is powered-off or the ngrok proxy has been closed.

First of all, download a ngrok for windows and unzip it in the folder, for instance: c:\\util.

Lunch the ngrok.exe application as an Administrator, see the following screen snippet:

Image 21

2a. Launch the ngrok with a cmd line

From the File menu, select the Ngrok Tunnel and click on the Get cmd line, as is shown on the following screen snippet:

Image 22

Your clipboard contains a ngrok cmd line, so past it to the console program like is shown in the following picture:

Image 23

Run this cmd line and the result is the following:

Image 24

At this moment, the ngrok tunnel between the internet and your local machine has been created. The above screen shows a public internet address of the ngrok tunnel and where the request is forwarded.

2b. Get the Tunnel Address

In this step, we want to get the nqrok tunnel address into the Tester, so click on the Get tunnel address, as is shown in the following picture:

Image 25

After this action, the tunnel host name will be updated with an actual address, such as in this example 206e8394.nqrok.io.

That's all for ngrok tunnel.

Note, when the Tester is reopened and the ngrok proxy is still running, the Tester will automatically get this ngrok tunnel address, so there is no need to follow the Step 2.

Step 3. Open the Azure Hybrid Connection

To perform this step, the Azure Hybrid Connection is required in prior. Let's assume our Azure account has one, so we can continue with the following screen for its opening:

Image 26

Clicking on the Open menu, the following dialog box shows up:

Image 27

From the Azure portal copy and paste the Name and ConnectionString of the Hybrid Connection, then select the row and press the button OK.

At this moment, the listener for this specified Hybrid Connection has been created and opened. The result is logged in the Log panel and the context menu has been changed into the Close action, see the following screen snippet:

Image 28

As you can see, step for opening the Hybrid Connection is very simple and straightforward comparing to the ngrok tunnel channel.

To continue, we need to make one more step and then the Azure Event Grid Tool is ready for usage. We need to sign-in to our Azure account. Ok, let's do it.

Step 4. Login to your Azure Account

Note: Prior to this step and only the first time for logging into your Azure account, go to the binary folder of the Tester assemblies and rename the template (rk20180618) json file with the name of the Azure Subscription.

Go to the File menu and select the Login to Azure Event Grid:

Image 29

Actually, this is a sign-in the Tester tool to your AAD allows to managed the Event Grid resources, etc.

The following dialog box is shown up on your screen:

Image 30

Add the new row with values:

  • Name: Name of the Azure Subscription
  • Id: Azure Subscription ID (guid)
  • ApplicationKey: Registered Application Id from the AAD

Select the row and press the button OK, you will ask for your credential username/password registered in the AAD:

Image 31

Once your credential has been accepted (for 60 minutes), the Tester tool will show up in the AzureEventGrid treeView node, see the next step.

Step 5. Azure Event Grid Tester is Ready for Using

After login the Tester to your Azure account, the Tester is ready for usage. The Azure account (your Azure Subscription) represents a child node of the AzureEventGrid root. Each Azure account in this treeView has three subnodes likes is shown in the following picture:

Image 32

More details about these nodes:

  • REST-API - This node is for general usage of the REST API calls. It's loaded from the template json file located in the binaries folder. I have included in this download a template REST API calls for Azure Event Grid using a Management API. The next picture shows an example of this default template. Basically, this node allows to extend the Tester tool for any REST API calls, for instance: create the Storage account, etc.
  • TesterEventSubscriptions - This node start searching all cloned Tester-Subscriber with a ngrok WebHook endpoint in your resource groups. Note that this process takes some time to finish all searches groups.
  • ResourceGroups - This is a main entry for your Tester usage. Through this node, you can drilldown to the specific resource and provider such as Microsoft.EventGrid. In other words, the ResourceGroup is a root entity for getting an Event Subscription or Custom Topic.

If you renamed the template json file with a name of your Azure Subscription (in this example, the name is rk20180618), then the REST-API node looks like the following:

Image 33

Step 6. Example: Select a Custom Topic Resource

This example demonstrates simulation of the Custom Topic with receiving an event message. So, select the resource group where the specific custom Topic is located, see the following screen snippet:

Image 34

Next, find your resource custom Topic (in this example, the name is rk20180618topic1) and click on the Select Eventing Resource on the context menu on the selected resource group from previously action:

Image 35

Now, we have a Microsoft.EventGrid provider where we can see all Event Subscription. Because this is an end of the drilldown process, the node is blue colored. Also, within this resource, a custom Topic will automatically show a FireTopic node (green color), see the following screen snippet:

Image 36

The above picture shows details about all subscribed Event Subscriptions for this topic. The details are in the datagrid form and for the selected row, we can see event subscription properties in the json formatted text.

Selecting a Fire Topic node, we can see a REST Client with a sample payload for firing a Custom Topic, so press the button SEND in this REST Client:

Image 37

Note: Generating a guid and/or datetime properties every time when the button Send is pressed, use the following substitution:

JavaScript
[
  {
    "id": "$Guid.NewGuid",
    "mySubject": "/myapp/vehicles/motorcycles",
    "myEventType": "recordInserted",
    "eventTime": "$DateTime.UtcNow",
    "data": {
      "make": "Ducati",
      "model": "Monster"
    }
  }
]

What has happened here? The REST call sent the POST request to the custom Topic (see the url address) endpoint. This publisher endpoint will emit an event message for Event Grid delivery to all subscribers on this topic based on their subscription. The following screen snippet shows a new node such as Events with all messages related with this topic:

Image 38

That's great. We can see the full eventing pushed by publisher and received by Tester subscriber on this topic.

Step 7. Event Subscription

In this step, I am going to demonstrate a Create/Clone/Edit/Delete subscription on the selected Topic (Microsoft.EventGrid provider) - blue color node.

The following screen snippet shows a context menu for this node and selected row of the Event Subscription:

Image 39

Basically, here are the following choices for Event Subscription:

Create a new Event Subscription for Tester:

Image 40

Create a clone of selected Event Subscription for Tester:

Image 41

Edit selected Event Subscription:

Image 42

As you can see, the above Event Subscription dialogs are the same with predefined properties and some read-only properties. The Subscriber Type has a special feature for WebHook and Hybrid Connection allows to select a ngrok tunnel address or open a Hybrid Connection.

The response of the REST API call to the Management API is logged into the Log panel.

In addition, the context menu has more actions such as:

  • Delete Subscription: This action shows up at the ngrok or Hybrid Connection for Tester, in other words, you can delete only event subscription related to this Tester.
  • Get SubscriberFullUrl: This action shows up at the WebHook event handler to see a full endpoint url included a query string.
  • Remove: This action will remove a node from the treeView.
  • Refresh: This action will refresh a selected content from the Azure. Note, that this refresh also occurs when the node has been selected.

Example

This example demonstrates delivery a custom topic event with a retry policy and dead-lettering feature. For this example, we need the following:

  • a custom topic with a publisher simulator (FireTopic)
  • event subscription on that custom topic with a retry policy and dead lettering. This subscriber will always fail (code 503)
  • event subscription for storage account where the dead-lettering message is going to be stored

Let's make the above requirements.

The following screen snippet shows our needs on the Azure Event Grid Tester:

Image 43

Clicking on the custom topic event node, we can select for test purpose always error with a code = 503, so if the tester subscriber will receive an event, the response will with the HttpStatus.ServiceUnavailable (503):

Image 44

Now, the event subscriber at the custom topic is the following:

Image 45

As you can see the above, the maxDeliveryAttempts = 3, so we are expecting 3x delivery event message and after that, the message is going to store to the deadletter container as a dead-letter message.

The regulate storage event subscription:

Image 46

Note, that both event handlers are Hybrid Connection, so we have to open them in our tester for receiving events.

Now, clicking on the Fire Topic node, we can simulate a custom topic and see how the retry policy works. After all retry deliveries, you should see the following:

Image 47

As the above picture shows, there are 3 times event messages sent to the Tester Subscriber (#0 0sec, #1 ~8sec, #2 ~38sec). The dead-letter has been sent after ~ 5 minutes. I think, this timeframe does not have to be too long, the 5-10 seconds after the last failed delivery is enough. I am going to ask Microsoft team, why we have to wait for 5 minutes for dead-lettering.

OK, and finally the following screen snippet is the picture of the dead-letter message:

Image 48

Note, that the correlation Id (such as an id generated by custom topic) is part of the message payload. I think, it should be also in the blob metadata and/or in the blob pathname.

Anyway, that is all for this example.

REST-API

The REST-API node allows to send a http(s) request to the url address. The request headers can be as simple as inserting as a name/value pair with a colon delimiter. For instance, content-type:application/json. Each line represents one header, only.

There is one special header such as Authorization header in the request. If this header doesn't exist it, the runtime client proxy will ask a Bearer token from the Azure Active Directory.

Using this REST-API node is very straightforward like another REST tool such as setting the Url, headers, method and clicking on the Send button. The request will return a response status and payload.

As I mentioned earlier, the Azure Management APIs is used for handling an Azure Event Grid resources, so the Azure Event Grid Tester allows to provide these REST API calls with minimum required settings.

Based on the MSDN Document Event Grid REST API each request is described by simple json object, which will be used for creating its tree node in the tester. Each request definition represents one item in the json array. Note, that the name of the json file (where array is stored) must be a name of the Azure Subscription, for instance rk20180618 with an extension json, otherwise the tester will not find it.

The structure of this request template is shown in the following code snippet:

JavaScript
[
  {
    "category": "CustomTopic/EventPublisher",
    "name": "RegenerateKey",
    "method": "POST",
    "url": "/resourceGroups/rk2018-tester/providers/
    Microsoft.EventGrid/topics/testerTopic/regenerateKey?api-version=2018-05-01-preview",
    "headers": "content-type:application/json | myHeader:abcd",
    "payload": {
      "keyName": "key2"
    },
    "description": null
  },
  { 
    ...
  }
]

As you can see the above code, the headers splitter is character pipe (|) and the url address can have only path and query, the protocol with the domain will be added by tester during the runtime such as:

JavaScript
https://management.azure.com/subscriptions/{subscriptionId}

If the url address contains a full address (protocol, domain, etc.), the tester will accept it without any modification.

Note: The article download contains sample of the REST-API templates under the name rk20180618.json, so please rename it based on your Azure Subscription name. When the Tester tool is used for multiple Azure accounts, each Azure Subscription will have its own template json file located in the binaries folder.

Example

This example demonstrates how to create a template for REST-API node that creates a new Storage account rk2018stg in the resource group rk20180618resgroup. The following item can be added to the array of the REST API call in the json file:

JavaScript
{
  "category": "Misc",
  "name": "CreateStorageAccount",
  "method": "PUT",
  "url": "/resourceGroups/rk20180618resgroup/providers/
  Microsoft.Storage/storageAccounts/rk2018stg?api-version=2017-10-01",
  "headers": null,
  "payload": {
    "sku": {
      "name": "Standard_LRS",
      "tier": "Standard"
      },
    "kind": "StorageV2",
    "location": "westus",
    "tags": {
      },
    "properties": {
      "accessTier": "Cool"
     }
  },
  "description": null
}

To obtain the storage keys, we can create a REST-API template like it is shown in the following code snippet:

JavaScript
{
  "category": "Misc",
  "name": "ListOfKeysForStorageAccount",
  "method": "POST",
  "url": "/resourceGroups/rk20180618resgroup/providers/Microsoft.Storage/
  storageAccounts/rk2018stg/listKeys?api-version=2017-10-01",
  "headers": null,
  "payload": {
  },
  "description": null
}

In the above advanced example, it has been demonstrated how easily we can extend this Tester tool by using a predefined Http template to call it.

Finally, the following screen snippet shows the above templates in the REST-API node under the category Misc.

Image 49

Implementation

First of all, following are the prerequisites:

  • Visual Studio 2017 Version 15.7.5 and up
  • Microsoft Azure account
  • Azure Relay Hybrid Connection
  • Ngrok
  • Connectivity to the Internet
  • Downloading packages (source and/or exe) for this article

I am going to describe few methods, fragments critical for the concept and design of the Azure Event Grid Tester tool. As I have mentioned earlier, the Tester tool communicate with the Azure Management APIs via the REST APIs. Every REST request to the Management API must be authenticated using the Bearer token.

To obtain this Bearer token from the Azure AD is implemented by the following code snippet located in the Form1.cs source file:

JavaScript
private TokenInfo AccessTokenToARM(string clientID, string subscriptionId)
{
    string redirectUri = "https://login.live.com/oauth20_desktop.srf";
    authContext = new AuthenticationContext
    ("https://login.windows.net/common/oauth2/authorize", TokenCache.DefaultShared);
    authContext.ExtendedLifeTimeEnabled = true;
    var ar = authContext.AcquireTokenAsync("https://management.azure.com/", 
    clientID, new Uri(redirectUri), 
    new PlatformParameters(PromptBehavior.SelectAccount)).Result;
    return new TokenInfo() { Token = ar.AccessToken, ExpiresOn = ar.ExpiresOn, 
    ApplicationKey = clientID, SubscriptionId = subscriptionId };
}

The above result is stored in the REST-API node as a tag object TokenInfo:

JavaScript
[Serializable]
[DataContract(Namespace = "urn:rkiss.eventgrid/tester/2018/04")]
public class TokenInfo
{
    [DataMember]
    public string Token { get; set; }
    [DataMember]
    public DateTimeOffset ExpiresOn { get; set; }
    [DataMember]
    public string ApplicationKey { get; set; }
    [DataMember]
    public string SubscriptionId { get; set; }
}

Each time if the REST client (at any treeview node) is going to call an Azure Management API, the following method is performed:

JavaScript
private TokenInfo AccessTokenToARM(TreeNode node, bool regenerate = false)
{
    TokenInfo tokenInfo = null;

    var node1 = this.GetRestApiNode(node);
    if (node1 != null && node1.Tag != null && node1.Tag is TokenInfo)
    {
        tokenInfo = node1.Tag as TokenInfo;
        if (regenerate || tokenInfo.ExpiresOn < DateTimeOffset.UtcNow - 
            TimeSpan.FromMinutes(1))
        {
            tokenInfo = AccessTokenToARM(tokenInfo.ApplicationKey, 
                        tokenInfo.SubscriptionId);
            node1.Tag = tokenInfo;
        }
    }
    return tokenInfo;
}

As the above code snippet shows, the Bearer token is obtained from the TokenCache or retrieved again with asking for a user credential.

One more interesting implementation is a listener for Hybrid Connection. When the Hybrid Connection has been selected from the dialog box, the following task is performed:

JavaScript
ThreadPool.QueueUserWorkItem(delegate (object state)
{
    try
    {
        this.InvokeEx(() => this.openToolStripMenuItem.Enabled = false);

        var listener = new HybridConnectionListener
                       (selectedHybridConnectionInfo.ConnectionString);
        listener.Connecting += (o, hce) =>
        {
            this.InvokeEx(() => this.richTextBoxLog.AppendText
            ($"[{DateTime.Now.ToLocalTime().ToString("yyyy-MM-ddTHH:MM:ss.fff")}] 
            HybridConnection: Connecting, 
            listener:{listener.Address}\r\n", Color.Black));
        };
        listener.Online += (o, hce) =>
        {
            this.InvokeEx(() =>
            {
                this.hybridConnectionToolStripMenuItem.Tag = listener.Address;
                this.hybridConnectionToolStripMenuItem.ToolTipText = 
                                                  listener.Address.ToString();
                this.richTextBoxLog.AppendText($"[{DateTime.Now.ToLocalTime().ToString
                ("yyyy-MM-ddTHH:MM:ss.fff")}] 
                HybridConnection: Online, listener = 
                                          {listener.Address}\r\n", Color.Green);
                this.richTextBoxLog.AppendText($"  {sastoken}\r\n", Color.Gray);
                this.openToolStripMenuItem.Visible = false;
                this.closeToolStripMenuItem.Enabled = true;
                this.closeToolStripMenuItem.Visible = true;
            });
        };
        listener.Offline += (o, hce) =>
        {
            this.InvokeEx(() =>
            {
                this.hybridConnectionToolStripMenuItem.ToolTipText = "";
                this.hybridConnectionToolStripMenuItem.Tag = null;
                this.richTextBoxLog.AppendText($"[{DateTime.Now.ToLocalTime().ToString
                ("yyyy-MM-ddTHH:MM:ss.fff")}] HybridConnection: Offline, 
                listener = {listener.Address}\r\n", Color.Red);
                this.openToolStripMenuItem.Visible = true;
                this.closeToolStripMenuItem.Enabled = false;
            });
        };

        listener.RequestHandler = (context) =>
        {
            try
            {
                if (!context.Request.Headers.AllKeys.Contains("Aeg-Event-Type", 
                StringComparer.OrdinalIgnoreCase) || 
                !string.Equals(context.Request.Headers["Aeg-Event-Type"], 
                "Notification", StringComparison.CurrentCultureIgnoreCase))
                    throw new Exception
                    ("Received message is not for EventGrid subscriber");

                string jsontext = null;
                using (var reader = new StreamReader(context.Request.InputStream))
                {
                    var jtoken = JToken.Parse(reader.ReadToEnd());
                    if (jtoken is JArray)
                        jsontext = jtoken.SingleOrDefault<jtoken>().ToString
                        (Newtonsoft.Json.Formatting.Indented);
                    else if (jtoken is JObject)
                        jsontext = jtoken.ToString
                        (Newtonsoft.Json.Formatting.Indented);
                }

                this.InvokeEx(() => this.AddMessageToTreview
                (JsonConvert.DeserializeObject<eventgridevent>(jsontext), 
                context.Request.Headers, jsontext));
            }
            catch (Exception ex)
            {
                this.InvokeEx(() => this.richTextBoxLog.AppendText
                ($"[{DateTime.Now.ToLocalTime().ToString("yyyy-MM-ddTHH:MM:ss.fff")}] 
                HybridConnection: Message processing failed - 
                                  {ex.InnerMessage()}\r\n", Color.Red));
            }
            finally
            {
                context.Response.StatusCode = HttpStatusCode.NoContent;
                context.Response.Close();
            }
        };

        this.mre.Reset();
        listener.OpenAsync();
        this.mre.WaitOne();
        listener.CloseAsync();
    }
    catch (Exception ex)
    {
        this.InvokeEx(() => this.richTextBoxLog.AppendText
        ($"[{DateTime.Now.ToLocalTime().ToString("yyyy-MM-ddTHH:MM:ss.fff")}] 
        Open HybridConnection failed - {ex.InnerMessage()}\r\n", Color.Red));
    }
    finally
    {
        this.InvokeEx(() => this.hybridConnectionToolStripMenuItem.Tag = null);
        this.InvokeEx(() => this.openToolStripMenuItem.Enabled = true);
        this.InvokeEx(() => this.openToolStripMenuItem.Visible = true);
        this.InvokeEx(() => this.closeToolStripMenuItem.Visible = false);
    }
});

As the above code shows, the background task will create a listener object for Hybrid Connection from its connection string. Then there are three handlers for online/offline and the handler for received request. This handler handles an incoming event message from the Azure Event Grid. The listener is opened until the signal from the ManualResetEvent object such as a Close item on the context menu or closing/exiting the Tester tool.

That's all for the implementation.

Conclusion

This article gives you a tiny tester for Azure Event Grid. It can be your helper while evaluating and exploring the event driven resources in the Azure namespaces. This tiny tool is a next tool from my line such as Azure Service Bus Tester and Azure IoT Hub Tester. I hope you will find it useful.

Appendix A - Version 1.1

This appendix described all new Azure Event Grid version 2018-09-15-preview implemented features in the Azure Event Grid Tester version 1.1.0.0.

A1. Create New Folder for this Version

I do recommend to download it into separate folder and then manually copy/paste all your configurations (such as AzureSubscriptionsDialog and AzureHybridConnectionsDialog) from the previously version. Also, your subscription REST-API json file must be copied into this new folder and updated based on the sample file rk20180618.json if you want to use new templates related with an version 2018-09-15-preview. Note, that this updating process of the custom REST-API templates node is manual and it will require to re-process it for each new version of the Tester.

OK, let's describe what is new in this version.

A2. Event Domains

The Event Domains is a big new feature of this update version 2018-09-15-preview to manage the flow of event domain topics. The previous version allows to handle each custom topic as an event publisher endpoint in the manner one to one, where the subscriber subscribed for existing custom topic in the tightly coupled manner.

In the event domains model, we have a different pattern such as one to many. One event domain endpoint can have multiple dynamically topics, see more details in the document Understand event domains for managing Event Grid topics.

The following picture is from that document and shows a model of the Event Domain:

Image 50

As you can see, the above Event Domain Endpoint is an entry point of the event publisher for distributing event messages within the Event Domain based on the topic property in the event message. The payload of this entry point is an array of the event messages. In the case when the domain topic doesn't exist, the event message is routed to the Event Domain root (no topic), where can be subscribed the domain scope subscriptions. Note, that the above picture doesn't show domain scope subscriptions.

The domain topic is created during its first subscription, that is the major difference to the custom topic. The Event Domain has built-in the capability to route the event message to the Event Domain route if there is no match on topic. This Event Domain Pub/Sub Model enables to subscribe a subscription in loosely coupled manner and dynamically forwarded event message to the specific topic instead of the event domain route. In other words, the event domain root subscriber can easily figured out all existing domain topics.

As I have mentioned, the domain topic is created during its first subscription. In addition, the domain topic is automatically deleted when the last subscription has been deleted. Thanks for this built-in feature for event domain Pub/Sub model, it looks like very useful.

Based on the Event Domain feature, the Tester UI tree node has been extended like is shown on the following screen snippet:

Image 51

As you can see, the above Event Domain (myDomain) has a node for receiving root event messages and a special resource node Topics. We can select a domain topic from the right datagrid and add it to the tree node for its exploration.

A3. Creating a Domain Topic

To create a domain topic using this Tester requires to have in prior an Event Domain (for example myDomain) resource (endpoint). Selecting a New Subscription on this node, the following dialog will show up:

Image 52

As you can see, there is a domainTopic textbox in the above dialog. If this textbox is empty, the subscription will be created for myDomain scope, otherwise the domain topic will be created if this subscription is the first one for this topic.

Note: Using the REST-API EventDomain/EventPublisher node with a template CreateOrUpdateDomain in the Tester, we can create/update any Event Domain resource in our resource group.

A4. Event Subscription Time-to-Live (TTL)

This new feature allows to define time to live duration for subscription. Expiration time will automatically delete a subscription. If this subscription is for domain topic and it is the last one, then also a domain topic will be deleted from the Event Domain scope. Note, that expirationTimeUtc property is updateable property of the subscription, so it can be updated/removed using an Edit Subscription dialog.

A5. Advanced Filtering

The advancedFilters property is an array of the filters allows filtering on envelope properties as well as the first layer of the data payload. The following screen snippet shows this property of the subscription dialog. The syntax format for each filter is used the same as Azure CLI 2.0 programming. The filter delimiter is used character '|' like is highlighted in the picture and the delimiter in the array of values is used a character space ' '. Validation of the filters is during the typing in the tooltip textbox.

Image 53

In advanced filtering, you specify the:

  • key - The field in the event data that you're using for filtering. It can be a number, boolean, or string
  • operator type - The type of comparison
  • value or values - The value or values to compare to the key

Based on the above description, the advancedFilters format is:

C#
key operatorType value/values [ | other filter ...]

The following operatorTypes, keys, values are supported:

OperatorType

The available operators for numbers are:

  • NumberGreaterThan
  • NumberGreaterThanOrEquals
  • NumberLessThan
  • NumberLessThanOrEquals
  • NumberIn
  • NumberNotIn

The available operator for booleans is:

  • BoolEquals

The available operators for strings are:

  • StringContains
  • StringBeginsWith
  • StringEndsWith
  • StringIn
  • StringNotIn

All string comparisons are case-insensitive.

Key

For events in the Event Grid schema, use the following values for the key:

  • Id
  • Topic
  • Subject
  • EventType
  • DataVersion
  • Event data (like Data.key1)

For events in Cloud Events schema, use the following values for the key:

  • EventId
  • Source
  • EventType
  • EventTypeVersion
  • Event data (like Data.key1)

For custom input schema, use the event data fields (like Data.key1).

Values

The values can be:

  • number
  • string
  • boolean
  • array

More details about the advanced filtering for Event Grid Subscription can be found here.

A6. DeadLetterDestination

This feature has been already built in the Tester version 1.0, here I would like to notice, that after with Microsoft Event Grid team discussion, the deadLetterDestination property can not be removed from the event subscription using a REST PATCH call like for property labels, for instance. In other words, once the deadLetterDestination property has been populated (deadLettering is enabled) we cannot turn it off. The enabled deadLettering feature can only be modified. For this issue, we have to make a Clone Subscription and select a None EndpointType in the deadLetterDestination groupbox.

References

History

  • 30th July, 2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Satish Boddu14-May-20 15:07
Satish Boddu14-May-20 15:07 
PraiseFantastic IoT Article Pin
Satish Boddu14-May-20 15:06
Satish Boddu14-May-20 15:06 
PraiseNice Article and Test Tool Pin
abhi07511-Aug-18 15:14
professionalabhi07511-Aug-18 15:14 
Hi Roman

I really appreciate your effort for such a nice article and Test tool. This save developers effort to invest in testing of event grid.

Thanks
Abhishek
GeneralRe: Nice Article and Test Tool Pin
Roman Kiss2-Aug-18 4:15
Roman Kiss2-Aug-18 4:15 
QuestionFan-bloody-tastic Pin
Sacha Barber30-Jul-18 20:28
Sacha Barber30-Jul-18 20:28 
AnswerRe: Fan-bloody-tastic Pin
Roman Kiss31-Jul-18 3:03
Roman Kiss31-Jul-18 3:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.