Two-way sync of bookings between Dynamics 365 and Outlook, Part IV – Deleting bookings

In part III we looked at how bookings are synced from Outlook to D365. In this part we’ll look at how an event can be deleted in Outlook after its related booking is deleted in D365. While bookings deleted in Outlook are deleted in D365 with the cloud flow covered in part II, bookings deleted in D365 aren’t that easily reflected in Outlook. This is because the Dataverse trigger used in cloud flows for delete operations only provides the GUID of the row that was deleted. What we need is a row’s pre-image to get the value of the Event id for booking sync column for the booking row that was deleted. With that value an event in Outlook can be deleted.

Getting a row’s pre-image

Before we look at a way of getting a row’s pre-image, I want to point out that if this wasn’t a concept but an actual implementation, I’d leave getting a pre-image to a developer. Getting one doesn’t require any development per se but it does require us to choose between webhooks or plug-ins so we’re easily stepping on a developer’s toes when it comes to the solution’s technical architecture. In this example we’re getting a pre-image by using a webhook. There are plenty of articles in the community about how to register a webhook so all we’ll look at in this blog post are the step and the image for the registered webhook. They’re seen in images 1 and 2 below.

Registering a webhook

For this concept the stage is PreOperation, the webhook executes synchronously, and we’re interested in the pre-image as the message is for delete. I’ve set the image’s parameters to include not only the event id column but also some additional columns for additional scenarios. The endpoint URL for the webhook comes from the HTTP POST URL of a could flow’s When a HTTP request is received trigger. We’ll dissect that flow in the next chapter.

1. Webhook’s step.
2. Webhook’s image.

Deleting event resources with a cloud flow

The webhook that was registered is used to trigger a cloud flow. This flow will run asynchronously but we’ll be able to access the deleted row’s pre-image in the flow. This way we’ll be able to delete an event resource that is related to the deleted booking. Let’s dissect the cloud flow in question.

Trigger and parsing JSON

Getting the JSON just right for a parse JSON action and then extracting the value for d2d_eventidforbookingsync can be a bit challenging so below is the JSON schema used in the parse JSON action. While the method I’ve used might not be beautiful, it gets the job done with fair performance. If you want to explore other options, I suggest you look at parsing XML with an XPath expression. MVP John Liu has written a blog post about it and there’s also a nice video on YouTube by MVP Matt Weston.

{
  "type": "object",
  "properties": {
    "PreEntityImages": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {},
          "value": {
            "type": "object",
            "properties": {
              "Attributes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "key": {},
                    "value": {}
                  },
                  "required": [
                    "key",
                    "value"
                  ]
                }
              },
              "EntityState": {},
              "FormattedValues": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "key": {},
                    "value": {}
                  },
                  "required": [
                    "key",
                    "value"
                  ]
                }
              },
              "Id": {},
              "KeyAttributes": {},
              "LogicalName": {},
              "RelatedEntities": {},
              "RowVersion": {}
            }
          }
        },
        "required": [
          "key",
          "value"
        ]
      }
    }
  }
}
3. Trigger and parse JSON.

Apply to each with filters and deleting the related event

In my approach I’ve used an apply to each loop, followed by a filter array and a condition. The output from the parse JSON action that is used in the apply to each is PreEntityImages. It should be available as dynamic content, when selecting a value for the apply to each loop. Next, a filter array for Attributes is used. When a key is equal to d2d_eventidforbookingsync and the filter array action returns data, an event exists.

4. Apply to each and filtering results.

There are different expressions that can be used to get the value for an event. The expressions I’ve used in the two different compose actions are:
body('Filter_array_for_d2d_eventidforbookingsync')?[0]?['value']
last(body('Filter_array_for_d2d_eventidforbookingsync')).value

Next, another filter array is used so that we’re able to get the related (bookable) resource. When key is equal to resource, we’re able to extract the resource from the filter array with one of the following expressions:
body('Filter_array_for_resource')?[0]?['value']?['id']
last(body('Filter_array_for_resource')).value.id

We can then use a get records action for the Bookable Resource table with an expand query. This way we’ll have the resource’s AAD Object Id, with which we can delete the related event.

5. Deleting the related event.
6. Results in the apply to each loop in a successful flow run.

We’re finally at the end of the 4-part series that covered syncing bookings two-ways between D365 and Outlook. You can get to the previous parts from the following links:
Part I
Part II
Part III

Disclaimer:
All my blog posts reflect my personal opinions and findings unless otherwise stated.