Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in
It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Trigger a script runner script on sprint events

I want to transit all non-resolved issues in a Sprint to a specific workflow status, whenever a Sprint is closed. In order to do so, I need to latch onto the Sprint end event.

So, I have found out that the only place to latch onto this event, is at JIRA Webhooks. My idea was to add ScriptRunner REST Endpoints to reroute the Webhook back to ScriptRunner through the Webhook and go on from there.

Now as you can see, this is a cumbersome way to accomplish this. My questions are: 

  • Is there a better way to do this?
  • If yes, how can I accomplish this
  • If no, I would appreciate if anyone has a working example of something similar

 

5 answers

1 accepted

3 votes
Answer accepted

JIRA Software 7.1+ seems to have a com.atlassian.greenhopper.api.events.sprint.SprintClosedEvent. Unfortunately ScriptRunner only handles JIRA events for scripted event handlers (ones that extend JiraEvent or IssueEvent). The sprint closed event only extends AbstactSprintEvent so you can't write a scripted event handler at present.

Please track and vote on this issue SRJIRA-1910 which would let you do that.

Its true that you can programatically register an event handler using the event publisher but then you have to handle registering and unregistering on startup and you could get into a bit of a mess so we will avoid it for this solution.

You should setup a web hook for the sprint closed event and your url should look something like:

<jira-base-url>/rest/scriptrunner/latest/custom/sprintClosed/${sprint.id}

You'll also need to add the following script REST endpoint script: 

import com.atlassian.greenhopper.service.rapid.view.RapidViewService
import com.atlassian.greenhopper.service.sprint.SprintManager
import com.atlassian.greenhopper.web.rapid.chart.HistoricSprintDataFactory
import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.runner.customisers.PluginModuleCompilationCustomiser
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript

import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@WithPlugin("com.pyxis.greenhopper.jira")

@BaseScript CustomEndpointDelegate customEndpointDelegate

sprintClosed(httpMethod: "POST") { MultivaluedMap queryParams, String body -&gt;
    def extraPath = extraPath as String
    def sprintId = extraPath.replace("/", "")
    def userKey = queryParams.getFirst("user_key")

    def sprintManager = PluginModuleCompilationCustomiser.getGreenHopperBean(SprintManager)
    def historicSprintDataFactory = PluginModuleCompilationCustomiser.getGreenHopperBean(HistoricSprintDataFactory)
    def rapidViewService = PluginModuleCompilationCustomiser.getGreenHopperBean(RapidViewService)

    def issueService = ComponentAccessor.getIssueService()
    def userManager = ComponentAccessor.getUserManager()

    def user = userManager.getUserByKey(userKey)

    def closedSprint = sprintManager.getSprint(sprintId.toLong()).value
    def view = rapidViewService.getRapidView(user, closedSprint.rapidViewId).value
    def sprintContents = historicSprintDataFactory.getSprintOriginalContents(user, view, closedSprint)

    def sprintData = sprintContents.value

    if (sprintData) {
        def incompleteIssues = sprintData.contents.issuesNotCompletedInCurrentSprint*.issueId

        incompleteIssues.each { issueId -&gt;
            // This is the id of the transition you want to make
            def actionId = 21

            def transitionValidationResult = issueService.validateTransition(user, issueId, actionId, issueService.newIssueInputParameters())

            if (transitionValidationResult.isValid()) {
                issueService.transition(user, transitionValidationResult)
            } else {
                log.warn transitionValidationResult.errorCollection.errorMessages
            }
        }
    }

    return Response.noContent().build()
}

That will transition all the incomplete issues to a specified status. You will need to replace actionId in the script with the id you want to transition to which you can find in the workflow.

The user that has closed the sprint will need to have permissions to transition the issue as well.

When you close the sprint the page it shows the incomplete issues on doesn't seem to have the status updated to reflect the transition we did, there doesn't seem like a way I can fix this small bug. But if you click into the issue it will have the correct status.

Note that the body in the script contains data like:

{
	"timestamp": 1474627199320,
	"webhookEvent": "sprint_closed",
	"sprint": {
		"id": 8,
		"self": "http://localhost:8080/jira/rest/agile/1.0/sprint/8",
		"state": "closed",
		"name": "Sample Sprint 7",
		"startDate": "2016-09-23T11:39:43.555+01:00",
		"endDate": "2016-10-07T11:39:00.000+01:00",
		"completeDate": "2016-09-23T11:39:59.318+01:00",
		"originBoardId": 1
	}
}

In case you wanted to extract anymore data out but you should be able to get most of that from the closedSprint object in the script.

Hope this helps.

Adam

 

Adam, you're the best! I will test this immediately.

Yup, that did it. So, I am trying to understand your code, and I am already stumbling in your first line in the closure:

def extrapath = extrapath as String

This is also related to something else I do not understand, the REST Endpoint URL. So, in your documentation, <jira-base-url>/rest/scriptrunner/latest/custom/sprintClosed should be matched with the sprintClosed Method. But, we append /${sprint.id} to the Webhook-URL. Does this mean that is <jira-base-url>/rest/scriptrunner/latest/custom/sprintClosed/<whatever> always matched with the sprintClosed Endpoint, and /<whatever> is put into the extrapath variable? I have never written a groovy code ever, so perhaps I am missing some syntax magic.

Yes thats correct. The extraPath is not really documented in ScriptRunner but it will give you anything in the path after "sprintClosed".

Thanks again, really appreciate your help here smile

HI @adammarkham 

I have the same requirement as..while sprint closing/completing..the incomplete issues will be moved either future sprint or backlog...so whenever we moved the incomplete issues to backlog then the sprint field should be reset to null...i mean sprint value should be removed.

Normally when we add the incomplete issues to backlog the sprint field contains the sprint name to which the ticket has belonged.

Could you please help me on this...i tried to update the sprint field but getting below error.

 

2019-04-30 20:23:03,055 http-nio-8712-exec-2 ERROR admin 1222x1118x1 1lhqugt 0:0:0:0:0:0:0:1 /rest/greenhopper/1.0/sprint/106/complete [c.o.scriptrunner.runner.AbstractScriptListener] Script function failed on event: com.atlassian.greenhopper.api.events.sprint.SprintClosedEvent, file: <inline script>
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '[10201, 10601]' with class 'java.util.ArrayList' to class 'com.atlassian.jira.issue.MutableIssue' due to: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.atlassian.jira.issue.MutableIssue(java.lang.Long, java.lang.Long)
at Script9.run(Script9.groovy:62)

Hi, Somebody know how can I do in Cloud?

Serach in event the number Sprint or boardId

thank's

Also, there is an entry in the Atlasssian Issue DB regarding Sprint events:

https://jira.atlassian.com/browse/JSW-6395

 

Thanks for you time! We are using JIRA Software 7.1.1 (JIRA Agile does not exist anymore) and JIRA Core 7.1.0. 

It took me a while to investigate the best approach for this.

What version of JIRA Agile are you using? This may be possible using a script that registers an event listener rather than using a webhook but it depends if you have JIRA Agile 7.1+ or not as the SprintClosedEvent is only available in that version onwards.

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Published in Apps & Integrations

Codegeist '21 Customer Choice Awards - Vote Now!

Last quarter, 290 developers participated in Atlassian’s annual hackathon, Codegeist, for a chance to win thousands of dollars with a new Marketplace app. We announced winners selected by Atlassian...

911 views 4 16
Read article

Community Events

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

Find an event

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

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you