Webhook event tasks
Webhook event tasks are extensible automated tasks that wait for events from both webhooks endpoints and deployment provider webhook sources, 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.
The input options for the Webhook: Wait for Json event task are:
Option | Description |
---|---|
Event Source | Configured HTTP Endpoint for webhooks or webhook sources to listen on. |
Condition | The condition that must be satisfied to transition the task from in-progress to complete. |
Received event | Output 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.
See Deployment Provider Event Source Properties to learn more about the properties you can use to build your condition for deployment provider event sources.
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.
Using the Wait for Deployment Event Task
Use the generic Webhook: Wait for Deployment Event task to listen to deployment events from deployment provider event sources.
The input options for the Webhook: Wait for Deployment Event task are:
Option | Description |
---|---|
Event Source | Configured deployment provider event sources to listen to. |
Application Title | The title of the application to wait for. |
Environment Title | The title of the environment (the application will be deployed to) to wait for. |
Application Version | The version of the application to wait for. |
Deployment Statuses | The deployment status to wait for. Default is success . |
This task, if configured, waits for the matching event on the deployment provider event source and completes when the event occurs.
There are no output properties for this task.
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:
- Define new task type which extends
webhook.ReactiveTask
- Define a
webhookScriptLocation
property which points to the condition script file
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:
- Make
jira.WaitForIssueStatus
type extendjira.JiraScript
- Add back the properties previously provided by the
webhook.ReactiveTask
- theendpoint
andwaitForSignal
properties - Add a new
WaitForIssueStatus.py
script which will perform the initial poll
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
.