ScriptRunner for Confluence Cloud allows you to extend the functionality of Confluence Cloud, executing scripts to interact with Confluence as Built-in Scripts, Script Listeners, Fragments, etc. Scripts can take actions such as updating a set of pages when a space is created or watching a whole page tree. Administrators have the power of the Groovy programming language at their disposal to respond to events, manipulating Confluence using the REST API.
Differences to ScriptRunner for Confluence Server
ScriptRunner for Confluence Cloud follows the same principles as the original ScriptRunner but due to the differences in extension points between the Atlassian Connect framework used to write add-ons in the cloud and the Plugins V2 framework used in behind the firewall implementations, the execution model is significantly different. Scripts in ScriptRunner for Confluence Cloud do not execute within the same process as Confluence and so must interact with Confluence using the REST APIs rather than the JAVA APIs. Atlassian Connect is also inherently asynchronous which means that when a script executes the user may see the page load before the script has completed.
At this time it is not possible to implement Script Macros or CQL Script Functions.
Asynchronous execution is at the heart of the Atlassian Connect Framework that ScriptRunner must use to extend Confluence Cloud. Confluence fires webhooks when events occur and any user interface elements are loaded in iframes. It is not possible to veto, cancel or otherwise prevent Confluence performing an action using ScriptRunner. Scripts themselves are written synchronously, that is REST API calls are made in a blocking style (although async methods are provided too). This page has an excellent introduction to the Atlassian Connect framework for those that are interested.
When installing the app, a list of permissions, or scopes, are presented that ScriptRunner for Confluence Cloud requires to run successfully. A list of the scopes required for each REST API endpoint that Confluence Cloud provides can be found here. Below is a detailed explanation of why we need each of those scopes:
Act on a Confluence user’s behalf, even when the user is offline
Scripts can be configured to execute as either the app, or as the user who initiated that script. For example, if a user creates a page when a space is created then the created by will be initiated by that user, so it makes sense to execute the Script Listener as the user who triggered the event. This ensures that each user’s permissions are respected, and provides a much clearer history of who’s made changes to the issues in your system.
This scope allows for the creation, update and deletion of pages, spaces, etc, when running a script as the ScriptRunner Add-on user.
Administer Confluence projects
This allows you to write scripts that execute as the ScriptRunner Add-on user for creating, updating or removing spaces, pages, so that you don’t need to grant those permissions to the rest of your user base.
Delete Confluence data
This scope is required in order to delete pages, spaces, etc, while running a script as the ScriptRunner Add-on user.
Write data to Confluence
This scope is required in order to create pages, spaces, etc while running a script as the ScriptRunner Add-on user.
Read Confluence data
This scope is required in order to view pages, spaces, etc while running a script as the ScriptRunner Add-on user.
All user provided code is executed in an isolated container. ScriptRunner for Confluence Cloud is a service that is shared by many customers so isolation is very important that when executing scripts. Isolation is a goal that has been core to the design of ScriptRunner for Confluence from its inception. Each customer has a container that is theirs and theirs only. It is not possible for the code of one customer to have been run in the container of another. Containers are also designed to be single use, however it is possible that containers will be re-used for execution on occasion, but script writers should not rely on this. Disk storage in these containers is ephemeral, any changes written to the disk will be discarded.
All interaction with Confluence must be through the provided REST APIs. Atlassian provides comprehensive documentation including JSON Schema for responses and request bodies which describe the shape of the JSON. For effective usage of ScriptRunner it is important to have an understanding of how to interact with Confluence using the REST API.
Authentication and Authorisation
All REST requests made from ScriptRunner are performed as either the ScriptRunner Add-on user or as the user that initiated the action. The initiating user is the current user of the Script Console, the user that performed the action to cause an event to fire. Atlassian Connect Add-ons must also register for API scopes. These work in a similar way to the permissions that are granted to iOS or Android apps, when installing, the Confluence Administrator can see the scopes that any given add-on is requesting. ScriptRunner requests all scopes as it is not know beforehand what any given user will want to do. Even given this there are restrictions on the APIs that are available to ScriptRunner.
No private APIs are available, only those specifically white-listed by Atlassian
Scopes are documented for
Authentication from user scripts is handled by an authentication proxy builtin to ScriptRunner. Each script invocation is given a temporary authentication token to use to make requests into this proxy which will then perform the necessary request signing to make the authenticated request to your Confluence instance. In this way authenticated requests happen transparently. Tokens that are handed to scripts are only valid for two minutes. Responses that come through the proxy have URLs modified to go through the proxy so that URLs in the JSON response can be used directly without manipulation.
The HTTP library provided by ScriptRunner is Unirest. Unirest is a simple, lightweight library that makes interacting with REST APIs simple and straightforward. It was chosen due to the minimal dependencies (based on Apache HTTP Client 4.5), flexibility (JSON mapping support is built in and object mapping provided by Jackson) and the clarity of API.
Unirest.options are included in all scripts
import static which means no imports are needed in order to make HTTP requests. The base url to the ScriptRunner
authentication proxy is filled in along with the authentication headers so making REST calls is as simple as copying
and pasting from the Confluence REST documentation
For examples please visit the Script Console page
Static Type Checking
Static type checking is a feature intended to provide information to you about whether your script is correctly written.
The first thing to understand about Groovy is that it’s a dynamic language. Amongst many other facets of dynamic languages, this means that method and property names are looked up when your code is run, not when it’s compiled, unlike java.
Take a very simple but complete script:
That is, call the method
bar() on the object
foo. Unlike Java, this script will compile without errors, but when
you run it you will get a
foo hasn’t been defined. This behaviour is useful as there
are many circumstances that could make this code execute successfully, for instance an object called foo, or a closure
getFoo(), being passed to the script’s binding.
Although Groovy is a dynamic language, we can compile scripts in a manner that checks method and property references at compile time. The static type checking (STC) feature does just that, so that you can see any problems in your scripts when you are writing them, as opposed to when they execute.
It’s important to understand that when your scripts are executed, they are always compiled dynamically. When they are compiled for STC, the resulting generated bytecode is thrown away.
There are limitations to the type checker - it’s quite possible to write code that is displayed as having errors, but which is perfectly valid and will execute fine. Some examples of this are:
using certain Builders
using closures where the parameter types can’t be inferred
nested maps where type information is not available
|At the risk of repetition, the type checker is not always correct. It is a best-efforts attempt to let you know if there are problems with your code - but please let us know if you find something that should compile but doesn’t. Note also that your code could still have runtime errors that won’t be found until it executes.|
If we were writing a condition where we wanted to check the number of issues returned from a search is zero, we might inadvertently write:
boolean empty = isssues.total = 0
STC is telling us that we are trying to set the total, rather than retrieve it. We meant to use the
boolean empty = isssues.total == 0
Code areas will show a green dot, which tells us that the code is syntactically correct, and the methods and properties have been checked.
STC in the cloud will provide additional help for the script context, ensuring that properties access matches the JSON Schema provided by Atlassian