Introduction
This is Part 3 of a series of posts where I try to explain how to send Form answers from a Power App into Power Automate and then send Adaptive Cards in Microsoft Teams to allow the Requestor and Approver make incremental changes to the Form answers.
We're going to start Part 3 by building a second Flow. In this Form Approval Process this Flow will actually be the one that sends an Adaptive Card to our Approver.
Here is an overview of what we're going to do here.
Why don't I use the standard Approvals Actions?
What I'm trying to achieve is a more conversational process for the Approval, where the Requestor and Approver can both edit the data inside Message Cards in Teams until the Approver decides what to do with it.. We'll do this by passing our Object again, plus a few other useful Expressions.
We're going to trigger this from our "[proxy] - Object Flow" we made in an earlier Post: Passing Objects from Power Apps to Power Automate.
The Approver's Flow
As this will be a Child Flow. We'll need a Manual trigger and I'll call this Flow "[child] - Approver Card". We just need one text Input. varObject as this can contain everything we need.
Then, we'll Initialize a Variable that will convert the Text Input to JSON.
json(triggerBody()['text'])
Adaptive Card
Let's add another action - Post adaptive card and wait for a response. We'll need to construct an Adaptive card for this. Again I would recommend you build your full Adaptive Card by previewing it with the Help of The Adaptive Card Designer or the VSCode Microsoft Adaptive Card Previewer
Text Inputs
I will start with the Text value we're getting for the "Name" field in the Form.
- We need a type, this will be Input.Text
- We can specify the Label above the field as "Name"
- For the Value, we can reference the property inside of varObject by inserting "@{variables('varObject')?['Name']}"
- Finally, each field in the Adaptive Card needs an id, which we can keep exactly the same as the Object Property name to make it easier to reference in the future.
In my scenario, we already have an input value, so 'Placeholder Text' isn't really needed.
{
"type": "Input.Text",
"label": "Name",
"value": "@{variables('varObject')?['Name']}",
"id": "Name"
},
You can use the example above to build any other Text Inputs.
Toggle
The 'Mortgaged' field is a Boolean field in our Power Apps Form, so we can replicate this with Input.Toggle, similar to the Text Input we need to give an id, label and value.
{
"type": "Input.Toggle",
"title": "Mortgaged",
"id": "Mortgaged",
"label": "Mortgaged",
"value": "@{variables('varObject')?['Mortgaged']}"
},
Choices
I have previously described how we can have a Choices Dropdown in this Blog Post:
Passing a Choices List from Power Apps to an Adaptive Card - in this example, we can add a Label: "Country"
{
"type": "Input.ChoiceSet",
"label": "Country",
"choices": @{variables('varObject')?['Countries']},
"id": "Country",
"value": "@{variables('varObject')?['Country']}"
}
For the Rating Field, we know there's only five options, so we might as well enter these manually, giving us this:
{
"type": "Input.ChoiceSet",
"choices": [
{
"title": "1",
"value": "1"
},
{
"title": "2",
"value": "2"
},
{
"title": "3",
"value": "3"
},
{
"title": "4",
"value": "4"
},
{
"title": "5",
"value": "5"
}
],
"label": "Rating",
"id": "Rating",
"value": "@{variables('varObject')?['Rating']}"
}
Actions
That's the fields done, now we need some actions to respond with. These can be placed at the foot of the Adaptive Card like this and will be where the Approver makes their decision.
"actions": [
{
"type": "Action.Submit",
"title": "Approve",
"style": "positive",
"id": "approve"
},
{
"type": "Action.Submit",
"title": "Respond",
"id": "respond"
},
{
"type": "Action.Submit",
"title": "Reject",
"style": "destructive",
"id": "reject"
},
{
"type": "Action.ShowCard",
"title": "ReAssign",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"label": "ReAssign to",
"style": "Email",
"id": "ReAssign_To",
"isRequired": true,
"errorMessage": "Please enter an email address"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "ReAssign"
}
]
},
"id": "acAssignCard"
}
]
Feel free to elaborate on the options you provide, such as comments, or even an option to reassign. We'll use these soon in a Switch Action to decide on the next step. IF you want to use this you'll see that there's an expandable section (https://adaptivecards.io/explorer/Action.ShowCard.html)[Action.ShowCard] when ReAssign is selected, this is to ensure an email address is entered.
The JSON for the Approval Adaptive Card is here.
Union
Now we have an Adaptive card that we can send to an Approver and Wait for a response.
We can now capture any changes that the Approver may have made back into our original varObject.
Start by adding a Compose Action, and in the Compose Action we can enter this Expression
union(variables('varObject'),outputs('Post_adaptive_card_and_wait_for_a_response').body.data)
What the Union expression does is combine the properties from the varObject with the data obtained from the response of the action Post_adaptive_card_and_wait_for_a_response. This is done to update or synchronize the values in the varObject with the field values changed in the Adaptive Card.
Then we just need to feed the output of our Compose expression into Set the varObject.
Switch
We'll add a switch Condition to allow us to decide the path of the Flow from this point using some Switch Cases, which match the the ID of our Actions.
outputs('Post_adaptive_card_and_wait_for_a_response')?['body/submitActionId']
We have the options: approve, respond, reject, ReAssign_To and we can decide what we we want to happen with each of these paths. I'll come to respond in a moment, but I will first give some examples of how you can use the other Switch Cases.
Approve
This would be where you would define what you want to happen if the Adaptive Card is approved; Send an Email, update a SharePoint List Item etc...
Reject
Here you could send Post a Standard Adaptive card or send an email to the Requestor.
ReAssign
A good example for using the setProperty() expression would be if you had a defined Approver_Email in your varObject. You could use a Compose Action with:
setProperty(variables('varObject'), 'Approver_Email', outputs('Approver_adaptive_card')?['body/data/ReAssign_To'])
Then, Set Variable - varObject again with the output of the Compose Action.
Respond
So for the Respond Case, we're going to create another Child Flow, it will be a copy of this Flow where the Requestor can make changes to re-submit to the Approver.
Respond to a PowerApp or flow
To make sure this Child Card let's the Parent Flow it has run, place in a Respond to a PowerApp or flow as the last Action.
[proxy] - Object Flow
Before we move on to the Requestor Card, we need to trigger this Child Flow from the [proxy] - Object Flow by making this the 3rd action in the Flow.
Requestor Card
Now - back to the [child] - Approver Card] Flow, so we can copy it....
We'll call this: [child] - Requestor Card and add it to the same solution as our App and other Flows.
Let's first make a couple of small adjustments to the Post adaptive card and wait for a response Message in this one by changing the first TextBlock with text Property from:
To:
{
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "Requestor Request"
},
Then we just need to swap out the "actions": [] section at the bottom of the Card. The Requestor won't have the same options as the Approver, so we can swap out the whole section for this:
"actions": [
{
"type": "Action.Submit",
"title": "Re-Submit ๐",
"id": "acSubmit",
"style": "positive"
},
{
"type": "Action.Submit",
"title": "Cancel Request โ",
"id": "acCancelRequest",
"style": "destructive"
}
]
And of course, change the Receipient, so that the message goes to our Receipient. The Union action is still here to update any changes which then update our varObject.
Switch
As we've changed the Actions, we'll now need to change the cases to "acSubmit" and "acCancelRequest".
acCancelRequest
Again, I'll leave it up to you what to do with this path; you may want to imform the Approver with another standard Adaptive Card message.
acSubmit
So here's the key feature that allows us to perform this conversation loop between the Approver and Requestor. - We're now going to re-trigger [child] - Approver Card and with that, pass back the varObject as a String again.
Create a 'Run a Child Flow' action and select our [child] - Approver Card Flow we created earlier. For the varObject Text Input enter: string(variables('varObject')) - this converts the varObject into a String again to pass to Approver Flow.
Respond to a PowerApp or flow
Again, to make sure this Child Card let's the Parent Flow it has run, place in a Respond to a PowerApp or flow as the last Action.
JSON for the Requestor Adaptive Card is here
Back to [child] - Approver Card to finish off
All we need to do now is the mirror opposite of this in the [child] - Approver Card Flow. Go back to that flow and underneath the Case - respond create a 'Run a Child Flow' action and select our [child] - Requestor Card
Testing and fine tuning
To prevent any confusing repeat runs of these Flows, I would recommend changing the Retry Policy
on each Run a Child Flow Action to None
So, here are some examples that we should see in Microsoft Teams.
Power App Form
Approver's 1st Message
Requestor's 1st Message
Approver's 2nd Message
Conclusion
Hopefully, that was a helpful guide. I have one final Post to add a little bit extra to this type of Approval process. Please leave any questions or comments below.
Top comments (1)
Very cool ๐