Custom Sprint Report: getting sprint info from Java API (JIRA Software v7+ server) Edited

Summary

I wanted to share some of my trials and tribulations while working on a custom sprint report for Jira Software (v7.3) server using the Plugins2 framework, in case there were others trying to do the same.

Context

I started this project before knowing that the agile features of JIRA Software are not exposed in the Java API. After some digging found suggestions to use the REST API, which did have access to agile entities. This involved building a little HTTP client in my code from scratch (since Jira Java REST API Client is not supported past JIRA v6 because of course it isn't).

Important part

While I was testing my client however, I found that in fact, you can get a good amount of information about sprints from the Java API using the getValue() method of the CustomField class. That is, you can get access to an instance of CustomField object that represents an issue's sprints from the CustomFieldManager class (provided by the ComponentAccessor class, for example), and then call the getValue() method, which returns a list of info about the sprint. You can then parse the list for the needed info (using regex patterns, for example).

An example of the output of getValue() is shown below. This issue was in two sprints, Sprint 1 and Sprint 2. Sprint 1 is closed and started on 2017-05-31, while Sprint 2 is a Future sprint with no start date, etc.

[com.atlassian.greenhopper.service.sprint.Sprint@77e7bf34[id=225,rapidViewId=161,state=CLOSED,name=Sprint 1,startDate=2017-05-31T10:31:07.779-05:00,endDate=2017-06-07T10:31:00.000-05:00,completeDate=2017-12-11T14:12:16.082-06:00,sequence=225], com.atlassian.greenhopper.service.sprint.Sprint@68792939[id=400,rapidViewId=161,state=FUTURE,name=Sprint 2,startDate=,endDate=,completeDate=,sequence=400]]

Conclusion

If the report you're making requires any of this info, you don't need to access the REST API, and can get it from the Java API.

  • Sprint name
  • Agile board ID
  • Sprint state
  • Sprint start date
  • Sprint end date
  • Sprint completed date

I hope this info is helpful for anyone in my position who just wants to make some basic custom sprint reports!


References

3 comments

Thanks for posting! This got me started on the right track. I ended up also going down the rabbit hole of casting the getValue() Object to an  ArrayList<Sprint> instead of parsing the toString() output.

Could you please paste the part of your code with the CustomFields here?

 

I am getting a similar output by using:

 IssueField field = issue.getFieldByName("Sprint");

field.getValue();

 

The problem is, I do not know how I could extract the attributes from the following output:

 

[com.atlassian.greenhopper.service.sprint.Sprint@77e7bf34[id=225,rapidViewId=161,state=CLOSED,name=Sprint 1,startDate=2017-05-31T10:31:07.779-05:00,endDate=2017-06-07T10:31:00.000-05:00,completeDate=2017-12-11T14:12:16.082-06:00,sequence=225], com.atlassian.greenhopper.service.sprint.Sprint@68792939[id=400,rapidViewId=161,state=FUTURE,name=Sprint 2,startDate=,endDate=,completeDate=,sequence=400]]

 

Any suggestion?

The easy way is just to parse the string returned from getValue().toString() - what you pasted.

Alternatively, after a little extra work you can cast the object returned by getValue() to what it actually is: an ArrayList<Sprint>. This requires adding a dependency on the library that contains the definition of the Sprint class: com.atlassian.greenhopper.service.sprint.*. This page goes into detail on that topic https://community.atlassian.com/t5/Answers-Developer-Questions/Access-com-atlassian-greenhopper-Java-API/qaq-p/566539

Like 1 person likes this

Many thanks for replying.

 

1 Method)

By parsing you mean, writing my self a method that tries to get the attributes somehow from the String? 

 

2 Method)

I have tried the following:

ArrayList<Sprint> sprints = field.getValue();

but I am getting the following error:

Incompatible types.
Required: java.util.ArrayList<com.atlassian.greenhopper.service.sprint.Sprint>
Found: java.lang.Object

 

and 

ArrayList<Sprint> sprints = (ArrayList<Sprint>) field.getValue();

Exception in thread "main" java.lang.ClassCastException:

class org.codehaus.jettison.json.JSONArray cannot be cast to class java.util.ArrayList (org.codehaus.jettison.json.JSONArray is in unnamed module of loader 'app'; java.util.ArrayList is in module java.base of loader 'bootstrap')

With 

field.getValue().getClass() 

 I am getting ---> org.codehaus.jettison.json.JSONArray

After trying though

JSONArray jsonArray = new JSONArray(field.getValue());

I am getting --> Cannot resolve constructor 'JSONArray(java.lang.Object)' 

I am kind of lost right now.

1 Method )

Yes. Using a regex string as suggested in the original post.

2 Method)

I will just paste my example code. This may not be drop-in-ready in your environment, but should provide you enough context to get you over the hump. Note you will need to `import com.atlassian.greenhopper.service.sprint.*;` which requires following the directions in the original link I posted.

public void validate(Map transientVars, Map args, PropertySet ps) throws InvalidInputException {
Issue issue = (Issue) transientVars.get("issue");
log.debug("Processing issue '" + issue.getKey() + "'");

List<CustomField> customFields = customFieldManager.getCustomFieldObjects(issue);
log.debug("Number of fields in issue '" + customFields.size() + "'");

for (CustomField field : customFields) {
log.debug("Checking field name '" + field.getFieldName() + "'");

if (field.getFieldName().equals("Sprint")) {
log.debug("Found 'Sprint' field");

Object fieldValue = field.getValue(issue);

if (fieldValue == null) {
log.debug("Sprint value is null");
// TODO: throw exception
} else {
// Get list of sprints this issue has been assigned to
ArrayList<Sprint> sprintList = (ArrayList<Sprint>) fieldValue;

log.debug("The issue has been assigned to the following sprints: ");
for(Sprint sprint : sprintList) {
log.debug("Name = '" + sprint.getName() + "' State = '" + sprint.getState() + "'");
}

// Are any of them active?
boolean anyActiveSprints = Iterables.any(sprintList,
new Predicate<Sprint>() {
@Override
public boolean apply(Sprint sprint) {
return sprint.isActive();
}
});

if (!anyActiveSprints) {
log.debug("None of the assigned sprints are active.");
// TODO: throw exception
}
}
}
}
}

 Sorry for the poor style there - this site is removing all my indentation spaces .

Like 1 person likes this

Thank you again for replying.

 

How do you even get the Class:

customFieldManager

 ? 

When I import with Gradle the following lib

compile group: 'com.atlassian.jira', name: 'jira-api', version: '7.6.1'

from repository

maven { url'http://repo.spring.io/plugins-release/'}

I get constantly the following error:

jta:jta:1.0.1 FAILED ->Build: ...\Could not find jta:jta:1.0.1. 

I have tried to download only the jta lib and install it, but it still doesnt work.

I also have tried the newer version: 

compile group: 'com.atlassian.jira', name: 'jira-api', version: '7.11.2'

but I still get the same error.

To that I can only write a part of your code without gettint an error like this:

List<CustomField> customFields = ComponentAccessor.getCustomFieldManager().getCustomFieldObjects((com.atlassian.jira.issue.Issue) issue);

so by additionally using the "ComponentAccessor".

 

I do not understand why "JIRA" made the extraction of Data so difficult. It just doesnt make any sense to me.

Comment

Log in or Sign up to comment
Community showcase
Published Jan 08, 2019 in Jira

How to Jira for designers

I’m a designer on the Jira team. For a long time, I’ve fielded questions from other designers about how they should be using Jira Software with their design team. I’ve also heard feedback from other ...

1,006 views 4 9
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you