Gửi Approval process qua email sử dụng Adaptive Card tới external organization
Trong bài viết này mình xin chia sẻ cách để tạo một approval flow đơn giản gửi email approval tới 1 email nằm bên ngoài organization.
Tạo flow approval:
Chọn Automated cloud flow

Chọn điều kiện trigger theo yêu cầu thực tế

Khởi tạo 1 biến email template để điền chuỗi JSON định nghĩa adaptive card bằng action set variable

Truyền giá trị cho biến bằng action set variable, chính là chuỗi JSON định nghĩa adaptive card, xem link này để thiết kế adaptive card và lấy JSON. Lưu ý trong JSON sẽ có thêm property “originator” là phần quan trọng nhất, sẽ giải thích ở cuối bài.

{
"type": "AdaptiveCard",
"originator": "fa359d10-674a-4dbf-b0a9-8f8c68dec2ab",
"body": [
{
"type": "Container",
"style": "emphasis",
"items": [
{
"type": "TextBlock",
"horizontalAlignment": "Left",
"color": "Dark",
"text": "Vacation Approval",
"wrap": true
}
],
"padding": "Default",
"spacing": "None"
},
{
"type": "Container",
"separator": true,
"style": "accent",
"items": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"color": "Dark",
"text": "Vacation request by Collin Ballinger is pending for your approval",
"wrap": true
}
],
"padding": "Default",
"spacing": "None"
},
{
"type": "Container",
"separator": true,
"items": [
{
"type": "Container",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "Image",
"horizontalAlignment": "Center",
"style": "Person",
"url": "https://amdesigner.azurewebsites.net/samples/assets/Colin_Ballinger.png",
"size": "Small"
}
],
"width": "auto",
"padding": "None"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "Collin Ballinger",
"wrap": true
},
{
"type": "TextBlock",
"spacing": "None",
"color": "Light",
"text": "Design Lead",
"wrap": true,
"size": "Small"
},
{
"type": "TextBlock",
"spacing": "None",
"color": "Light",
"text": "Bangalore - O365",
"wrap": true,
"size": "Small"
}
],
"width": "stretch",
"padding": "None"
}
],
"padding": "None"
}
],
"padding": "None"
},
{
"type": "FactSet",
"id": "105b5da3-b129-6ba3-f414-0fa914b10268",
"facts": [
{
"title": "Reason:",
"value": "I need to visit my aunt in the hospital and so I need 2 days off. "
},
{
"title": "From:",
"value": "23.09.2019"
},
{
"title": "To:",
"value": "25.09.2019"
},
{
"title": "Type of leave:",
"value": "Paid Holiday"
},
{
"title": "Employee's leave balance:",
"value": "13"
}
]
}
],
"padding": {
"top": "Default",
"bottom": "None",
"left": "Default",
"right": "Default"
},
"spacing": "None"
},
{
"type": "Container",
"id": "466ee0a4-b5ba-520e-0b5d-53841fffe66e",
"padding": "Default",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.ShowCard",
"title": "Approve",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"id": "ApproveReason",
"placeholder": "Add a reason",
"isMultiline": true
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Http",
"title": "Submit",
"method": "POST",
"url": "https://prod-06.southeastasia.logic.azure.com:443/workflows/4fa728016e5342deabba7e968e53335e/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=psps8KSLnBqI3UCXT8Kn0D_DMtTvdKuxFYV3qwWQBuE",
"body": "{ActionName:'approved'}",
"headers":[
{
"name":"Authorization",
"value":""
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"padding": "None"
},
"style": "positive",
"isPrimary": true,
"id": "approve"
},
{
"type": "Action.ShowCard",
"title": "Decline",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"id": "DeclineReason",
"placeholder": "Add a reason",
"isMultiline": true
},
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Http",
"title": "Submit",
"method": "POST",
"url": "https://messagecardplaygroundfn.azurewebsites.net/api/HttpPost?code=zJaYHdG4dZdPK0GTymwYzpaCtcPAPec8fTvc2flJRvahwigYWg3p0A==&message=The vacation request was rejected",
"body": "{Reason: {{DeclineReason.value}}}"
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"padding": "None"
},
"id": "decline"
}
],
"spacing": "None"
}
],
"spacing": "None"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0",
"padding": "None"
}
Cuối cùng là gửi email bằng action send an email (v2). Chọn to là 1 email nằm bên ngoài organization. Xem Outlook version requirements for actionable messages tại đây

Ở đây mình sẽ gửi email approval từ vando@corporation.onmicrosoft.com tới adelev@novemberborn.onmicrosoft.com nằm ở 2 tenant khác nhau.
Tạo flow nhận request API khi submit approve hoặc reject
Tạo flow với action là When a HTTP request is received, lưu lại sẽ sinh ra 1 URL

Tạo thêm 1 bước tùy ý tiếp theo theo yêu cầu thực tế muốn xử lý sau khi approve hoặc reject, ở đây mình tạo bước compose chỉ để lưu được flow và test.
Tạo provider cho actionable message theo link này.
Lưu ý: ở đây mình gửi từ vando@corporation.onmicrosoft.com tới adelev@novemberborn.onmicrosoft.com cho nên khi tạo provider sẽ tạo ở organization của adelev@novemberborn.onmicrosoft.com, còn mọi flow sẽ tạo tại organization của vando@corporation.onmicrosoft.com

trong provider này có 2 phần:
- Provider id: sử dụng để điền vào property “originator” trong adaptive card JSON
- sender email address: sử dụng để verify email sender, nếu như email gửi tới không được verify tại đây, thì adaptive card sẽ không thể hiển thị. vì mình gửi từ vando@corporation.onmicrosoft.com nên sẽ điền email này vào mục này.
- Target URL: chính là url thu được sau khi tạo flow HTTP request is received phía trên.
Như vậy là đã xong các bước để tạo một approval process đơn giản nhất, mọi người có thể test thử sau đó để lại bình luận và cùng thảo luận. Hy vọng sẽ có thêm ý tưởng ứng dụng của việc này.
Một số hình ảnh kết quả đã được test.



P/s: giới thiệu 1 tool giúp mọi người debug adaptive card đó là 1 add-in của outlook Actionable Messages Debugger for Outlook
