Skip to content

Instantly share code, notes, and snippets.

@jeremieflrnt
Last active October 7, 2024 16:44
Show Gist options
  • Save jeremieflrnt/e769639ee29761b51421fc2f80644203 to your computer and use it in GitHub Desktop.
Save jeremieflrnt/e769639ee29761b51421fc2f80644203 to your computer and use it in GitHub Desktop.
Power Automate workflow to report test failures to Teams

Workflow to Report Test Failures to Microsoft Teams with Power Automate

This document outlines a workflow using Microsoft Power Automate to report test failures to a specific Microsoft Teams channel. The workflow enables users to take immediate action to either resolve the issue or automatically create a Jira ticket.

It is part of a medium article I wrote here.

Overview

Whenever a test fails, a dynamic card is sent to the designated Teams channel. The card includes test failure details and offers two primary actions:

  • Mark as Fixed – Allows the user to mark the issue as resolved and add any relevant comments.
  • Create a Jira Ticket – Opens a Jira ticket to track the issue.

Additionally, a text field is provided for users to include extra details related to the failure, fix, or Jira ticket creation.

In this workflow example, a dynamic card is triggered for every failed test on the exaprint.fr website's product pages.

Workflow Overview

Workflow Visual Representation

Below is a visual representation of the entire workflow:

Full Workflow Visualization

Workflow Steps

The Trigger: HTTP Request

The workflow begins with an HTTP request that serves as the trigger for the test report.

{
  "type": "Request",
  "kind": "Http",
  "inputs": {
    "triggerAuthenticationType": "All",
    "schema": { "type": "string" },
    "method": "POST"
  }
}

In our use case, the payload (body) is simply the url of the failed product page.

Step 1: Sending the Adaptive Card to Teams

In this step, an adaptive card is posted to the Teams channel. The card displays the failed test URL as a clickable link and includes two action buttons: Mark as Fixed and Create a Jira Ticket.

Adaptive Card Example

Once the user selects an action, the workflow proceeds based on their choice.

Connector: MS Teams / Operation ID: PostCardAndWaitForResponse

{
  "type": "OpenApiConnectionWebhook",
  "inputs": {
    "parameters": {
      "poster": "Flow bot",
      "location": "Group chat",
      "body/body/messageBody": "{\n    \"type\": \"AdaptiveCard\",\n    \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n    \"version\": \"1.2\",\n    \"msteams\": {\n        \"width\": \"Full\"\n    },\n    \"body\": [\n        {\n            \"type\": \"Container\",\n            \"items\": [\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"[@{triggerBody()}](@{triggerBody()})\",\n                    \"wrap\": true,\n                    \"id\": \"failedUrl\"\n                },\n                {\n                    \"type\": \"Input.Text\",\n                    \"placeholder\": \"Ajouter des détails ici sur la correction ou pour le Jira\",\n                    \"isMultiline\": true,\n                    \"id\": \"moreInfo\",\n                    \"separator\": true,\n                    \"label\": \"Plus d'info (facultatif) :\"\n                }\n            ]\n        },\n        {\n            \"type\": \"ActionSet\",\n            \"actions\": [\n                {\n                    \"type\": \"Action.Submit\",\n                    \"title\": \"Corrigé\",\n                    \"id\": \"fixed\",\n                    \"style\": \"positive\"\n                },\n                {\n                    \"type\": \"Action.Submit\",\n                    \"title\": \"Faire un JIRA\",\n                    \"id\": \"jira\",\n                    \"style\": \"default\"\n                }\n            ]\n        }\n    ]\n}",
      "body/body/updateMessage": "Un instant, je traite l'info.... :)",
      "body/body/recipient/recipient": "id of your channel.."
    },
    "host": {
      "apiId": "/providers/Microsoft.PowerApps/apis/shared_teams",
      "connection": "shared_teams",
      "operationId": "PostCardAndWaitForResponse"
    }
  },
  "runAfter": {}
}

You can design and test your adaptive card using the Adaptive Card Designer.

Step 2: Handling User Actions

Once the card is sent, the workflow waits for user input, retrieving details about their selected action.

Compose Action Data

The following block composes data about the user's action:

{
  "type": "Compose",
  "inputs": {
    "submitActionId": "@body('ReportCard').submitActionId",
    "messageId": "@body('ReportCard').messageId"
  },
  "runAfter": {
    "ReportCard": ["Succeeded"]
  }
}

Timeout Scenario

If no action is taken, the workflow includes a timeout mechanism. After 2 weeks, the workflow automatically terminates.

Step 1a: Start a Timer

A timer variable is initialized for the timeout duration.

{
  "type": "InitializeVariable",
  "inputs": {
    "variables": [
      {
        "name": "endTime",
        "type": "string",
        "value": "@{getFutureTime(2, 'Week')}"
      }
    ]
  },
  "runAfter": {}
}

Step 1b: Wait for the Timer to Expire

The workflow waits for 2 weeks before terminating.

{
  "type": "Wait",
  "inputs": {
    "until": {
      "timestamp": "@variables('endTime')"
    }
  },
  "runAfter": {
    "End_Time": ["Succeeded"]
  }
}

Step 1c: Terminate the Workflow

After the wait period, the workflow fails if no action was taken.

{
  "type": "Terminate",
  "inputs": {
    "runStatus": "Cancelled"
  },
  "runAfter": {
    "Delay_Until": ["Succeeded"]
  }
}

Conditional Branching Based on User Action

This step creates two branches, based on whether the user clicked Fixed or Create a Jira Ticket.

{
  "type": "If",
  "expression": {
    "and": [
      {
        "equals": [
          "@outputs('Retrieve_infos_from_card_(action_n_more_info)').submitActionId",
          "fixed"
        ]
      }
    ]
  },
  "actions": "Will go into details bellow",
  "runAfter": {
    "Retrieve_infos_from_card_(action_n_more_info)": ["Succeeded"]
  }
}

Step 3: Marking the Issue as Fixed

If the user selects Fixed, the adaptive card is updated with relevant details, including the user who resolved the issue and any additional comments.

{
  "type": "OpenApiConnection",
  "inputs": {
    "parameters": {
      "poster": "Flow bot",
      "location": "Group chat",
      "body/messageId": "@{outputs('Retrieve_infos_from_card_(action_n_more_info)').messageId}",
      "body/recipient": "id of your channel..",
      "body/messageBody": "{\n    \"type\": \"AdaptiveCard\",\n    \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n    \"version\": \"1.2\",\n    \"msteams\": {\n        \"width\": \"Full\"\n    },\n    \"body\": [\n        {\n            \"type\": \"Container\",\n            \"items\": [\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"[@{triggerBody()}](@{triggerBody()})\",\n                    \"wrap\": true,\n                    \"id\": \"failedUrl\"\n                },\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"Le problème a été noté comme corrigé par @{body('ReportCard').responder.displayName}\",\n                    \"wrap\": true\n                },\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"Infos aditionnelles : @{body('ReportCard')?.data?.moreInfo}\",\n                    \"wrap\": true\n                }\n            ]\n        }\n    ]\n}"
    },
    "host": {
      "apiId": "/providers/Microsoft.PowerApps/apis/shared_teams",
      "connection": "shared_teams",
      "operationId": "UpdateCardInConversation"
    }
  }
}

Step 3a : Creating a Jira Ticket

If the user selects Create a Jira Ticket, the workflow calls the Jira REST API to create a new ticket.

{
  "type": "Http",
  "inputs": {
    "uri": "https://exaprint.atlassian.net/rest/api/3/issue",
    "method": "POST",
    "headers": {
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    "body": {
      "fields": {
        "description": {
          "content": [
            {
              "content": [
                {
                  "text": "This url is failing: @{triggerBody()}\n\nAdditional info:\n@{body('ReportCard')?.data?.moreInfo}",
                  "type": "text"
                }
              ],
              "type": "paragraph"
            }
          ],
          "type": "doc",
          "version": 1
        },
        "issuetype": {
          "id": "10320"
        },
        "project": {
          "key": "BUG"
        },
        "customfield_11129": "@body('ReportCard').responder.email",
        "summary": "Bug price can't order from Teams: @{concat(slice(triggerBody(), 23, 73),'[...]')}"
      },
      "update": {}
    },
    "authentication": {
      "type": "Basic",
      "username": "your email adress",
      "password": "token"
    }
  }
}

Notes

Use the API Get issue from Jira to get information on what is expected as fields and values for the Create issue route. See the documentation here.

customfield_11129 is a custom field in our Jira instance that stores the sender's email address. You can create this custom field by visiting your Jira instance at: https://your-domain.atlassian.net/secure/admin/ViewCustomFields.jspa.

This field is necessary because Jira will automatically assign the issue to you as the reporter by default. By including the sender's email in this custom field, you can use Jira automation to reassign the issue to the correct user. Whenever an issue is created and the email field is populated, an automated Jira action can change the reporter from the default to the user who initiated the request.

Maybe an edit with the API is possible to change the reporter. We had that automation for a other use case, so we went with it.

An example of how the automation can look like

Step 3b. Updating the Adaptive Card After Jira Creation

The adaptive card is updated again to reflect that a Jira ticket has been created, along with a link to the new Jira issue.

{
  "type": "OpenApiConnection",
  "inputs": {
    "parameters": {
      "poster": "Flow bot",
      "location": "Group chat",
      "body/messageId": "@{outputs('Retrieve_infos_from_card_(action_n_more_info)').messageId}",
      "body/recipient": "id of the channel..",
      "body/messageBody": "{\n    \"type\": \"AdaptiveCard\",\n    \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n    \"version\": \"1.2\",\n    \"msteams\": {\n        \"width\": \"Full\"\n    },\n    \"body\": [\n        {\n            \"type\": \"Container\",\n            \"items\": [\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"[@{triggerBody()}](@{triggerBody()})\",\n                    \"wrap\": true,\n                    \"id\": \"failedUrl\"\n                },\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"Un jira a été créé par @{body('ReportCard').responder.displayName} :\",\n                    \"wrap\": true\n                },\n                {\n                    \"type\": \"TextBlock\",\n                    \"text\": \"[@{body('Send_POST_to_create_Jira').key}](https://exaprint.atlassian.net/browse/@{body('Send_POST_to_create_Jira').key})\",\n                    \"wrap\": true\n                }\n            ]\n        }\n    ]\n}"
    },
    "host": {
      "apiId": "/providers/Microsoft.PowerApps/apis/shared_teams",
      "connection": "shared_teams",
      "operationId": "UpdateCardInConversation"
    }
  },
  "runAfter": {
    "Send_POST_to_create_Jira": ["SUCCEEDED"]
  }
}

Step 4: Mark Workflow as Complete

Once either action is completed, the workflow is marked as successful.

{
  "type": "Terminate",
  "inputs": {
    "runStatus": "Succeeded"
  },
  "runAfter": {
    "If_error_fixed": ["Succeeded"]
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment