Skip to main content
Version: Release 22.3

Webhook Event Tasks

Webhook event tasks are extensible automated tasks that wait for events from webhooks endpoints and complete when the incoming event matches the specified condition.

Using the Wait For Json Event task

In Digital.ai Release there is a supplied generic webhook task Webhook: Wait for Json event.

Webhook task wait for json event example

The input options for the Webhook: Wait for Json event task are:

OptionDescription
EndpointConfigured HTTP Endpoint for webhooks to listen on.
ConditionThe condition that must be satisfied to transition the task from in-progress to complete.
Received eventOutput property bound to a text variable that will store the JSON of the webhook event (optional).
note

The task will listen for incoming events only while it is in the In progress state.

Condition script

The webhook task condition is a Jython script that works similar to the Webhook event trigger filter rule:

If the script returns True, the task will complete.

If the script returns False, the task will stay in progress.

If there is an error with the script, the task will fail.

In the script, you can access the Release Jython API, and the following properties: event, headers, parameters. event is the parsed JSON body of the webhook event, while headers and parameters are the HTTP request headers and parameters.

The event, headers, parameters data is also accessible using dot access, e.g. event.commits[0].author, but the regular dict access is still available.

Define a custom webhook event task

In order to reduce repetition of event matching logic, you can define your own webhook task type in the synthetic.xml. All webhook event tasks are a sub-type of Custom script task. All rules concerning custom script tasks are valid for webhook event tasks.

To define a custom webhook event task, you need to:

  1. Define new task type which extends webhook.ReactiveTask
  2. Define a webhookScriptLocation property which points to the condition script file

Example of a custom webhook event task:

Custom webhook event task example

The synthetic.xml type definition:

<synthetic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.xebialabs.com/deployit/synthetic"
xsi:schemaLocation="http://www.xebialabs.com/deployit/synthetic synthetic.xsd">


<type type="jira.WaitForIssueStatus" extends="webhook.ReactiveTask">
<property category="input" name="issueKey" kind="string"/>
<property category="input" name="issueStatus" kind="string" default="Closed"/>

<property category="output" name="resolution" kind="string"/>
<property category="output" name="resolutionDate" kind="date"/>

<property hidden="true" transient="true" name="webhookScriptLocation" default="jira/WaitForIssueStatusWebhook.py" />
</type>
</synthetic>

The WaitForIssueStatusWebhook.py script:

global issueKey, issueStatus, resolution, resolutionDate

if not issueKey:
raise Exception("Issue key must be defined")

if not issueStatus:
raise Exception("Expected issue status to wait for must be defined")

print "Matching new event: %s" % payload

result = event.webhookEvent == "jira:issue_updated" and event.issue.key == issueKey and event.issue.fields.status.name == issueStatus
if result:
print "Issue %s has successfully transitioned to status %s" % (issueKey, issueStatus)
resolution = event.issue.fields.resolution.name
resolutionDate = event.issue.fields.resolutiondate
else:
print "Event did not match expected conditions"

Further, it is possible to design a custom webhook event task which, when starting, does an initial pull of the external system. If the condition has already been satisfied, the task will transition directly into the Completed state. Otherwise, it will stay In progress until a matching JSON event gets received on the configured HTTP endpoint event source.

We can quickly update the example jira.WaitForIssueStatus task and reuse some JIRA plugin base types and utility classes:

  1. Make jira.WaitForIssueStatus type extend jira.JiraScript
  2. Add back the properties previously provided by the webhook.ReactiveTask - the endpoint and waitForSignal properties
  3. Add a new WaitForIssueStatus.py script which will perform the initial poll

Example of the updated task:

Updated webhook event task example

The synthetic.xml type definition:

<synthetic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.xebialabs.com/deployit/synthetic"
xsi:schemaLocation="http://www.xebialabs.com/deployit/synthetic synthetic.xsd">


<type type="jira.WaitForIssueStatus" extends="jira.JiraScript">
<property category="input" name="endpoint" kind="ci" referenced-type="events.WebhookEndpoint" />
<property category="input" name="issueKey" kind="string"/>
<property category="input" name="issueStatus" kind="string" default="Closed"/>

<property category="output" name="resolution" kind="string"/>
<property category="output" name="resolutionDate" kind="date"/>

<property category="script" name="waitForSignal" kind="boolean" default="false"/>

<property hidden="true" transient="true" name="scriptLocation" default="jira/WaitForIssueStatus.py" />
<property hidden="true" transient="true" name="webhookScriptLocation" default="jira/WaitForIssueStatusWebhook.py" />
</type>

</synthetic>

The WaitForIssueStatus.py script:

global issueKey, issueStatus, resolution, resolutionDate

import sys
import com.xhaus.jyson.JysonCodec as json
from jira import JiraServer

if not jiraServer:
raise Exception("JIRA server must be defined")

if not issueKey:
raise Exception("Issue key must be defined")

if not issueStatus:
raise Exception("Expected issue status to wait for must be defined")

ISSUE_RETREIVED_STATUS = 200

jira = JiraServer(jiraServer, username, password, apiToken)

request = jira._createRequest()

statusTask = jira._getVersionUri() + '/issue/' + issueKey

response = request.get(statusTask, contentType='application/json', headers=jira._createApiTokenHeader())

# if response received from Jira
if response.getStatus() == ISSUE_RETREIVED_STATUS:
# retrieve issue status
issueData = json.loads(response.getResponse())
currentStatus = issueData['fields']['status']['name']
print "\nThe status of issue %s is %s" % (issueKey, currentStatus)
if currentStatus == issueStatus:
print "\nIssue is in expected status, completing task now"
resolution = issueData['fields']['resolution']['name']
resolutionDate = issueData['fields']['resolutiondate']
waitForSignal = False
else:
print "\nIssue is not in expected status yet, will wait for matching event"
waitForSignal = True
else:
print "Error from JIRA, HTTP Return: %s" % (response.getStatus())
response.errorDump()
sys.exit(1)

Depending on the status of the ticket, the WaitForIssueStatus.py script will either complete the task by setting the waitForSignal flag to False or will stay in progress and pass the control to the WaitForIssueStatusWebhook.py script by setting the waitForSignal flag to True.