Advanced | Flow of the Week: Selective calendar event sync using Condition advanced editor mode and Boolean expressions

Some of the most popular Flow templates sync calendar events from one service to another (Office 365 to Google, Google to Office 365). I've found these useful, but I don't want all events synced — there's no need for every single meeting to appear on my personal calendar. I primarily want my personal calendar to reflect early meetings (so I know when I need to start sitting in traffic to make it to work on time), late meetings or soccer games (so my family knows when I'll be home!), or other events of interest. In this post I'll show how to use the Flow designer to write more complex Boolean logic in the "Condition" card's advanced editor. This Boolean logic will help my Flow select only the events I care about syncing (using the body, subject, and/or time of the event). Along the way I'll show how to use other Flow features like the Control connector to terminate a Flow run, datetime manipulation and formatting, and preventing infinite loops. 

I started from the basic template linked above, so I won't focus too much on the trigger and actions to save the event. For now, I want to implement the following criteria for syncing the event between calendars:

  • The subject contains "MSSL" (Microsoft Soccer League!). I always want my soccer games sent to my personal calendar.
  • The event starts at or before 10am
  • The event starts at or after 5pm
  • The body of the event contains a special identifier ("@@HOME") that I can use to force an event to be synced even if it doesn't match any of the other criteria.

To start, the events come through the Office 365 connector in the UTC timezone, but I want to reference them in my local time zone. I will use the "Convert time zone" action to do this. (This action is documented in more detail here). Because my criteria above only cares about the time of the event, I will use a custom format string "HHmm" to output the hour and minute of the start time in a form that can be parsed by int(). For example, an event starting at 6:30pm would be output as 1830. 

Convert UTC event time to local time and format hours and minutes

The basic "Condition" designer card only supports a single condition, not a complex AND or OR. In order to combine our above criteria, we will need to switch it into Advanced mode to enter a full WDL expression. Instead of authoring the full condition expression by hand, I'll add each individual criteria as its own condition, and then use the Advanced editor to copy the expressions out. The condition branches don't need to be hooked up to anything yet, since I will remove them once extracting the expressions. Here's the condition I added for one of the time checks:

Basic condition editor

And here's the advanced editor view of the same Condition:

Advanced condition view

We end up with the following expressions after adding all conditions and copying out of the advanced mode:

  • @contains(triggerBody()?['Body'], '@@HOME')
  • @contains(triggerBody()?['Subject'], 'MSSL')
  • @lessOrEquals(int(body('Convert_time_zone')), 1000)
  • @greaterOrEquals(int(body('Convert_time_zone')), 1700)

We'll use the WDL or() function to combine these expressions. This function takes two expressions, and returns true if either or both are true. We'll need to nest the expressions through multiple or()s since we have more than two expressions. Note that only the outer or() needs to be prefixed with the @ character.

@or(
    contains(triggerBody()?['Body'], '@@HOME'),
    or(
        contains(triggerBody()?['Subject'], 'MSSL'),
        or(
            lessOrEquals(int(body('Convert_time_zone')), 1000),
            greaterOrEquals(int(body('Convert_time_zone')), 1700)
        )
    )
)

Note: I've written it across multiple lines for readability and easier matching of parenthesis, but it will ultimately need to be entered in without line breaks like so:

@or(contains(triggerBody()?['Body'], '@@HOME'), or(contains(triggerBody()?['Subject'], 'MSSL'), or(lessOrEquals(int(body('Convert_time_zone')), 1000), greaterOrEquals(int(body('Convert_time_zone')), 1700))))

On the "If yes" branch of the single condition, I'll use the Create an event action to sync the event to my personal calendar. Because I have a similar Flow set up to sync personal events to my work calendar, I need to take some extra steps to avoid an infinite loop caused by one Flow creating an event on a calendar that triggers a different Flow, syncing it back and forth. To do this I'll add a special string in the body of the new event: Synced with Flow! 

Create an event card

I've added another condition right after the trigger to skip any event that contains that string. I could embed this with an and() around the existing expression that we created above, but I'll show another technique — using the Terminate action to stop a Flow run. By using Terminate in the "If yes" branch, I don't need to embed the remainder of the flow inside the "If no" branch. This technique can be used to keep the level of nested conditions to a reasonable level. 

Condition with Terminate action

That's all it takes! With complex expressions and the Terminate action, you can reduce the number of redundant actions, simplify conditions, and eliminate levels of actions nested below the Yes/No branches of Conditions.