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

 

4 answers

1 accepted

This widget could not be displayed.

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

This widget could not be displayed.

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.

This widget could not be displayed.

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

This widget could not be displayed.

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

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

 

Suggest an answer

Log in or Sign up to answer
Community showcase
Published Aug 22, 2018 in Marketplace Apps

How a Marketplace app tech team is achieving gender diversity

Hello! My name is Genevieve Blanch, and I'm the Marketing Manager at RefinedWiki, creators of apps to give teams the tools to customize Atlassian platforms. Currently, 44% of the tech team at Re...

510 views 3 18
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