Skip to content

Commit

Permalink
JENKINS-49178 Support JIRA Cloud comment event.
Browse files Browse the repository at this point in the history
  • Loading branch information
ceilfors committed Apr 21, 2018
1 parent 30fe790 commit bb6e048
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 89 deletions.
Expand Up @@ -12,7 +12,6 @@ import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import spock.lang.Ignore
import spock.lang.Specification

import static JiraCommentTrigger.DEFAULT_COMMENT
Expand Down Expand Up @@ -61,7 +60,6 @@ class JiraTriggerAcceptanceTest extends Specification {
jenkins.buildShouldBeScheduled('job')
}

@Ignore
def 'Should trigger a build when a comment is added in JIRA cloud'() {
given:
String issueKey = jiraCloud.createIssue()
Expand Down
@@ -1,105 +1,90 @@
{
"timestamp": 1516745644071,
"timestamp": 1524205492300,
"webhookEvent": "comment_created",
"comment": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/issue/38747/comment/43735",
"id": "43735",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/issue/10023/comment/10021",
"id": "10021",
"author": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/user?username=alex",
"name": "alex",
"key": "alex",
"accountId": "557058:93f71831-7090-4c70-ab8e-c9a5c9aa2d8e",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/user?username=admin",
"name": "admin",
"key": "admin",
"accountId": "557058:eb43b4a2-fad1-4ff6-a2d9-38eee0b088b9",
"avatarUrls": {
"48x48": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue",
"24x24": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue",
"16x16": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue",
"32x32": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue"
"48x48": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue",
"24x24": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue",
"16x16": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue",
"32x32": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue"
},
"displayName": "Alex Figueroa ",
"displayName": "Wisen Tanasa",
"active": true,
"timeZone": "America/New_York"
"timeZone": "Europe/London"
},
"body": "Jenkins go home",
"body": "build this please",
"updateAuthor": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/user?username=alex",
"name": "alex",
"key": "alex",
"accountId": "557058:93f71831-7090-4c70-ab8e-c9a5c9aa2d8e",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/user?username=admin",
"name": "admin",
"key": "admin",
"accountId": "557058:eb43b4a2-fad1-4ff6-a2d9-38eee0b088b9",
"avatarUrls": {
"48x48": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue",
"24x24": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue",
"16x16": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue",
"32x32": "https://avatar-cdn.atlassian.com/07be219aa1f894b95a868f2eef9d4e17?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F07be219aa1f894b95a868f2eef9d4e17%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue"
"48x48": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue",
"24x24": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue",
"16x16": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue",
"32x32": "https://avatar-cdn.atlassian.com/e610a58b5e6c136fa2b2f509969b15c3?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fe610a58b5e6c136fa2b2f509969b15c3%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue"
},
"displayName": "Alex Figueroa ",
"displayName": "Wisen Tanasa",
"active": true,
"timeZone": "America/New_York"
"timeZone": "Europe/London"
},
"created": "2018-01-23T17:14:04.071-0500",
"updated": "2018-01-23T17:14:04.071-0500"
"created": "2018-04-20T07:24:52.300+0100",
"updated": "2018-04-20T07:24:52.300+0100"
},
"issue": {
"id": "38747",
"self": "https://sportsrecruits.atlassian.net/rest/api/2/issue/38747",
"key": "SR-4186",
"id": "10023",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/issue/10023",
"key": "TEST-24",
"fields": {
"summary": "Identify and fix email address entry points to strip white space",
"summary": "test",
"issuetype": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/issuetype/3",
"id": "3",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/issuetype/10002",
"id": "10002",
"description": "A task that needs to be done.",
"iconUrl": "https://sportsrecruits.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10318&avatarType=issuetype",
"iconUrl": "https://jira-trigger-plugin.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10318&avatarType=issuetype",
"name": "Task",
"subtask": false,
"avatarId": 10318
},
"project": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/project/10000",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/project/10000",
"id": "10000",
"key": "SR",
"name": "Sports Recruits",
"key": "TEST",
"name": "test",
"projectTypeKey": "software",
"avatarUrls": {
"48x48": "https://sportsrecruits.atlassian.net/secure/projectavatar?pid=10000&avatarId=11300",
"24x24": "https://sportsrecruits.atlassian.net/secure/projectavatar?size=small&pid=10000&avatarId=11300",
"16x16": "https://sportsrecruits.atlassian.net/secure/projectavatar?size=xsmall&pid=10000&avatarId=11300",
"32x32": "https://sportsrecruits.atlassian.net/secure/projectavatar?size=medium&pid=10000&avatarId=11300"
"48x48": "https://jira-trigger-plugin.atlassian.net/secure/projectavatar?pid=10000&avatarId=10400",
"24x24": "https://jira-trigger-plugin.atlassian.net/secure/projectavatar?size=small&pid=10000&avatarId=10400",
"16x16": "https://jira-trigger-plugin.atlassian.net/secure/projectavatar?size=xsmall&pid=10000&avatarId=10400",
"32x32": "https://jira-trigger-plugin.atlassian.net/secure/projectavatar?size=medium&pid=10000&avatarId=10400"
}
},
"assignee": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/user?username=Paul",
"name": "Paul",
"key": "paul",
"accountId": "5a43c904b66c2f374530f7b7",
"emailAddress": "paul@sportsrecruits.com",
"avatarUrls": {
"48x48": "https://avatar-cdn.atlassian.com/d7a71cabea4d48b35a499dd3a68a5fcb?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fd7a71cabea4d48b35a499dd3a68a5fcb%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue",
"24x24": "https://avatar-cdn.atlassian.com/d7a71cabea4d48b35a499dd3a68a5fcb?s=24&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fd7a71cabea4d48b35a499dd3a68a5fcb%3Fd%3Dmm%26s%3D24%26noRedirect%3Dtrue",
"16x16": "https://avatar-cdn.atlassian.com/d7a71cabea4d48b35a499dd3a68a5fcb?s=16&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fd7a71cabea4d48b35a499dd3a68a5fcb%3Fd%3Dmm%26s%3D16%26noRedirect%3Dtrue",
"32x32": "https://avatar-cdn.atlassian.com/d7a71cabea4d48b35a499dd3a68a5fcb?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fd7a71cabea4d48b35a499dd3a68a5fcb%3Fd%3Dmm%26s%3D32%26noRedirect%3Dtrue"
},
"displayName": "Paul Cox",
"active": true,
"timeZone": "America/New_York"
},
"assignee": null,
"priority": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/priority/3",
"iconUrl": "https://sportsrecruits.atlassian.net/images/icons/priorities/major.svg",
"name": "Major",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/priority/3",
"iconUrl": "https://jira-trigger-plugin.atlassian.net/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"status": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/status/10500",
"description": "This is managed by JIRA",
"iconUrl": "https://sportsrecruits.atlassian.net/images/icons/statuses/generic.png",
"name": "QA on stage",
"id": "10500",
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/status/10000",
"description": "",
"iconUrl": "https://jira-trigger-plugin.atlassian.net/",
"name": "To Do",
"id": "10000",
"statusCategory": {
"self": "https://sportsrecruits.atlassian.net/rest/api/2/statuscategory/4",
"id": 4,
"key": "indeterminate",
"colorName": "yellow",
"name": "In Progress"
"self": "https://jira-trigger-plugin.atlassian.net/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "To Do"
}
}
}
Expand Down
Expand Up @@ -106,7 +106,10 @@ class RealJiraSetupRule extends ExternalResource {
url : "http://${vagrantHostDefaultIp}:8080/${jenkinsRunner.jiraWebhook.urlName}/"],
].each {
webhookRestClient.registerWebhook(
new WebhookInput(name: it.name, events: [JiraWebhook.WEBHOOK_EVENT], url: it.url)).claim()
new WebhookInput(name: it.name, events: [
JiraWebhook.ISSUE_UPDATED_WEBHOOK_EVENT,
JiraWebhook.COMMENT_CREATED_WEBHOOK_EVENT
], url: it.url)).claim()
}
}

Expand Down
Expand Up @@ -21,7 +21,8 @@ import java.util.logging.Level
class JiraWebhook implements UnprotectedRootAction {

public static final URL_NAME = 'jira-trigger-webhook-receiver'
public static final WEBHOOK_EVENT = 'jira:issue_updated'
public static final ISSUE_UPDATED_WEBHOOK_EVENT = 'jira:issue_updated'
public static final COMMENT_CREATED_WEBHOOK_EVENT = 'comment_created'
private JiraWebhookListener jiraWebhookListener

@Inject
Expand Down Expand Up @@ -101,11 +102,12 @@ class JiraWebhook implements UnprotectedRootAction {
}

boolean isChangelogEvent() {
eventType == WEBHOOK_EVENT && webhookEventMap['changelog']
eventType == ISSUE_UPDATED_WEBHOOK_EVENT && webhookEventMap['changelog']
}

boolean isCommentEvent() {
eventType == WEBHOOK_EVENT && webhookEventMap['comment']
(eventType == ISSUE_UPDATED_WEBHOOK_EVENT
|| eventType == COMMENT_CREATED_WEBHOOK_EVENT) && webhookEventMap['comment']
}

String getUserId() {
Expand Down
Expand Up @@ -9,6 +9,8 @@ import com.atlassian.jira.rest.client.internal.json.JsonParseUtil
import org.codehaus.jettison.json.JSONException
import org.codehaus.jettison.json.JSONObject

import static com.ceilfors.jenkins.plugins.jiratrigger.webhook.WebhookJsonParserUtils.satisfyRequiredKeys

/**
* @author ceilfors
*/
Expand All @@ -23,14 +25,14 @@ class WebhookChangelogEventJsonParser implements JsonObjectParser<WebhookChangel

@Override
WebhookChangelogEvent parse(JSONObject json) throws JSONException {
satisfyRequiredKeys(json)

Collection<ChangelogItem> items = JsonParseUtil.parseJsonArray(
json.getJSONObject('changelog').getJSONArray('items'), changelogItemJsonParser)
JSONObject issue = json.getJSONObject('issue')
issue.put('expand', '') // Webhook event doesn't have expand
new WebhookChangelogEvent(
json.getLong('timestamp'),
json.getString('webhookEvent'),
issueJsonParser.parse(issue),
issueJsonParser.parse(json.getJSONObject('issue')),
new ChangelogGroup(null, null, items)
)
}
Expand Down
Expand Up @@ -6,21 +6,37 @@ import com.atlassian.jira.rest.client.internal.json.JsonObjectParser
import org.codehaus.jettison.json.JSONException
import org.codehaus.jettison.json.JSONObject

import static com.ceilfors.jenkins.plugins.jiratrigger.webhook.WebhookJsonParserUtils.putIfAbsent
import static com.ceilfors.jenkins.plugins.jiratrigger.webhook.WebhookJsonParserUtils.satisfyRequiredKeys

/**
* @author ceilfors
*/
class WebhookCommentEventJsonParser implements JsonObjectParser<WebhookCommentEvent> {

private static final DATE_FIELD_NOT_EXIST = '1980-01-01T00:00:00.000+0000'
private static final ISSUE_KEY = 'issue'

private final IssueJsonParser issueJsonParser = new IssueJsonParser(new JSONObject([:]), new JSONObject([:]))

/**
* Fills details needed by JRC JSON Parser that are missing in JIRA Cloud Webhook events.
*/
private static void satisfyCloudRequiredKeys(JSONObject json) {
JSONObject fields = json.getJSONObject(ISSUE_KEY).getJSONObject('fields')
putIfAbsent(fields, 'created', DATE_FIELD_NOT_EXIST)
putIfAbsent(fields, 'updated', DATE_FIELD_NOT_EXIST)
}

@Override
WebhookCommentEvent parse(JSONObject json) throws JSONException {
JSONObject issue = json.getJSONObject('issue')
issue.put('expand', '') // Webhook event doesn't have expand
satisfyRequiredKeys(json)
satisfyCloudRequiredKeys(json)

new WebhookCommentEvent(
json.getLong('timestamp'),
json.getString('webhookEvent'),
issueJsonParser.parse(issue),
issueJsonParser.parse(json.getJSONObject(ISSUE_KEY)),
new CommentJsonParser().parse(json.getJSONObject('comment'))
)
}
Expand Down
@@ -0,0 +1,23 @@
package com.ceilfors.jenkins.plugins.jiratrigger.webhook

import org.codehaus.jettison.json.JSONObject

/**
* @author ceilfors
*/
class WebhookJsonParserUtils {

/**
* Fills details needed by JRC JSON Parser that are missing in Webhook events.
*/
static void satisfyRequiredKeys(JSONObject json) {
JSONObject issue = json.getJSONObject('issue')
putIfAbsent(issue, 'expand', '')
}

static void putIfAbsent(JSONObject jsonObject, String key, Object value) {
if (!jsonObject.opt(key)) {
jsonObject.put(key, value)
}
}
}

0 comments on commit bb6e048

Please sign in to comment.