Skip to Content
Technical Articles

Last action hero with adaptive cards for cloud demo system ES5

Dear community,

You can relax I won’t be talking about Arnie today 😉but I will be “back” on the topic maybe next time. This post will be about closing the loop for SAP approval processes, that are surfaced in Outlook. So, we will not only see how to act upon the approval request from SAP but also see updates to the adaptive card in the E-Mail in case someone else already “approved”. Exciting, right? Probably, just short of the excitement, that there might be more movie references further down the line 😉

I won’t be repeating the communication basics on OData integration or the adaptive cards because they are covered by the community quite well already.

For instance, I like this prototype from Harald Schubert a lot. He combined a SAP Cloud Platform workflow service with Outlook and was able to generate adaptive cards from workflow forms.

Let’s look at the moving parts

Fig.1 overview of integration scenario, message update

I am running the flow on Power Automate on a timely basis, because I have no control over SAP’s demo system ES5. Otherwise I would prefer a push-based mechanism based on S/4Hana business events or the likes. Let’s follow along the bubbles on figure 1 and discuss steps in detail.

Step 1

My flow checks the OData service on ES5 for pending approvals for my user:

https://sapes5.sapdevcenter.com/sap/opu/odata/sap/SEPMRA_PO_APV/PurchaseOrders?$format=json&$filter=OrderedByName eq ’Firstname <my SUser>’.

Step2

After that I polish the response a little for the adaptive card ui and send it. Please note that I created two flows: one for Teams as a target and one for Outlook. The structure of the card is the same, but the actions are implemented differently. For the Teams connector this is built-in but for Outlook I need to provide an endpoint to be called from the buttons. Have a look at the comparison down below for reference or the docs here for Teams and Outlook if you want to know more:

Fig.2 Comparison of actions in adaptive cards

In the Microsoft world you will also find the term “actionable message” very often for the adaptive cards used with Outlook. Let’s use that one to avoid confusion further down for the cards on Outlook. So far so good on the context. Now on to some polishing 😊

The OData timestamp format for instance needs transformation to UTC to be nicely displayed.

Fig.3 Screenshot from date time formatting on adaptive card

The function and rendered cared respectively look like this:

formatDateTime(addSeconds('1970-01-01T00:00:00Z',div(variables('timestamp'),1000)),'dd.MM.yyyy HH:mm:ss')

Fig.4 Screenshot of adaptive card in Teams

No worries, you can find the full flows on my GitHub repos for you to strip for pieces.

Step3

For such approvals you often have multiple people, who can do this. So, you could either have multiple individual targets or Teams channels/shared inboxes to achieve that. Next my colleague Tobias approves this process from our Teams Channel (see fig.4).

Fig.5 Screenshot of updated Teams card

So, everyone in the Teams channel is aware from the feed history, that there is nothing more to be done with that card and respective process. But what happens now in Outlook? E-Mails don’t usually change by themselves when you open them, right? Right!!! I understand if that is a little scary: E-Mails suddenly changing while you are looking at them, without touching anything. But I promise it will be more fun than ludicrous 😉.

Step 4

The approval is done through OData again:

https://sapes5.sapdevcenter.com/sap/opu/odata/sap/SEPMRA_PO_APV/ApprovePurchaseOrder?sap-client=002&POId=’@{body(‘Parse_JSON_flat’)?[‘POId’]}’&Note=’Thanks’

To get this done from Power Automate or Logic Apps you need to be aware of a special Cookie format. You need not only to get the X-CSRF-Token but also replace the commas with semi-colons on the cookie header before passing it on:

replace(body('Parse_JSON_header')?['Set-Cookie'],',',';')

Step 5 – 7

Fig.6 Gif about mysterious email refresh on open

Remember the purchase order from ES5 is already approved from Teams. The gif* above shows, that my approval request gets updated while I open it. This works due to  the marvelous auto-invoke action of Actionable Messages. With that I can call an endpoint for updated information. At the end of the gif you see the actionable message content with the shiny buttons being replaced by a text stating that Tobias approved the request already.

*it was my first conversion from video to gif -> still working to improve resolution 😉

There is only one caveat: for user experience reasons those update request need to finish under 2 seconds. Unfortunately, the OData request to ES5 to check for the status of the approval request often takes 3 or more seconds due to latency, load and performance capabilities of the demo system. In your own setup in Azure for instance you would be able to handle this within your Azure zone.

Due to this hurdle I added a “near-shore” table storage that stores the ids of approval requests temporarily when I request them through Power Automate. Obviously, that doesn’t work when you approve in ES5 directly. Again, that would not be in issue with your own system, because you could the process accordingly.

Thoughts on production readiness

To make this scenario more production ready you would need to fine-tune a couple of things:

  • Securing calls and responses: You shall not pass! You earned that movie reference by reading this far 😉There is a nice blog and a docs entry on how to work with tokens and signed cards to verify the sender from a service that receives messages from the actionable messages.
  • Multiple receivers: We touched on this briefly before. You would probably want to broadcast those approvals to multiple recipients. The options are parallel branches in Power Automate, using Teams Channels over individual users and shared mailboxes on Outlook. Here is another blog with some illustrations on it.
  • Identifying users: I was making jokes and references to my colleague Tobias although my flows not actually identify anyone. But you could enhance the example and have the Microsoft Graph or O365 connectors actually identify the user running the flow and use that info in your messages.
  • Card design: Be prepared to react to service delays and missed updates. One option to do this would be the CARD-ACTION-STATUS You could for example react to outdated approvals from Power Automate. Once you send the OData request to ES5, it will let you know the request is gone already. That response could be returned to the user in Teams as an additional message or through the error callout (check the card-action-status for details) in Outlook stating the request was already approved.
  • Resiliency of Power Automate flow / Logic Apps: Both technologies implement retry-policies (4 by default currently), that solve many latency topics out-of-the-box. For more problematic situations it would be advisable to create separate branches for error handling. I like the “configure-run-after” option. The red arrow indicates that this route is going to be taken on error (has failed, is skipped, has timed out). The other arrow is black and will only continue if the preceding action was successful.

Final Words

Aaaaand done! I showed you how you can enrich the adaptive card experience with the spooky auto-invoke in Outlook. This is interesting because many processes like to purchase order approval can often be done by multiple people on multiple clients/devices. The auto-refresh ensures that you know quickly that the approval request in your inbox has already been taking care of by someone else (last action hero😉). Or in my case “bloody” Tobias!

Find the resources for this prototype on my GitHub repos: https://github.com/MartinPankraz/SAPES5-Approvals-PowerAutomate

As always feel free to leave or ask lots of follow-up questions.

 

Best Regards

Martin

1 Comment
You must be Logged on to comment or reply to a post.