I created a JIRA bug and sent the subsequent generated event to a SpringBoot a via webhook. That works so far. In the SpringBoot app I tried to deserialize the received JSON. To achieve this I have an IssueEvent class which has an Issue field:
package my.package.domain.jira.entity;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.atlassian.jira.rest.client.api.domain.ChangelogGroup;
import com.atlassian.jira.rest.client.api.domain.Issue;
import com.atlassian.jira.rest.client.api.domain.User;
/**
* RootObject JSON
*
*/
public class IssueEvent implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4467263405878138432L;
private Long timestamp;
private String webhookEvent;
@JsonProperty("issue_event_type_name")
private String issueEventTypeName;
private User user;
private Issue issue;
// private Changelog changelog;
private ChangelogGroup changelog;
... }
Here's the JSON generated by JIRA when creating a bug/issue:
{
"timestamp": 1516646881687,
"webhookEvent": "jira:issue_created",
"issue_event_type_name": "issue_created",
"user": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"issue": {
"id": "10610",
"self": "http://localhost:9090/rest/api/2/issue/10610",
"key": "COVA-77",
"fields": {
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
},
"components": [],
"timespent": null,
"timeoriginalestimate": null,
"description": "Teeest",
"project": {
"self": "http://localhost:9090/rest/api/2/project/10000",
"id": "10000",
"key": "COVA",
"name": "CoVA",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324",
"24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324",
"16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324",
"32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324"
}
},
"fixVersions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"aggregatetimespent": null,
"resolution": null,
"timetracking": {},
"customfield_10005": "0|i000dj:",
"attachment": [],
"aggregatetimeestimate": null,
"resolutiondate": null,
"workratio": -1,
"summary": "Bug-Tester",
"lastViewed": null,
"watches": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-77/watchers",
"watchCount": 0,
"isWatching": false
},
"creator": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"subtasks": [],
"created": "2018-01-22T19:48:01.494+0100",
"reporter": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "blabla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"customfield_10000": null,
"aggregateprogress": {
"progress": 0,
"total": 0
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"labels": [
"Bug",
"Test"
],
"customfield_10004": [
"com.atlassian.greenhopper.service.sprint.Sprint@327e23ff[id=1,rapidViewId=1,state=ACTIVE,name=Sample Sprint 2,startDate=2017-11-30T02:29:06.805+01:00,endDate=2017-12-14T02:49:06.805+01:00,completeDate=<null>,sequence=1,goal=<null>]"
],
"environment": "Buuuug",
"timeestimate": null,
"aggregatetimeoriginalestimate": null,
"versions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"duedate": null,
"progress": {
"progress": 0,
"total": 0
},
"comment": {
"comments": [],
"maxResults": 0,
"total": 0,
"startAt": 0
},
"issuelinks": [
{
"id": "10410",
"self": "http://localhost:9090/rest/api/2/issueLink/10410",
"type": {
"id": "10000",
"name": "Blocks",
"inward": "is blocked by",
"outward": "blocks",
"self": "http://localhost:9090/rest/api/2/issueLinkType/10000"
},
"outwardIssue": {
"id": "10609",
"key": "COVA-76",
"self": "http://localhost:9090/rest/api/2/issue/10609",
"fields": {
"summary": "254z245z245z2",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
}
}
}
}
],
"votes": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-77/votes",
"votes": 0,
"hasVoted": false
},
"worklog": {
"startAt": 0,
"maxResults": 20,
"total": 0,
"worklogs": []
},
"assignee": null,
"updated": "2018-01-22T19:48:01.494+0100",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
}
}
},
"changelog": {
"id": "10720",
"items": [
{
"field": "Link",
"fieldtype": "jira",
"from": null,
"fromString": null,
"to": "COVA-76",
"toString": "This issue blocks COVA-76"
}
]
}
}
Since the JIRA classes don't have a prameterless default constructors I tried to use Mixins. It works well for the User but seems not for Issue class:
package my.package.domain.jira.util;
import java.net.URI;
import java.util.Collection;
import java.util.Set;
import org.joda.time.DateTime;
import com.atlassian.jira.rest.client.api.domain.Attachment;
import com.atlassian.jira.rest.client.api.domain.BasicComponent;
import com.atlassian.jira.rest.client.api.domain.BasicPriority;
import com.atlassian.jira.rest.client.api.domain.BasicProject;
import com.atlassian.jira.rest.client.api.domain.BasicVotes;
import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
import com.atlassian.jira.rest.client.api.domain.ChangelogGroup;
import com.atlassian.jira.rest.client.api.domain.Comment;
import com.atlassian.jira.rest.client.api.domain.IssueField;
import com.atlassian.jira.rest.client.api.domain.IssueLink;
import com.atlassian.jira.rest.client.api.domain.IssueType;
import com.atlassian.jira.rest.client.api.domain.Operations;
import com.atlassian.jira.rest.client.api.domain.Resolution;
import com.atlassian.jira.rest.client.api.domain.Status;
import com.atlassian.jira.rest.client.api.domain.Subtask;
import com.atlassian.jira.rest.client.api.domain.TimeTracking;
import com.atlassian.jira.rest.client.api.domain.User;
import com.atlassian.jira.rest.client.api.domain.Version;
import com.atlassian.jira.rest.client.api.domain.Worklog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
*/
public abstract class IssueMixin {
@JsonCreator
public IssueMixin(@JsonProperty("summary") String summary, @JsonProperty("self") URI self, @JsonProperty("key") String key,
@JsonProperty("id") Long id, @JsonProperty("project") BasicProject project, @JsonProperty("issueType") IssueType issueType,
@JsonProperty("status") Status status, @JsonProperty("description") String description, @JsonProperty("priority") BasicPriority priority,
@JsonProperty("resolution") Resolution resolution, @JsonProperty("attachments") Collection<Attachment> attachments, @JsonProperty("reporter") User reporter,
@JsonProperty("assignee") User assignee, @JsonProperty("creationDate") DateTime creationDate, @JsonProperty("updateDate") DateTime updateDate,
@JsonProperty("dueDate") DateTime dueDate, @JsonProperty("affectedVersion") Collection<Version> affectedVersion,
@JsonProperty("fixVersions") Collection<Version> fixVersions, @JsonProperty("components") Collection<BasicComponent> components,
@JsonProperty("timeTracking") TimeTracking timeTracking, @JsonProperty("fields") Collection<IssueField> issueFields,
@JsonProperty("comments") Collection<Comment> comments, @JsonProperty("transitionsUri") URI transitionsUri,
@JsonProperty("issueLinks") Collection<IssueLink> issueLinks, @JsonProperty("votes") BasicVotes votes, @JsonProperty("worklogs") Collection<Worklog> worklogs,
@JsonProperty("watchers") BasicWatchers watchers, @JsonProperty("expandos") Iterable<String> expandos, @JsonProperty("subtasks") Collection<Subtask> subtasks,
@JsonProperty("changelog") Collection<ChangelogGroup> changelog, @JsonProperty("operations") Operations operations, @JsonProperty("labels") Set labels ) {}
}
This Mixin matches exactly the constructor of class Issue from package com.atlassian.jira.rest.client.api.domain.
I get the following Exception:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: {
"timestamp": 1516692474575,
"webhookEvent": "jira:issue_created",
"issue_event_type_name": "issue_created",
"user": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"issue": {
"id": "10623",
"self": "http://localhost:9090/rest/api/2/issue/10623",
"key": "COVA-90",
"fields": {
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
},
"components": [],
"timespent": null,
"timeoriginalestimate": null,
"description": "dhmedhmed",
"project": {
"self": "http://localhost:9090/rest/api/2/project/10000",
"id": "10000",
"key": "COVA",
"name": "CoVA",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/projectavatar?avatarId=10324",
"24x24": "http://localhost:9090/secure/projectavatar?size=small&avatarId=10324",
"16x16": "http://localhost:9090/secure/projectavatar?size=xsmall&avatarId=10324",
"32x32": "http://localhost:9090/secure/projectavatar?size=medium&avatarId=10324"
}
},
"fixVersions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"aggregatetimespent": null,
"resolution": null,
"timetracking": {},
"customfield_10005": "0|i000gf:",
"attachment": [],
"aggregatetimeestimate": null,
"resolutiondate": null,
"workratio": -1,
"summary": "jrzj",
"lastViewed": null,
"watches": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-90/watchers",
"watchCount": 0,
"isWatching": false
},
"creator": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"subtasks": [],
"created": "2018-01-23T08:27:54.388+0100",
"reporter": {
"self": "http://localhost:9090/rest/api/2/user?username=bla",
"name": "bla",
"key": "abla",
"emailAddress": "a.b@xyz.com",
"avatarUrls": {
"48x48": "http://localhost:9090/secure/useravatar?avatarId=10336",
"24x24": "http://localhost:9090/secure/useravatar?size=small&avatarId=10336",
"16x16": "http://localhost:9090/secure/useravatar?size=xsmall&avatarId=10336",
"32x32": "http://localhost:9090/secure/useravatar?size=medium&avatarId=10336"
},
"displayName": "bla",
"active": true,
"timeZone": "Europe/Berlin"
},
"customfield_10000": null,
"aggregateprogress": {
"progress": 0,
"total": 0
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"labels": [
"Test",
"Bug"
],
"customfield_10004": [
"com.atlassian.greenhopper.service.sprint.Sprint@14d495fe[id=1,rapidViewId=1,state=ACTIVE,name=Sample Sprint 2,startDate=2017-11-30T02:29:06.805+01:00,endDate=2017-12-14T02:49:06.805+01:00,completeDate=<null>,sequence=1,goal=<null>]"
],
"environment": "dghndghmnedghmn",
"timeestimate": null,
"aggregatetimeoriginalestimate": null,
"versions": [
{
"self": "http://localhost:9090/rest/api/2/version/10000",
"id": "10000",
"name": "Version 1.0",
"archived": false,
"released": true,
"releaseDate": "2017-11-29"
}
],
"duedate": null,
"progress": {
"progress": 0,
"total": 0
},
"comment": {
"comments": [],
"maxResults": 0,
"total": 0,
"startAt": 0
},
"issuelinks": [
{
"id": "10421",
"self": "http://localhost:9090/rest/api/2/issueLink/10421",
"type": {
"id": "10000",
"name": "Blocks",
"inward": "is blocked by",
"outward": "blocks",
"self": "http://localhost:9090/rest/api/2/issueLinkType/10000"
},
"outwardIssue": {
"id": "10616",
"key": "COVA-83",
"self": "http://localhost:9090/rest/api/2/issue/10616",
"fields": {
"summary": "fj,t.uoöpuz.ötultl",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
},
"priority": {
"self": "http://localhost:9090/rest/api/2/priority/3",
"iconUrl": "http://localhost:9090/images/icons/priorities/medium.svg",
"name": "Medium",
"id": "3"
},
"issuetype": {
"self": "http://localhost:9090/rest/api/2/issuetype/10004",
"id": "10004",
"description": "Ein Problem, das die Funktionen des Produkts beeinträchtigt oder verhindert.",
"iconUrl": "http://localhost:9090/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype",
"name": "Bug",
"subtask": false,
"avatarId": 10303
}
}
}
}
],
"votes": {
"self": "http://localhost:9090/rest/api/2/issue/COVA-90/votes",
"votes": 0,
"hasVoted": false
},
"worklog": {
"startAt": 0,
"maxResults": 20,
"total": 0,
"worklogs": []
},
"assignee": null,
"updated": "2018-01-23T08:27:54.388+0100",
"status": {
"self": "http://localhost:9090/rest/api/2/status/10000",
"description": "",
"iconUrl": "http://localhost:9090/",
"name": "Aufgaben",
"id": "10000",
"statusCategory": {
"self": "http://localhost:9090/rest/api/2/statuscategory/2",
"id": 2,
"key": "new",
"colorName": "blue-gray",
"name": "Aufgaben"
}
}
}
},
"changelog": {
"id": "10742",
"items": [
{
"field": "Link",
"fieldtype": "jira",
"from": null,
"fromString": null,
"to": "COVA-83",
"toString": "This issue blocks COVA-83"
}
]
}
}; line: 1, column: 709] (through reference chain: de.vwncova.defectadapter.domain.jira.entity.IssueEvent["issue"]->com.atlassian.jira.rest.client.api.domain.Issue["fields"])
It seems as if the JSON generated from JIRA does not its own class Issue.
How can I solve this problem?
Try to use com.atlassian.jira.rest.client.internal.json.IssueJsonParser from atlassian-jira-rest-java-client
Something like:
private Issue parseIssue(final String resourcePath) throws JSONException {
final JSONObject issueJson = ResourceUtil.getJsonObjectFromResource(resourcePath);
final IssueJsonParser parser = new IssueJsonParser();
return parser.parse(issueJson);
}
Okay...I see that I don't need ResourceUtil.getJsonObjectFromResource because I do already have it because the JSON comes as parameter...
Is the JSONObject still available in Atlassian JIRA 7.1.6?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I am using JIRA 7.1.6.
I found out that the IssueEvent class is in jira-api but when I use this dependency in my POM the build fails with an exception that dependency jta 1.0.1 cannot be found...and it doesn't work when I put the jta dependency in my POM a well.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Okay...I had to exclude the JTA portion from the jira-api dependency.
However, I still can't get the JSON deserialized to the JIRA domain objects...
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.