I'm trying to access the list of GreenHopper Sprints, with associated metadata (name and dates), from workflow post-functions. Can it be done?
What I'm trying to do is simple: automatically set the GH Sprint field when resolving an issue that was not planned (i.e. included in a sprint).
Thanks in advance,
David.
All right, I've implemented the solution. I thought I'd share it with the rest of the community:
/** * */ import org.apache.commons.httpclient.HttpClient import org.apache.commons.httpclient.methods.GetMethod import org.apache.commons.httpclient.HttpException import org.apache.commons.httpclient.HttpStatus import groovy.json.JsonSlurper import com.atlassian.jira.ComponentManager import com.atlassian.jira.issue.CustomFieldManager import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.util.ImportUtils import com.atlassian.jira.issue.util.IssueChangeHolder import com.atlassian.jira.issue.util.DefaultIssueChangeHolder import com.atlassian.jira.issue.ModifiedValue import com.atlassian.jira.issue.index.IssueIndexManager import com.atlassian.jira.issue.customfields.view.CustomFieldParams import com.atlassian.jira.issue.customfields.view.CustomFieldParamsImpl import com.atlassian.jira.issue.customfields.CustomFieldType import org.apache.log4j.Category import com.atlassian.jira.issue.history.ChangeItemBean //create a class to wrap our post-function, so that it can be debugged in a Groovy debugger class AddCurrentSprintPostFunctionClass { private static Category log = Category.getInstance("com.polyspot.AddCurrentSprintPostFunction") static void execute(Map transientVars, Issue issue, IssueChangeHolder changeHolder) { ComponentManager componentManager = ComponentManager.getInstance() CustomFieldManager customFieldManager = componentManager.getCustomFieldManager() IssueIndexManager indexManager = ComponentManager.getInstance().getIndexManager() //get the Sprints the isssue is already in CustomField cfSource = customFieldManager.getCustomFieldObject("customfield_11200") //Sprint field id - may need to be adjusted for each Jira instance CustomFieldType cfType = cfSource.getCustomFieldType(); Object sprintsOfIssue = issue.getCustomFieldValue(cfSource) // Create an instance of HttpClient. HttpClient client = new HttpClient(); // Create a method instance. //We must go through the public URL (not localhost) otherwise, although it works, it generates Crowd WARNings in the logs GetMethod method = new GetMethod("https://<URL>/rest/greenhopper/1.0/sprints/14?os_username=<username>&os_password=<password>"); try { // Execute the method. int statusCode = client.executeMethod(method); if (statusCode != HttpStatus.SC_OK) { log.error("REST call failed: " + method.getStatusLine()); return; } //parse the result into a "struct" JsonSlurper slurp = new JsonSlurper(); Reader reader = new InputStreamReader(method.getResponseBodyAsStream()); def result = slurp.parse(reader); //look for the (only) active sprint for (Object sprint : result.sprints) if (!sprint.closed) { //we've found the current sprint, add it to the issue def sprintSet = [] as Set if (sprintsOfIssue != null) sprintSet = sprintsOfIssue.collect {it.id} sprintSet.add(sprint.id as String) def value = sprintSet.join(",") //now set the issue field value boolean wasIndexing = ImportUtils.isIndexIssues(); ImportUtils.setIndexIssues(true); try { //convert from string to Object CustomFieldParams fieldParams = new CustomFieldParamsImpl(cfSource, value); value = cfType.getValueFromCustomFieldParams(fieldParams); cfSource.updateValue(null, issue, new ModifiedValue(issue.getCustomFieldValue(cfSource), value), changeHolder) } finally { indexManager.reIndex(issue); ImportUtils.setIndexIssues(wasIndexing); } } } catch (HttpException e) { log.error("Fatal protocol violation: " + e.getMessage()); } catch (IOException e) { log.error("Fatal transport error: " + e.getMessage()); } catch (Exception e) { log.error("Error during post-function execution: " + e.getMessage()); } finally { // Release the connection. method.releaseConnection(); } } //wrapper call to manage history logging static void executeWithHistory(Map transientVars, Issue issue) { IssueChangeHolder holder = createChangeHolder(transientVars); try { execute(transientVars, issue, holder); } finally { releaseChangeHolder(holder, transientVars); } } /** * Create new holder with changes from transient vars */ private static IssueChangeHolder createChangeHolder(Map<String, Object> transientVars) { List<ChangeItemBean> changeItems = (List<ChangeItemBean>) transientVars.get(CHANGE_ITEMS); if (changeItems == null) { changeItems = new LinkedList<ChangeItemBean>(); } if (log.isDebugEnabled()) { log.debug("Create new holder with items - " + changeItems.toString()); } IssueChangeHolder holder = new DefaultIssueChangeHolder(); holder.setChangeItems(changeItems); return holder; } /** * Release change holder */ private static void releaseChangeHolder(IssueChangeHolder holder, Map<String, Object> transientVars) { List<ChangeItemBean> items = holder.getChangeItems(); if (log.isDebugEnabled()) { log.debug("Release holder with items - " + items.toString()); } transientVars.put(CHANGE_ITEMS, items); } private static final String CHANGE_ITEMS = "changeItems"; }; //call our static method AddCurrentSprintPostFunctionClass.executeWithHistory(transientVars,issue)
Thanks Renjith for the idea.
But how do I consume Jira's REST services from within a Jira plugin - more precisely a workflow function? More specifically, how do I find out about Jira's REST endpoint, and more importantly, how do I handle authentication since I'm already within Jira server-side code?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Just to clarify, the Rapid Board allows many Sprints to be active at once, but will not allow Plan mode to be used to start another when one is already active on Work mode.
That said, Renjith is totally right, this REST end point will list all of the Sprints that are active on the work mode of the specified Rapid Board.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Rapidboard allows only one active sprint at a time and the details can be fetched used REST. See a sample url below
http://localhost:8080/rest/greenhopper/1.0/sprints/4
The '4' in the url is the Rapid View Id.
This returns a list of sprints and the last object in the list will indicate the active sprint ("closed":false)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I'm talking about the RapidBoard Sprints. But what I'm trying to do is get the list of active Sprints in order to set the Sprint custom field automatically.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You mean rapid board of the regular Greenhopper? If rapidboard this is a custom field.
if it is a regular one it is the fixVersion field.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.