Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Below are some sample Webhook Listener use cases and how to implement them.

Table of Contents
minLevel1
maxLevel3

The Default Listener (POST Webhook)

...

To take advantage of ending a release after a pipeline execution completes you can create a new listener and select the Pipeline Stage Completed. You will then need to check whether the completed stage is the final stage in the pipeline. You can do this in a number of ways. One way is shown below by checking the environment code and comparing it to our final PROD stage.

...

...

Example Groovy Code

Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the documentation for reference or ideas.
//Optionally filter out events you dont want to execute by returning false on the filter script tab.
def listenerName = "endReleaseListener";
LOG.info("Running listener: ${listenerName}");
         
if(EVENT.payload.environment.environmentCode.equals("PROD")){
     LOG.setMessage("Release ${EVENT.payload.release.releaseName} has completed moving through the pipeline. Ending release.");
     FLEXDEPLOY.endRelease(EVENT.payload.release.releaseId);
}

Another way to take advantage of this is to use the Webhook Listener’s filter. This filter is also groovy script where you can determine whether the event should be processed by the Listener. In this example, we will filter the events so that only pipeline stage completed events that have completed the Prod stage in the pipeline are allowed through.

Info

Logically, both approaches will accomplish the same task, however, using the Filter will automatically hide it from the Outgoing Webhook Messages display. This will reduce some of the noise and allow you to easily find the messages you care about.

...

Pipeline Stage Completed Filter

Code Block
if(!EVENT.payload.environment.environmentCode.equals("PROD")){
  return false;
}else{
  return true;
}

Send a Slack Notification After Approval Task Is Created

...

Info

For a more comprehensive overview of how you can integrate Slack with your FlexDeploy Approval process check out this blog

...

...

TaskCreated Script

Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the snippets for reference or ideas.
def listenerName = "TaskCreated";
LOG.info("Running listener: ${listenerName}");

def channel = 'testing';

//Slack account code defined in Topology->Integration->Messaging
def account = 'SLACK';

//Create and post a FlexDeploy Slack Message object with interactive buttons to approve/reject
def message = SLACK.makeTaskCreatedMessage(EVENT.payload,true);
def tsId = SLACK.postMessage(account,channel,message);

LOG.setMessage("Posted task ${EVENT.payload.taskId} to Slack");

...

TaskCreated Filter

Code Block
languagegroovy
return EVENT.payload.taskType == "APPROVAL";

Attach Failed Plugin Logs to Jira Issue

...

Info

This script uses FLEXDEPLOY.getIntegrationInstance to get a configured Jira instance within FlexDeploy. This is a great alternative to putting a plain text password in the script

...

Upload Logs Jira Listener Script

Code Block
languagegroovy
import flexagon.ff.common.core.exceptions.FlexCheckedException;

//Carry out any custom action you wish for this event. Check out the snippets for reference or ideas.
def listenerName = "Upload Logs To Jira";
LOG.info("Running listener: ${listenerName}");

//Prior to 5.4.0.1 this function only returned a list of input streams. Update accordingly
//The second argument specifies we only want the error streams returned.
def streams = FLEXDEPLOY.getPluginLogInputStreams(EVENT.payload.workflowExecutionId, true);

//upfront validation on streams
if(!streams || streams.size() == 0) {
  LOG.logMessage("Received failure event but couldn't find any plugin log streams");
  return;
}

//We should only ever have one errored plugin execution, so grab first entry.
def failedPlugin = streams.entrySet().iterator().next();

//create our form data body
def body = REST.getClient().createFormDataWithInputStream(['file': failedPlugin.getValue()], "${failedPlugin.getKey()}.txt");

def properties = getJiraProperties('JIRA');

LOG.info("${EVENT.payload.issueNumbers}");

for(def issue: EVENT.payload.issueNumbers) {
  LOG.info("Upload logs to issue: ${issue}");
  
  //send request
  def client = REST.getClient().url(properties.url).addHeader('X-Atlassian-Token', 'no-check').basicauth(properties.user,properties.password);
  def response = client.path("/rest/api/2/issue/${issue}/attachments").post(body);
  LOG.setMessage("Uploaded logs to ${issue}. Response: ${response.getResponseCode()}");
}

//helper function to get jira properties from integration instance
def getJiraProperties(String code) {
  def instance = FLEXDEPLOY.findIntegrationInstance(code,'ITS');
  def properties = instance.getProperties();
  def url = properties.find { it.getPropertyName() == 'JIRA_URL' }.getPropertyValue();
  def user = properties.find { it.getPropertyName() == 'JIRA_USER_NAME' }.getPropertyValue();
  def password = properties.find { it.getPropertyName() == 'JIRA_PASSWORD' }.getPropertyValue();
  return ['url':url, 'user': user, 'password': password];
}

The next step is to set up our filter to only process failed workflow completed events with Jira issue numbers.

...

Workflow Completed Filter

Code Block
languagegroovy
return "FAILURE".equals(EVENT.payload.executionStatus) && EVENT.payload.issueNumbers.size() > 0;

Create ServiceNow Incident on Workflow Failure

In the below example we make use of the ChangeManagementSystemService to allow us to create an incident through FlexDeploy’s internal ServiceNow integration. The only thing that needs to be configured ahead of time is your ServiceNow instance in Topology->Integrations->ChangeManagement

...

...

Create Incident Script

Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the documentation for reference or ideas.
//Optionally filter out events you dont want to execute by returning false on the filter script tab.
def listenerName = "Create Incident";
LOG.info("Running listener: ${listenerName}");

//throws FlexNotFoundException if SERVICENOW is not found
def cmsService = FLEXDEPLOY.findCMSService("SERVICENOW",null)

def cmsFields = [:];
cmsFields.short_description = "Deployment failed for ${EVENT.payload.project.projectName}";
cmsFields.description = "Deployment failed for ${EVENT.payload.project.projectName}. Environment ${EVENT.payload.environment.environmentName} executionid ${EVENT.payload.workflowExecutionId} requestor ${EVENT.payload.updatedBy}";
 
LOG.fine("Creating Service Now Incident ${cmsFields}");

def cmsObject = cmsService.createIncident(EVENT.payload.workflowRequest.workflowRequestId, cmsFields);
LOG.setMessage("Successfully created Service Now Incident ${cmsObject.getNumber()}");
Info

Configured offscreen is a Filter script for only failed workflows.

...

The Workflow Completed Event includes all of the test execution data for any TEST workflow type. The below example shows how this data can be used by sending a Teams message with the data neatly formatted in an html table.

...

Tests Completed Script

title
Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the documentation for reference or ideas.
//Optionally filter out events you dont want to execute by returning false on the filter script tab.
def listenerName = "Tests Completed";
LOG.info("Running listener: ${listenerName}");

def builder = new StringBuilder();
builder.append("<h1 style=\"padding: 10px 0\">Tests ${EVENT.payload.testRun.runStatus} for ${getProjectLink(EVENT.payload.project.projectName, EVENT.payload.project.projectId)} in ${EVENT.payload.environment.environmentName}</h1>");

builder.append("<table><tr><th style=\"padding: 0 10px 5px 0\">Test Def Name</th><th style=\"padding: 0 10px 5px 0\">Testing Tool</th><th style=\"padding: 0 10px 5px 0\">Test Case Name</th><th style=\"padding: 0 10px 5px 0\">Status</th></tr>");

EVENT.payload.testRun.testSets.each{ testSet ->
  LOG.fine("Processing test set ${testSet.name}");
  
  testSet.testDefinitions.each{ testDef ->
    LOG.fine("Processing test def ${testDef.name}");
    
    def testDefName = testDef.name;
    def testingTool = testDef.testingTool;
    
    testDef.results.each{ testResult -> 
      def testCaseName = testResult.testCaseName;
      def status = testResult.status;
      
      builder.append("<tr><td style=\"padding-right: 10px\">${testDefName}</td><td style=\"padding-right: 10px\">${testingTool}</td><td style=\"padding-right: 10px\">${testCaseName}</td><td style=\"padding-right: 10px\">${status}</td></tr>");
    }
  }
}

builder.append("</table>");

def htmlMessage = builder.toString();

LOG.fine("Sending test results table to Teams: ${htmlMessage}");
MICROSOFTTEAMS.sendTeamsMessage("TEAMS","FD Developers","Testing",htmlMessage,null);

LOG.setMessage("Successfully sent test results message to teams for ${EVENT.payload.project.projectName}");

def getProjectLink(String pProjectName, Long pProjectId)
{
  String link = String.format("%s/flexdeploy/faces/projects?objecttype=Project&projectid=%s&projectname=%s", FLEXDEPLOY.getFlexDeployBaseUrl(), pProjectId, pProjectName);
  return String.format("<a href=%s>%s</a>", link, pProjectName);
}
Expand

Tests Completed Filter

Code Block
languagegroovy
return EVENT.payload.workflow.workflowType == 'TEST';

Naturally you can edit the table/style in any way you wish. The below screen shot is a sample message from the listener:

...

This use case can be helpful when you have a FlexDeploy Project that is dependent on some other Project building first. Generally speaking you would try to handle this on the source control side of things but sometimes that just isn't possible. In the below example we are initiating a build on Project with Id 10241 only after a build has successfully completed for the Project with Id 10002.

...

Build Dependent Project Script

title
Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the documentation for reference or ideas.
//Optionally filter out events you dont want to execute by returning false on the filter script tab.
def listenerName = "Build Dependent Project";
LOG.info("Running listener: ${listenerName}");

def streamId = FLEXDEPLOY.findStreamId(10241L,'master');
FLEXDEPLOY.buildProject(streamId,10241L);

LOG.setMessage("Successfully initiated a build for 10241");
Expand

Build Dependent Project Filter

Code Block
languagegroovy
//the project that should be built first
def initiator = 10002;

//only run when we have a successful build of project 10002
return EVENT.payload.executionStatus == "SUCCESS" && EVENT.payload.workflow.workflowType == "BUILD" && EVENT.payload.project.projectId == initiator;

Send email with logs on Workflow Failed

...

See the incoming samples also. An incoming webhook is needed to capture the button clicks that users perform on the Teams messages and ultimately approve/reject the task in FlexDeploy.

...

Send Teams Approval Message Listener

Code Block
languagegroovy
// Function Script - Create Approvable Tasks in Teams Channels (Outgoing Webhook) - Available in 5.5.0.2
String message = MICROSOFTTEAMS.makeTaskCreatedMessageForWebhook(EVENT.payload,"https://<urlToYourFlexDeployServerOrExternalProxy>/flexdeploy/webhooks/v1/<uri setup in Incoming Webhook>");

String webhookUrlBI = "https://flexagon.webhook.office.com/webhookb2/cb7f2430-...9ee6-98a7eae43f9f";
String webhookUrlMule = "https://flexagon.webhook.office.com/webhookb2/cb7f2430-9...-98a7eae43f9f";
String webhookUrlSF = "https://flexagon.webhook.office.com/webhookb2/cb7f2430-96dc-4....-98a7eae43f9f";

if(condition for BI Approval){
MICROSOFTTEAMS.sendTeamsWebhookMessage(webhookUrlBI,message);
} else if(condition for Mule Approval){
MICROSOFTTEAMS.sendTeamsWebhookMessage(webhookUrlMule,message);
} else if(condition for SF Approval){
MICROSOFTTEAMS.sendTeamsWebhookMessage(webhookUrlSF,message);
}

Execute Utility Project

...

This sample executes a specific FlexDeploy Utility Project called “RunSonar”. In the script below, we are executing the “RunSonar” project whenever a Build workflow with the specified projectId completes successfully. As an extension of the executeUtility function, we can pass ExecuteOptions which allows you to configure which instances the utility project will run on and also the ability to pass inputs and flexfields values to the utility workflow. If no ExecuteOptions are passed as parameters, then the utility will execute on all instances associated in the project configuration.

This function is listening to the ‘Workflow Completed’ Event.

...

Example Groovy Code

Code Block
languagegroovy
//Carry out any custom action you wish for this event. Check out the documentation for reference or ideas.
//Optionally filter out events you dont want to execute by returning false on the filter script tab.
def listenerName = "myListener";
LOG.info("Running listener: ${listenerName}");

def projectId = EVENT.payload.project.projectId;
def projectName = EVENT.payload.project.projectName;
def workflowType = EVENT.payload.workflow.workflowType;
def executionStatus = EVENT.payload.executionStatus;

if (projectId == 516098 && workflowType == "BUILD" && executionStatus == "SUCCESS") {
  LOG.info("Executing Utility Project : ${listenerName}");
  def sonarScanProjectId = FLEXDEPLOY.findProjectId("RunSonar");
  def environmentCode = "DEV";
  
  def workflowRequestId = FLEXDEPLOY.executeUtility(sonarScanProjectId, environmentCode);
  
  if (workflowRequestId) {
    LOG.info("Run Sonar Utility was successfully executed");
  }
  else {
    LOG.severe("Run Sonar Utility failed");
  }

Update Package to Completed After Production Deployment

You may want to change a package’s status to completed to indicate it successfully deployed to its final environment. This webhook handles changing the status for you using the Workflow Completed Event. You could update the filter to only update the status for certain projects, releases, instances, etc. The listener also handles removing the package from its release first, as a package cannot be marked completed if it’s in an active release.

...

Webhook Listener

title
Code Block
languagegroovy
​def releaseName = EVENT.payload.release.releaseName
def projectId = EVENT.payload.project.projectId
def packageName = EVENT.payload.packageName

if (releaseName != null)
{
  FLEXDEPLOY.removeProjectsFromRelease(releaseName, [new flexagon.fd.model.pojos.rest.release.ReleaseProjectsPojo(projectId, packageName, false)])
}

FLEXDEPLOY.updatePackageStatus(projectId, packageName, "COMPLETED")
Expand

Webhook Filter

Code Block
languagegroovy
return "SUCCESS".equals(EVENT.payload.executionStatus) && "PRODUCTION".equals(EVENT.payload.environment.environmentCode) && EVENT.payload.packageName != null