Let me share my excitement with you.
I'm Serhiy, Exalate development team member.
And this week we found that Exalate's scripting powers are absolutely enormous.
But let me start from the beginning...
The Problem and the Story
By design, our product allows administrators to configure the rules for Issue Tracker interaction using Groovy scripts.
Right from the start we wanted the user to rock. The Admin should be able to define messages of any content, regardless of how complex creating them might be. One doesn't configure the "synchronization" of issues. Instead, one rules "What should our JIRA say to another JIRA when an event happens to an issue".
So there we were. Our intent was to allow to write conditionals, checks, filters - all that stuff that claims "send this, only if that happens":
Such that the other JIRA Administrator could use the replica.customKeys."More than 10 votes for this issue" and replica.customKeys."More than a year":
Then we can help the user to write such decisions by providing helper methods:
Here's the problem: as long as we want to provide more and more ways to get information about JIRA, we need to write more and more methods which would copy existing JIRA API.
Which is neat. However, it was limited to only all the methods, that we provide in our helpers.
Enter the Power
In order for the script to be able to do things like new Date() the classpath for the script should contain the class java.util.Date . Obviously, this is something that any groovy application would have.
We included our classes in the script. Did that on purpose, so that the compiler would help identifying simple problems, like method name mistyping. This allows:
Ok, so we have all the classes from our plugin available in the script, so what?
You can do anything #1
(provided that JIRA API allows it)
JIRA API has this class com.atlassian.jira.component.ComponentAccessor that allows to get any service, registered in the JIRA application. I'm not afraid to emphasis: all classes from all the plugins and the JIRA itself! .
This would just get issue FOO-1 and delete it. If we wouldn't have the ComponentAccessor, the Exalate team would have to write a helper method to allow administrators to do this, along with all the possible flavours of it:
deleteIssue(User user, Issue issue, EventDispatchOption eventDispatchOption, boolean sendMail)
delete(User user, DeleteValidationResult issueValidationResult)
delete(User user, DeleteValidationResult issueValidationResult, EventDispatchOption eventDispatchOption, boolean sendMail)
You can do anything #2
(provided you can find it on the Internet)
OK, but what if an admin on JIRA A needs to send some info, to JIRA B, which is not available in the JIRA itself?
Provided that Exalate processors' classpath contains all the classes of Exalate, the scripts would do anything that Exalate can. Go go REST client power!
You can do anything #3
(that JIRA DB has)
OK, this means anything reachable with JIRA API and REST APIs all over the world. But how about the information, available in the JIRA, but not accessible via an API?
We've just queried the JIRA database with plain jdbc.
You can do anything #4
(that all JIRA add-ons offer)
Use another plugin's classes? Easy:
You can do anything with flexible JIRA synchronization.
But remember, that with great power comes great responsibility.
When you introduce scripting support, make sure, you know:
What's available in script's classpath
Who can access the scripting
What do you actually want the users to be able to do
Allow your administrators to kick ass!