DEV Community

Cover image for Using Mermaid JS to generate a diagram from Power Automate
Matthew Collinge
Matthew Collinge

Posted on

Using Mermaid JS to generate a diagram from Power Automate

Introduction

This is the "bonus part" of my blog series on
creating a Conversational Approval Process using Power Automate and Microsoft Teams.

I needed a way to produce an easy to read depiction of the steps of a process to users or even a way to debug what happened. After seeing Jon Russell and Mike Gowland's amazing demo of
JustAskIt on a Microsoft 365 & Power Platform Development community call I have been keen to try out Mermaid JS in Power Automate.

What is Mermaid JS?

Mermaid JS is a tool that lets you create diagrams and charts such as flowcharts, sequence diagrams, Gantt charts using simple text commands. It works by converting your text commands into a graphical representation that you can customize and share.
https://mermaid.live/

Example

Using Mermaid JS we easily can convert Markdown like this:

flowchart TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[fa:fa-car Car]

Enter fullscreen mode Exit fullscreen mode

into an image like this that lists the steps in an easy to read graphical format:
Mermaid Example

Building the Flow

Manual Trigger

We start off our Flow with the manual trigger. The first text input is varSteps, this is the steps that we are going to either create or append to our existing Flowchart.
The other text input is the Flowchart, so this is where we could insert an existing Flowchart to our Power Automate Flow.

Manual Trigger

Alphabets Variable

The first variable to initialise is Alphabets, here we will have a string value of the 26 character alphabet. We will use this to pick out the next alphabetical character if we need to append to an existing Flowchart.
Alphabets Variable

Flowchart Variable

The next variable to initialise is Flowchart, which is a string variable that will capture the Flowchart text input from the manual trigger.

Flowchart Variable

varSteps Variable

Then the next variable to initialise is the varSteps, which is an object variable. It will capture the text input for varSteps from the manual input, but we will wrap this inside JSON() so that it is converted to JSON from a string value.
varSteps Variable

Condition

We will start the actions with a Condition action. The condition will check whether variable('Flowchart') is equal to Null. If it is Null, then we'll create on from scratch. If it's not, then we can append to it with further Steps.

Condition - 1st Time Around

Creating a new FlowChart

I will first go through the steps that are required in the Yes or True side. In here we are just creating a brand new Flowchart that is created in the format ready for Mermaid JS. Set the variable FlowChart and in the value box we will build our markdown so that it can be read inside the Mermaid JS. We will reference the Object properties from the varSteps to build that.

  • Define the type using "flowchart TD"
  • Starting with “A” we will enter variables('varSteps')?['Trigger'] .
  • Then for “B” we will add the variables('varSteps')?['Condition']
  • Then ”B-->” for variables('varSteps')?['Outcome']
  • Lastly we will insert the “C” variables('varSteps')?['Action'].

That should give us all we need to create a new Flowchart.
Set Variable - FlowChart

Appending to an Existing Flowchart

On the No / False side of the Condition Action we will be appending to an existing Flowchart. This is useful if there are multiple child flows that make up part of the full process that you are trying to build into a Flowchart. In my Conversational Approval process, there may only be one run of the Approver's Adaptive card, or there may be a need to the Requestor to respond to an Adaptive Card also. Having the Append option allows us to keep building on top of the initial FlowChart that gets created.

Compose - Last Step

We will start by extracting the alphabetical character for the last step that exists in the Flowchart. For example; if the existing Flowchart is:

flowchart TD
A[RM Request] --> B{Matthew Collinge - receives Approval Card}
B -->|Pending| C[Send back to Matthew Collinge]
Then we want the letter **“C”**
Enter fullscreen mode Exit fullscreen mode

Using a Compose action we can use.

substring(variables('flowchart'), sub(lastIndexOf(variables('flowchart'), '['), 1), 1)

Compose - Last Step

We will need to use this value to increment to the next letter in the alphabet.

Compose Letter No

We now get the next letter's number by using the next compose action. Referencing the alphabet variable we get the next number (D would be 4).

indexOf(variables('Alphabets'), outputs('Compose_-_Last_Step'),1)

Compose - Letter No

Compose Approval

The next compose step is actually composing the approval Markdown text, starting with the original Flowchart and then appending the next steps. It will start with the starting letter that we have derived from the previous few actions.

@{variables('FlowChart')}
@{outputs('Compose_-_Last_Step')} --> @{substring(variables('Alphabets'), add(outputs('Compose_-_Letter_No'),2),1)
}@{variables('varSteps')?['Condition']}
@{substring(variables('Alphabets'), add(outputs('Compose_-_Letter_No'),2),1)
} -->@{variables('varSteps')?['Outcome']} @{substring(variables('Alphabets'), add(outputs('Compose_-_Letter_No'),3),1)
}@{variables('varSteps')?['Action']}
Enter fullscreen mode Exit fullscreen mode

Compose Approval

The last step on this append side is setting the Flowchart variable with the output from the previous compose action, so that we have a variable with the whole Flowchart ready to convert to Mermaid JS.
Set variable - FlowChart Appended

Putting it all together

Compose - URL

After the Condition Action we have a single compose action.
This will concatenate a string starting with 'https://mermaid.ink/img/', and here is where we convert the Flowchart variable to base 64 giving us the correct encoding to create a URL.

concat('https://mermaid.ink/img/',base64(variables('FlowChart')))
Enter fullscreen mode Exit fullscreen mode

Compose - URL

Respond to App or Flow

All we need to do now, is respond to the App or Flow that triggered this Flow. We will use Text input of Flowchart using the Flowchart variable and a text input of Mermaid URL which includes the URL we have just generated.

Respond to App or Flow

Inserting this Child Flow into another Flow to capture the steps

Using the "[child] - Approver Card" as an example from my previous posts. In our Parent Flow we can construct a JSON Object inside of a Compose action.

Compose - Outcome

Capture the outcome of the Adaptive Card
Compose - Outcome

outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId']
Enter fullscreen mode Exit fullscreen mode

Compose - Action

Convert this outcome using "If(equals" to what the follow on Action will be:

if(equals(outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId'], 'approve'),'Complete Approval',
if(equals(outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId'], 'respond'), 'Send back to Requestor', 
if(equals(outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId'], 'reject'),'Inform Requestor',
if(equals(outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId'], 'acAssignCard'), 'Assign to other person'))))
Enter fullscreen mode Exit fullscreen mode

Compose - Action

Compose - varSteps

The JSON Object will be written in a format that could capture various outcomes and statuses that happen within the flow in . For Example:

{
  "Trigger": "[@{variables('varObject')?['Request_Type']} Request]",
  "Condition": "{@{variables('varObject')?['Approver_Name']} - receives Approval Card}",
  "Outcome": "|@{outputs('Compose_-_Status')}|",
  "Action": "[@{outputs('Compose_-_Outcome')}]"
}
Enter fullscreen mode Exit fullscreen mode

Compose - varSteps

Then we can trigger the Child Flow using 'Compose - varSteps' as a String.

string(outputs('Compose_-_varSteps'))
Enter fullscreen mode Exit fullscreen mode

and then FlowChart. If we're carrying over a previous one held inside our varObject or Null if we're creating a new one.

if(empty(variables('varObject')['FlowChart']), 'null', variables('varObject')['FlowChart'])

Enter fullscreen mode Exit fullscreen mode

Run a Child Flow - Generate Mermaid Flowchart

Capture the Response

To then capture from our child flow and add it to our varObject.
Update Property

Set FlowChart

Conclusion

Thank you for reading this post. Please leave feedback in the Comments if you found this useful, or if there are any improvements you could recommend. Please experiment with it too, I'd love to hear some of the outcomes.

For such a simple Flow, I found it quite difficult to write and explain, so I hope it all makes sense!

Top comments (2)

Collapse
 
baxist profile image
Max

Thanks for sharing. Love it, but didn't test it.
Would I would like to do: use a Mermaid JS Image inside of PowerApps, to visualize something.
Unfortunately, I'm not able to do it. Encoding Mermaid JS text to use it with the image url isn't working.

Example:
graph TD;
A-->B;
A-->C;
B-->D;
C-->D

Used in Mermaid live gives me this encoded string after "poke":
eNo1jc0KwjAcw19l5DxH13X7dxUEp2-gJ-mluPoBthuzA3Xs3a2ip18SEjLh2LUWCufB9Jdkv11qv14sVs2Pm8gm8pNvPkQKZwdnrm0cTdoniUa4WGc1VJStPZnxFjS0n2PVjKHbPf0RKgyjTTH2rQl2ezXxzkGdzO0e0974Q9e5fylaqAkPKM5lxmrKS16LUhZ5iidUTRnluSBBVckYr6o5xes7Z5mUJGRJghMVrK7E_AYBm0Fc

If I decode it directly, I got:
Z3JhcGggVEQ7CkEtLT5COwpBLS0+QzsKQi0tPkQ7CkMtLT5E

So I'm confused.

Collapse
 
mcombp profile image
Matthew Collinge

Hey Max,
Thanks for reading.

If you prefix that with mermaid.ink/img/ it will render the image for you. You could then include that image inside PowerApps by placing "mermaid.ink/img/Z3JhcGggVEQ7CkEtLT..." inside an Image Control.

Hopefully this helps 🙂