Advanced tips: creating a linked Confluence space


Posted by
Gareth CANTRELL

August 23, 2016

Atlassian has two flavours of JIRA and Confluence, cloud and server, and whilst these are mostly comparable, the Cloud version sometimes gets extra functionality which for whatever reason is not made available in the server version. One of these features is the ability to create a linked Confluence space during project creation in JIRA, and in this post I’ll show you how we can emulate this on the server version.

Where is my space?

So you need to create a new JIRA project and create a linked space in Confluence without leaving JIRA? Well, as long as you have JIRA cloud and Confluence cloud, this is pretty simple – just leave the “Confluence space” option on the Create Project dialogue checked, as shown below:

JIRA Cloud allows you to create a corresponding Confluence space

Unfortunately if you have the server version, this functionality is not available. This means you’re stuck with the manual method: Create your JIRA project, then switch to Confluence and create a corresponding space. Wouldn’t it be great if we could replicate the JIRA cloud functionality on our own server?

Let’s get technical!

With a little help from our favourite scripting add-on, ScriptRunner for JIRA, we can add this functionality into the server version.

Most operations within JIRA cause an event to be fired and one of those is the ProjectCreatedEvent, which we’ll be making use of in a scripted listener.

If you haven’t already, install the ScriptRunner add-on into your JIRA instance and then navigate to the Add-ons->Script Listeners screen and click on Custom listener.

Add a note about what this listener does, select All projects from the Project Key selection and then select the ProjectCreatedEvent from the Events list.

Screen Shot 2016-07-22 at 17.39.47

Copy the following Groovy script into the Inline script field.

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.confluence.ConfluenceApplicationType
import com.atlassian.jira.applinks.JiraAppLinksHostApplication
import com.atlassian.jira.event.ProjectCreatedEvent
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder

/**
 * Retrieve the primary confluence application link
 * @return confluence application link
 */
def ApplicationLink getPrimaryConfluenceLink() {
    def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
    return applicationLinkService.getPrimaryApplicationLink(ConfluenceApplicationType.class)
}

def confluenceLink = getPrimaryConfluenceLink()
// we must have a working application link set up to proceed
assert confluenceLink

def event = event as ProjectCreatedEvent
def params = [
    key: event.project.key,
    name: event.project.name,
    description: [
        plain: [
            value: "Space created automatically for ${event.project.key} project on " + new Date().format("dd/MMM/yyyy HH:mm"),
            representation: "plain"
        ]
    ]
]

// make the REST API call to Confluence to create the space
def authenticatedRequestFactory = confluenceLink.createAuthenticatedRequestFactory()
authenticatedRequestFactory
    .createRequest(Request.MethodType.POST, "rest/api/space")
    .addHeader("Content-Type", "application/json")
    .setRequestBody(new JsonBuilder(params).toString())
    .execute(new ResponseHandler<Response>() {
        @Override
        void handle(Response response) throws ResponseException {
            if(response.statusCode != HttpURLConnection.HTTP_OK) {
                throw new Exception(response.getResponseBodyAsString())
            }
        }
	})

// Beyond this point we use non-public REST API calls
// Create reciprocal link to Confluence space
def linkRequest = [
    applicationId: ComponentLocator.getComponent(JiraAppLinksHostApplication.class).id.get(),
    key: event.project.key,
    name: event.project.name,
    typeId: "jira.project"
]

// Make the REST API call to Confluence to link the project and space
authenticatedRequestFactory
	.createRequest(Request.MethodType.PUT, "rest/applinks/1.0/entitylink/com.atlassian.applinks.api.application.confluence.ConfluenceSpaceEntityType/${event.project.key}?reciprocate=true")
	.addHeader("Content-Type", "application/json")
	.setRequestBody(new JsonBuilder(linkRequest).toString())
	.execute(new ResponseHandler<Response>() {
        @Override
        void handle(Response response) throws ResponseException {
            if(response.statusCode != HttpURLConnection.HTTP_CREATED) {
                throw new Exception(response.getResponseBodyAsString())
            }
        }        
    })

Next, create your project as per normal. Upon successful creation, you’ll be taken to your new project and notified that both a JIRA project and Confluence space have been created.

Screen Shot 2016-07-22 at 11.20.30

Screen Shot 2016-07-26 at 13.04.57

Finally, click on the link in the notification and verify that your new space is created in Confluence with the same name as your JIRA project!

Screen Shot 2016-07-22 at 17.48.26

Don’t forget that to make this all possible, your JIRA and Confluence need to be connected using Application Links.

Are there any similar tips or hacks you’ve implemented? Tell us in the comments below!