Quantcast
Channel: Kauffmann @ Dynamics 365 Business Central
Viewing all articles
Browse latest Browse all 118

How to test Business Central webhooks

$
0
0

I don’t think webhooks need an introduction. But for those who are new to the topic, here you can find the official documentation. In short, a webhook is a push notification. The “don’t call us, we’ll call you” scenario of web services. You tell Business Central in what resource you are interested (e.g. Item, Customer, etc.), and they’ll call you in case there is any change. With resource I mean any of the existing APIs, including custom APIs.

Over time, especially during my API courses and also after the session I did at the virtual Directions event about APIs, I’ve got many questions about how to test a Business Central webhook. You want to see the results before you move on with creating an application for it, right? And most people are struggling with the handshake mechanism, how to get that to work when testing the webhooks with a low-code platform or a simple solution?

Ok, here you go. Three different ways to test and work with Business Central webhooks:

Webhook subscriptions flow – Business Central perspective

Before we dive into these options, we need to understand the flow of subscribing to a Business Central Webhook. Let’s say we want to receive notifications in case there is any change in the customers. And we have a service running at a URL https://my.webhooklistener.com to receive those notifications. To tell Business Central to send notifications for the customers resource to this URL, we need to post the following request (leaving out the Authorization header):

POST https://api.businesscentral.dynamics.com/production/api/v2.0/subscriptions
Content-Type: application/json

{
  "notificationUrl": "https://my.webhooklistener.com",
  "resource": "companies(264f8dd2-4a2a-eb11-bb4f-000d3a25f2a9)/customers",
  "clientState": "SuperSecretValue123!"
}

What happens in the background, is that Business Central calls the notification URL passing a validationToken parameter. The service must respond within 5 seconds with status 200 and include the value of the validationToken parameter in the response body. When this handshake mechanism is successfully completed, Business Central will create the webhook subscription. Because a picture is worth a thousand words, here is the flow diagram of this process.

After this, Business Central will start to send notifications to the notification URL when a change occurs in the resource. Until the subscription expires, which happens after 3 days. Then the subscription must be renewed which performs the same handshake verification process.

Webhook subscriptions flow – subscriber perspective

Let’s now look at the subscriber side at the notification URL. The subscriber receives the notifications, checks the clientState value (optionally, but highly recommended), and processes the notifications. However, the very same URL is also called by Business Central during the handshake with the validationToken parameter to verify if the subscriber is actually up and running. And this does not only happen when the subscription is created, it also happens when the subscription is renewed. In other words, the subscriber should first check if the request contains a validationToken parameter and if so, respond with the validation token value in the response body. Let’s look at the flow diagram of the subscriber.

This flow is exactly what we need to implement in order to successfully subscribe to a Business Central webhook. You will recognize this flow in the next examples.

Azure Functions webhook subscriber

With Azure Functions you can easily create a subscriber on Azure and check the results. The simplest way would be to just log the received notifications so you connect to the Azure Functions monitor and watch the messages coming in. Or you go one step further and store the messages on Azure storage, push them to Service Bus queue or store them in a database like Azure SQL or (even better) Cosmos DB. The code below is a simple Azure Function that just logs the received notifications.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Kauffmann.TestBCWebhook
{
    public static class BCWebhook
    {
        const string clientState = "SuperSecretValue123!";
        [FunctionName("BCWebhook")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("BCWebhook function received a request.");

            string validationToken = req.Query["validationToken"];
            if (!String.IsNullOrWhiteSpace(validationToken))
            {
                log.LogInformation($"BCWebhook function processed validationToken: {validationToken}");
                return new OkObjectResult(validationToken);
            }

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

            dynamic data = JsonConvert.DeserializeObject(requestBody);
            if (data.value[0].clientState != clientState)
            {
                log.LogError("Received notification with incorrect clientState:");
                log.LogError(requestBody);
                return new BadRequestResult();
            }

            log.LogInformation("New notification:");
            log.LogInformation(requestBody);
            
            return new AcceptedResult();
        }
    }
}

On a side note, the notification payload contains the clientState value in every single object. The code above only checks it for the first object in the array. It seems overkill to me that it is repeated in every object, it would be sufficient if the clientState value was at the top level, next to value.

The output of the Azure Function looks like this.

If you want to inspect the notification further, then just copy the value and use a JSON viewer. I’m a fan of this online JSON viewer: http://jsonviewer.stack.hu/

Just grab the code and paste it into your Azure Function. Running Azure Functions requires an active Azure Subscription, and to view to the log you need to navigate through the Azure portal. The next option does not require that, it’s completely free and easy to use.

Pipedream

This service can be used to create workflows based on triggers. Workflows are code and you can run them for free (and there are paid plans as well of course). Head over to Pipedream to let them tell what you can do.

I’ve created a workflow that you can just copy and deploy right away. The only thing you need is to create an account, which is free. The workflow consists of a trigger, follows by three steps. Again, you should recognize the steps from the flow diagram for the event subscriber.

How to get your hands on this workflow and make use of it?

Just open this link to get to the shared version of the workflow: https://pipedream.com/@ajkauffmann/business-central-api-webhook-p_3nCDWl

You will get a window that shows the workflow and which has a green button COPY in the right top corner. Click on the COPY button and the workflow will be copied to your Pipedream environment. Then you get a blue DEPLOY button just above the trigger. If you click on it, you will get a unique URL displayed in the trigger that you can use to create a subscriber. You need to be logged in order to deploy a workflow.

Taking the above workflow as an example, you can create a webhook subscription with this call (first time it might run into a timeout!):

POST https://api.businesscentral.dynamics.com/production/api/v2.0/subscriptions
Content-Type: application/json

{
  "notificationUrl": "https://enln9ir1q8i5w5g.m.pipedream.net",
  "resource": "companies(264f8dd2-4a2a-eb11-bb4f-000d3a25f2a9)/customers",
  "clientState": "SuperSecretValue123!"
}

When the workflow is active you will see the events in the left column. These events can be clicked and you can explore the output of every single step in the workflow.

Power Automate Flow

How to receive webhook notifications with Power Automate is a question I see quite often. The handshake mechanism is not supported by the HTTP request trigger, so people are struggling with this getting to work. But by implementing the flow diagram above, it will be no problem. Here is a Flow that handles the validationToken (the handshake) and also checks the clientState.

This Flow is available for download, both for Power Automate and as Logic Apps template. Just download them and import them in your environment. I’ve also submitted it as a template, so hopefully it becomes available soon as a starter template.

Download links:

After the Flow has been activated, you can get the URL from the trigger ‘When a HTTP request is received’. Use this URL to create the subscription. In the Flow history you can view all runs including the data and how it has been processed. If you want to store the notifications, then add it after the step ‘Respond Accepted’ in the ‘If yes’ branch of the step ‘Check clientState’. Pretty simple, isn’t it?

On top of this, it would also be easy to create a custom connector in Power Automate so you can create your own triggers. The only difference with the Flow above would be the trigger, the other steps will be exactly the same. But… you need to keep one thing in mind! Subscriptions expire after three days, you need to renew them before they get expired. That can be done manually of course, which is perfectly fine for testing. For production scenarios you could think of creating a Flow that runs periodically and renews your subscriptions. That should not be very hard to create. However, we are now going beyond the scope of this post which was about testing webhooks.

With this I hope you got some ideas how to easily test your webhooks!


Viewing all articles
Browse latest Browse all 118

Trending Articles