In this article I would like to discuss code duplication in ScriptRunner scripts.
When you begin to write scripts in ScriptRunner you usually put all the required code in a script and then attach the script to a post-function, validator etc. Everything goes smoothly until you realize that you made a mistake in your code or your requirements changed and now you have to find all the scripts with the related code. You can make a search on the groovy script folder and replace the old value with the new value but in my experience such a replacement will cause a bug somewhere in your code. Moreover if you did not test your code with the following feature of ScriptRunner:
https://scriptrunner.adaptavist.com/latest/jira/testing.html
or some other way, I guess you are in trouble.
That is the classic problem with code duplication. Let's try to solve the problem in ScriptRunner.
Part 1
I think the easiest way to do it is to create a helper class and then call it from a groovy script.
First we create our helper class in scripts/groovy/util directory with the name UtilHelper.groovy:
package groovy.util;
public class UtilHelper {
public static String getMessage() {
return "util helper message";
}
}
Then we will create a post function script in scripts/groovy with the name postfunction.groovy where we call getMessage() function from our helper class:
package groovy
import groovy.util.UtilHelper;
log.error(UtilHelper.getMessage())
Just for the sake of simplification we will not change any workflow to run the post function. We will execute the script from the Script Console:
We can see the message from UtilHelper.groovy file. It worked.
Now I would like to discuss some problems with this approach:
1. Our team tried this approach on older versions of ScriptRunner and we had the following problems:
1.1. We had a static compilation error saying that our imported helper classes could not be found. But it worked in runtime.
1.2 If we made changes to a helper class then the changes were not visible in the calling script unless we also made changes to the calling script. This error did not let us automate script deployment between Jira instances.
2. I currently tried to use this approach with ScriptRunner 5.2.2. I did not have the errors above but:
2.1. in Script Console if I call the helper class from the inline script editor then sometimes my helper class can not be found.
2.2. I first saved my class in the scripts/groovy folder and then moved it into the scripts/groovy/util folder. Even if the file does not exist anymore in the scripts/groovy folder I still can call it.
I am sure that any of the behaviours above could be explained, but problems are problems and that is why I would like to offer another solution which also has a couple of other advantages.
Part 2
Just imagine if we could have a common Jira plugin library which we could invoke from our Jira plugins or Scriptrunner scripts. In this case we could have all code in our common library and make changes only to the library.
Or let's say we want to implement feature toggle functionality (you can read more about feature toggle here). We could save all our toggles in a property file and then take all toggles from the file. But it would be better, if we could create a web item somewhere in the add-ons menu, and then call our webwork on the web item click. We could develop a plugin, which would manage the UI part of the managing our toggles functionality, and provide an interface to other plugins or Scriptrunner scripts to retrieve our toggles.
In my previous article I described how to make a core plugin, which could be called from another plugin. The name of the core plugin was jira-library. Let's just try to call jira-library plugin from a Scriptrunner script. If we can make it then we can write our feature toggle plugin as well.
We will call the getLibraryMessage() function from jira-library plugin.
You can download the plugin from here, make a package with atlas-mvn package command and install it to your Jira instance.
You can read about working with other plugin from ScriptRunner here.
I do not think I can explain how it works better than in ScriptRunner's documentation but the most important parts are WithPlugin annotation and PluginModule annotation. In the @WithPlugin we provide jira-library key. It will let us import classes from jira-library plugin. @PluginModule let us inject exported services from jira-library.
Keeping this in mind we open Script Console and write the following script:
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import ru.matveev.alexey.tutorial.library.api.LibraryService
@WithPlugin("ru.matveev.alexey.tutorial.library.jira-library")
@PluginModule
LibraryService libraryService
log.error(libraryService.getLibraryMessage()
And after we run it, we will see the message from getLibraryMessage() function of jira-library:
That is it. It worked.
Now about problems.
Our team has encountered only one problem so far. This approach does not work in ScriptRunner behaviours. We published this bug in Adaptivist Service Desk. Hopefully it will be fixed.
Alexey Matveev
software developer
MagicButtonLabs
Philippines
1,574 accepted answers
7 comments