Scriptrunner : groovy, trying to use SwimlaneService and Swimlane

Olivier Billaud July 22, 2022

Hello dear friends, 

 

I'm stuck on groovy development about boards (rapid views), and more precisely on swimlanes.

I'm trying to add programmatically a new swimlane in my board.

For this, I found these classes:

https://docs.atlassian.com/jira-software/6.7.7/com/atlassian/greenhopper/service/rapid/view/SwimlaneServiceImpl.html

- https://docs.atlassian.com/jira-software/6.7.6/com/atlassian/greenhopper/model/rapid/Swimlane.SwimlaneBuilder.html

https://docs.atlassian.com/jira-software/6.7.7/com/atlassian/greenhopper/service/rapid/view/RapidViewServiceImpl.html

 

Here is my current code (it doesn't work at all):

import com.atlassian.greenhopper.model.rapid.RapidView
import com.atlassian.greenhopper.model.rapid.Swimlane
import com.atlassian.greenhopper.model.rapid.Swimlane.SwimlaneBuilder
import com.atlassian.greenhopper.service.rapid.view.RapidViewService
import com.atlassian.greenhopper.service.rapid.view.SwimlaneService
import com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.component.ComponentAccessor
import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean


@JiraAgileBean

RapidViewService rapidViewService

def authenticationContext = ComponentAccessor.jiraAuthenticationContext
def user = authenticationContext.loggedInUser

def view = rapidViewService.getRapidView(user, 11014).get() // ID of board - you can see that in the URL

def swimlaneService = new SwimlaneServiceImpl()
log.debug swimlaneService;
log.debug view

allSwims = swimlaneService.loadSwimlanes(view)

for (swim in allSwims){
    log.debug swim.toString()
}

def newSwimBuilder = new SwimlaneBuilder()
newSwimBuilder.name("New swimlane")
newSwimBuilder.query("'parent link' = KEY-1")
newSwimBuilder.position(2 as Integer)
def newSwim = newSwimBuilder.build()

log.debug newSwim.getId()
log.debug newSwim.getName()
log.debug newSwim.getQuery()
log.debug newSwim.getPosition()

swimlaneService.add(user, view, newSwim)

 

Honestly, ... 

1. I found how to instantiate "rapidViewService" and it works, but I don't even understand how and why, first time I see this "@JiraAgileBean"... But nevermind. It works. But if you have time to explain to me what is this...

2. With SwimlaneService, I have the following error, after trying to access all swimlanes (loadSwimlanes(RapidView)) and I don't know why:

2022-07-22 16:11:20,224 ERROR [common.UserScriptEndpoint]: Script console script failed: java.lang.NullPointerException at com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl.loadSwimlanes(SwimlaneServiceImpl.java:84)

3. I found the "SwimlaneBuilder" and i can populate some fields (name, query...) but I don't know how to save it (and then, how to have a valid id for this new swimlane).

 

I'm pretty far from my goal. 

Please, help me if you can!

Thank you so much <3 love on you.

Oliv'

 

1 answer

1 accepted

0 votes
Answer accepted
Tom Lister
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
July 25, 2022

Hi

Some things to check.

It's hard to tell without the line numbers shown. I'm guessing this line is the problem

allSwims = swimlaneService.loadSwimlanes(view)

Is one of view or swimlaneService set to null?

Try this syntax to create the service

def swimlaneService = ComponentAccessor.getComponent(SwimlaneService.class)
Put a def at the start of
allSwims = swimlaneService.loadSwimlanes(view)

 The swimlaneService.add should return a SwimLane object with an id property

The line

def view = rapidViewService.getRapidView(user, 11014).get()

looks suspicious. There is no get() method and it should be superfluous to getRapidView.

The @JiraAgileBean is an instruction to Scriptrunner to load the 'greenhopper' plugin so you can work with its components

Olivier Billaud July 25, 2022

Hi @Tom Lister and thank you for answering!

 

I answer in the disorder:

def view = rapidViewService.getRapidView(user, 11014).get()


The documentation of rapidViewService:

https://docs.atlassian.com/jira-software/6.7.7/com/atlassian/greenhopper/service/rapid/view/RapidViewServiceImpl.html

says that this function returns a "ServiceOutcome" object (https://docs.atlassian.com/software/jira/docs/api/7.2.3/com/atlassian/jira/bc/ServiceOutcome.html )

The ServiceOutcome class seems to have a "get" method to get the first result of the ServiceOutcome list.

When i try this code:

def view = rapidViewService.getRapidView(user, 31).get() 
log.debug view


The log is ok:

2022-07-25 14:30:16,068 DEBUG [runner.ScriptBindingsManager]: com.atlassian.greenhopper.model.rapid.RapidView@6366e9a8[id=31,name=Jira Support (Perso),savedFilterId=14025,owner=admin,sprintSupportEnabled=false,showDaysInColumn=true,swimlaneStrategy=CUSTOM,cardColorStrategy=NONE,showEpicAsPanel=YES,oldDoneIssuesCutoff=NONE,emptyFilterBoard=false]


When trying to create the service with your syntax, the console bugs (there is no "log" and "timing", "result" is in red and says "null" even if I return true)... I don't know what happens but it does not seem happy with this syntax.
With the one I used, 

def swimlaneService = new SwimlaneServiceImpl()
log.debug swimlaneService;

The log is:

2022-07-25 14:42:39,104 DEBUG [runner.ScriptBindingsManager]: com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl@79969103

 


To summary, view is not null, swimlaneService is not null, but the method crashes.


2022-07-25 14:46:59,269 DEBUG [runner.ScriptBindingsManager]: com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl@363ebe5 2022-07-25 14:46:59,270 DEBUG [runner.ScriptBindingsManager]: com.atlassian.greenhopper.model.rapid.RapidView@6366e9a8[id=31,name=Jira Support (Perso),savedFilterId=14025,owner=admin,sprintSupportEnabled=false,showDaysInColumn=true,swimlaneStrategy=CUSTOM,cardColorStrategy=NONE,showEpicAsPanel=YES,oldDoneIssuesCutoff=NONE,emptyFilterBoard=false] 2022-07-25 14:46:59,275 ERROR [common.UserScriptEndpoint]: ************************************************************************************* 2022-07-25 14:46:59,275 ERROR [common.UserScriptEndpoint]: Script console script failed: java.lang.NullPointerException at com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl.loadSwimlanes(SwimlaneServiceImpl.java:84) at com.atlassian.greenhopper.service.rapid.view.SwimlaneService$loadSwimlanes.call(Unknown Source) at Script179.run(Script179.groovy:24)

 


I give you the simplified piece of code if you want to try on your own.


import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.greenhopper.model.rapid.RapidView
import com.atlassian.greenhopper.model.rapid.Swimlane
import com.atlassian.greenhopper.service.rapid.view.RapidViewService
import com.atlassian.greenhopper.service.rapid.view.SwimlaneService
import com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl
import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean

@JiraAgileBean
RapidViewService rapidViewService

def authenticationContext = ComponentAccessor.jiraAuthenticationContext
def user = authenticationContext.loggedInUser

def view = rapidViewService.getRapidView(user, 31).get() // ID of board - you can see that in the URL

def swimlaneService = new SwimlaneServiceImpl()
//def swimlaneService = ComponentAccessor.getComponent(SwimlaneService.class)

log.debug swimlaneService;
log.debug view

def allSwims = swimlaneService.loadSwimlanes(view)


Do you know if i can access the GreenHopper source code to identify the error (com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl.loadSwimlanes(SwimlaneServiceImpl.java:84)) ? It would be very useful to know what is the line 84 of this file...
I hope I don't bother you, thank you for your time!
Olivier

Tom Lister
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
July 25, 2022

Hi @Olivier Billaud 

I got this to work as below. SwimLaneService is also a JiraAgileBean

 

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.greenhopper.model.rapid.RapidView
import com.atlassian.greenhopper.model.rapid.Swimlane
import com.atlassian.greenhopper.service.rapid.view.RapidViewService
import com.atlassian.greenhopper.service.rapid.view.SwimlaneService
import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean
import com.atlassian.greenhopper.web.rapid.view.RapidViewHelper
import com.atlassian.greenhopper.model.rapid.Swimlane.SwimlaneBuilder
import org.apache.log4j.Logger

def logit = Logger.getLogger("com.cheil.eu.logging")


@JiraAgileBean
RapidViewService rapidViewService

@JiraAgileBean
RapidViewHelper rapidViewHelper

@JiraAgileBean
SwimlaneService sls

def authenticationContext = ComponentAccessor.jiraAuthenticationContext
def user = authenticationContext.loggedInUser

def view = rapidViewService.getRapidView(user, 51).get() // ID of board - you can see that in the URL
logit.info "view " + view
logit.info "sls2 " + sls

def allSwims = sls.loadSwimlanes(view)

logit.info "lanes " + allSwims.size()
for (swim in allSwims){
logit.info swim.toString()
}

def newSwimBuilder = new SwimlaneBuilder()
newSwimBuilder.name("New swimlane")
newSwimBuilder.query("'parent link' = KEY-1")

//newSwimBuilder.position(2 as Integer)

def newSwim = newSwimBuilder.build()
//log.debug newSwim.getId()
log.debug newSwim.getName()
log.debug newSwim.getQuery()
log.debug newSwim.getPosition()

sls.add(user, view, newSwim)

//def allSwims = swimlaneService.loadSwimlanes(view)



Like Olivier Billaud likes this
Olivier Billaud July 25, 2022

Test...

Olivier Billaud July 25, 2022

Re @Tom Lister  !

Thanks again to give your time to this issue.
I tried this before, use AgileBean to load the service...
But i have compilation errors. You don't have them on your side?

The script could not be compiled: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: General error during semantic analysis: java.lang.NoClassDefFoundError: com.atlassian.greenhopper.manager.GreenHopperCache java.lang.RuntimeException: java.lang.NoClassDefFoundError: com.atlassian.greenhopper.manager.GreenHopperCache at org.codehaus.groovy.control.CompilationUnit.convertUncaughtExceptionToCompilationError(CompilationUnit.java:1118) at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1098) at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:640) at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:618) at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:595) at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:401) at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:89) at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:341) at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:338) at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:147) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:336) at groovy.util.GroovyScriptEngine$ScriptClassLoader.doParseClass(GroovyScriptEngine.java:242) at groovy.util.GroovyScriptEngine$ScriptClassLoader.parseClass(GroovyScriptEngine.java:229) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:320) at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:262) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.getScriptClass(GroovyScriptEngineImpl.java:331) at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.compile(GroovyScriptEngineImpl.java:181) at javax.script.Compilable$compile.call(Unknown Source) at com.onresolve.scriptrunner.model.validation.ScriptConfigValidatorImpl.isValid(ScriptConfigValidatorImpl.groovy:59) at com.onresolve.scriptrunner.model.validation.ScriptConfigValidatorImpl.isValid(ScriptConfigValidatorImpl.groovy) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:171) at org.hibernate.validator.internal.engine.constraintvalidation.SimpleConstraintTree.validateConstraints(SimpleConstraintTree.java:68) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:73) at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:127) at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:120) at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:552) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:515) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:485) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:447) at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:397) at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:173) at com.onresolve.scriptrunner.runner.rest.common.UserScriptEndpoint.validate(UserScriptEndpoint.groovy:249) at com.onresolve.scriptrunner.runner.rest.common.UserScriptEndpoint.execFromJson(UserScriptEndpoint.groovy:137) at sun.reflect.GeneratedMethodAccessor5385.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.atlassian.plugins.rest.common.interceptor.impl.DispatchProviderHelper$ResponseOutInvoker.lambda$_dispatch$0(DispatchProviderHelper.java:181) at com.atlassian.plugins.rest.common.interceptor.impl.DispatchProviderHelper.lambda$invokeMethodWithInterceptors$0(DispatchProviderHelper.java:81) at com.atlassian.plugins.rest.common.interceptor.impl.DefaultMethodInvocation.invoke(DefaultMethodInvocation.java:53) at com.atlassian.plugins.rest.common.expand.interceptor.ExpandInterceptor.intercept(ExpandInterceptor.java:42) at com.atlassian.plugins.rest.common.interceptor.impl.DefaultMethodInvocation.invoke(DefaultMethodInvocation.java:53)

Maybe, in my version of Jira (8.5.19), the API is different and I need to check the libraries imported... :( 

I try to find the origin of these errors but if you have any idea, thank you !

Olivier

Tom Lister
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
July 25, 2022

Hi

I'm not sure about your error. My server is 8.22.4 and Scriptrunner is 6.55.1

This is the 'raw' code. I prettified the last post but I can't see what would be different.

I'm running this in the script console.

 

import com.atlassian.jira.component.ComponentAccessor

import com.atlassian.greenhopper.model.rapid.RapidView

import com.atlassian.greenhopper.model.rapid.Swimlane

import com.atlassian.greenhopper.service.rapid.view.RapidViewService

import com.atlassian.greenhopper.service.rapid.view.SwimlaneService

//import com.atlassian.greenhopper.service.rapid.view.SwimlaneServiceImpl

import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean

//import com.atlassian.greenhopper.web.rapid.view.RapidViewHelper

import com.onresolve.scriptrunner.runner.customisers.WithPlugin

import com.atlassian.greenhopper.model.rapid.Swimlane.SwimlaneBuilder

import org.apache.log4j.Logger

def logit = Logger.getLogger("com.cheil.eu.logging")

@JiraAgileBean

RapidViewService rapidViewService

@JiraAgileBean

SwimlaneService sls

def authenticationContext = ComponentAccessor.jiraAuthenticationContext

def user = authenticationContext.loggedInUser

def view = rapidViewService.getRapidView(user, 51).get() // ID of board - you can see that in the URL

logit.info "view " + view

logit.info "sls2 " + sls

def allSwims = sls.loadSwimlanes(view)

logit.info "lanes " + allSwims.size()

for (swim in allSwims){

logit.info swim.toString()

}

def newSwimBuilder = new SwimlaneBuilder()

newSwimBuilder.name("New swimlane")

newSwimBuilder.query("'parent link' = KEY-1")

newSwimBuilder.position(2 as Integer)

def newSwim = newSwimBuilder.build()

logit.info newSwim.getName()

logit.info newSwim.getQuery()

logit.info newSwim.getPosition()

sls.add(user, view, newSwim)

allSwims = sls.loadSwimlanes(view)

logit.info "lanes " + allSwims.size()

for (swim in allSwims){

logit.info swim.toString()

}
Like Olivier Billaud likes this
Olivier Billaud July 25, 2022

@Tom Lister !

I found it.

I had to add:

@WithPlugin("com.pyxis.greenhopper.jira")
And I have no error anymore!
I just succedeed to create a new swimlane.
Oh my gosh, this is kind of esoteric code for me... I don't understand all these libraries loaders. Anyway, thank you so much, you helped me feeling less alone.
Thanks a lot!
Like Rinaldi Michael likes this
Rinaldi Michael December 5, 2022
@WithPlugin("com.pyxis.greenhopper.jira")

This worked like a charm. Trying to work with a board's quick filters. At first I couldn't figure out why this wasn't working but I later found out we need to use which I probably overlooked somewhere -

import com.onresolve.scriptrunner.runner.customisers.WithPlugin
Got something like this to work.
//Author: Rinaldi Michael

//Last Modified: 6:28 pm

//Reference: https://community.atlassian.com/t5/Jira-Software-questions/Scriptrunner-groovy-trying-to-use-SwimlaneService-and-Swimlane/qaq-p/2086660#U2208439

 

 

import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean

import com.atlassian.jira.user.ApplicationUser

import com.onresolve.scriptrunner.parameters.annotation.*

import com.atlassian.greenhopper.service.rapid.view.RapidViewService

import com.atlassian.greenhopper.service.rapid.view.QuickFilterService

import com.onresolve.scriptrunner.runner.customisers.WithPlugin

 

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

 

@UserPicker(label = "User", description = "Select a user")

ApplicationUser applicationUserInput

 

@JiraAgileBean

RapidViewService rapidViewService

 

@JiraAgileBean

QuickFilterService quickFilterService

 

def rapidViews = rapidViewService.getRapidView(applicationUserInput,66).get()

 

def getAllQuickFiltersIn66 = quickFilterService.loadQuickFilters(rapidViews)

 

//return getAllQuickFiltersIn66.query

 

 

def rapidViewsAll = rapidViewService.getRapidViews(applicationUserInput).get()

 

def getAllQuickFiltersInJira = quickFilterService.loadQuickFilters(rapidViewsAll[3])

 

return getAllQuickFiltersInJira.query
Thanks a lot!
Like Tom Lister likes this
Rinaldi Michael December 5, 2022

Now I'm stuck XD. I'm not able to do any operations on the Quick Filter. I'm getting nullPointExceptions. Is there a way to fix this? 

import java.lang.String.*

import java.io.*

import com.atlassian.jira.util.ErrorCollection

import com.onresolve.scriptrunner.runner.customisers.JiraAgileBean

import com.atlassian.jira.user.ApplicationUser

import com.onresolve.scriptrunner.parameters.annotation.*

import com.atlassian.greenhopper.*

import com.atlassian.greenhopper.service.rapid.view.RapidViewService

import com.atlassian.greenhopper.model.rapid.QuickFilter

import com.atlassian.greenhopper.service.rapid.view.*

import com.atlassian.greenhopper.service.rapid.view.QuickFilterService.*

import com.atlassian.greenhopper.service.rapid.view.QuickFilterServiceImpl.*

import com.atlassian.greenhopper.model.rapid.QuickFilter.QuickFilterBuilder

import com.onresolve.scriptrunner.runner.customisers.WithPlugin

import com.atlassian.jira.component.ComponentAccessor

import com.atlassian.greenhopper.service.rapid.view.QuickFilterServiceImpl.update.*

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

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

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






def authenticationContext = ComponentAccessor.jiraAuthenticationContext

def userManager = ComponentAccessor.userManager

@UserPicker(description = '', label = 'Main user', multiple = false)

ApplicationUser applicationUserInput






@JiraAgileBean

RapidViewService rapidViewService



@JiraAgileBean

QuickFilterService quickFilterService




//@JiraAgileBean

QuickFilterServiceImpl quickFilterServiceImpl = new QuickFilterServiceImpl()

def rapidViews = rapidViewService.getRapidView(applicationUserInput,1234).get()

def rapidViewsIn66 = quickFilterService.loadQuickFilters(rapidViews)

def quickFiltersIn66 = rapidViewsIn66.query

//return quickFiltersIn6

//return quickFiltersIn66[6]

//return rapidViewsIn66[5]

//@UserPicker(description = '', label = 'Test User replace', multiple = false)

//def testUser

def userName = "Rinaldi Michael Email"

def emailAddress = "rinaldi@email.com"

//replace username with email address

def newQuickFilter = quickFiltersIn66[5].replaceAll(userName,emailAddress).toString()

def newQuickFilterBuilder = new QuickFilterBuilder()

newQuickFilterBuilder.id(rapidViewsIn66[5].getId())

newQuickFilterBuilder.name(rapidViewsIn66[5].getName())

newQuickFilterBuilder.position(rapidViewsIn66[5].getPosition())

newQuickFilterBuilder.query(newQuickFilter)

def newQuickFilterBuild = newQuickFilterBuilder.build()

//return newQuickFilterBuild

//quickFilterServiceImpl.add(applicationUserInput,rapidViews,newQuickFilterBuild)



def update = quickFilterServiceImpl.update(applicationUserInput,rapidViews,newQuickFilterBuild,null)

return quickFiltersIn66[6]

 

The error I get is

java.lang.NullPointerException at com.atlassian.greenhopper.service.rapid.view.QuickFilterServiceImpl.update(QuickFilterServiceImpl.java:145) at com.atlassian.greenhopper.service.rapid.view.QuickFilterService$update$0.call(Unknown Source) at Script707.run(Script707.groovy:70)

Stefan Stadler June 17, 2024

This happened to me now as well.

@Rinaldi Michael Have you been able to solve this in the meantime?

Rinaldi James Michael June 17, 2024

Suggest an answer

Log in or Sign up to answer