Skip to end of metadata
Go to start of metadata

Creating a REST endpoint for your Confluence app is easy, fast and very useful. A REST endpoint with useful data as JSON is worth a lot to the user interface in Confluence as it is possible to use AJAX calls to get data in an asynchronous way and thereby making the interface fast and nice for Confluence users.

When developing apps for Confluence I often use REST endpoints to get and store data, especially if your app needs to store configuration settings.

In this example I will create a Confluence Space analyzer component that extracts information about Space view permission in Confluence for a given user and show the data via REST endpoints as JSON. All endpoints can only be used by the Confluence Administrator.

The first thing to do is to create a backend component that will fetch data from Confluence. Define the Java interface first.

Backend service interface (Java)

Backend service interface
package com.langhornweb.atlassian.confluence.analyzers;

import com.atlassian.confluence.spaces.Space;
import com.atlassian.user.User;

import java.util.List;

/**
 * Analyze a Confluence {@link com.atlassian.confluence.spaces.Space} in order to retrieve information
 * before indexing content
 */
public interface ConfluenceSpaceAnalyzer {

    /**
     * List {@link com.atlassian.confluence.spaces.SpaceType#GLOBAL} {@link Space}
     * @return the list
     * @param user the given {@link User}
     */
    List<Space> getGlobalSpaces(User user);

    /**
     * List {@link com.atlassian.confluence.spaces.SpaceType#PERSONAL} {@link Space}
     * @return the list
     * @param user the given {@link User}
     */
    List<Space> getPersonalSpaces(User user);

    /**
     * List {@link com.atlassian.confluence.spaces.SpaceStatus#CURRENT}
     * @return the list
     * @@param user the given {@link User}
     */
    List<Space> getCurrentSpaces(User user);

    /**
     * List {@link com.atlassian.confluence.spaces.SpaceStatus#ARCHIVED}
     * @return the list
     * @param user the given {@link User}
     */
    List<Space> getArchivedSpaces(User user);
}


Backend service implementation (Java)

Backend service implmentation
package com.langhornweb.atlassian.confluence.analyzers;

import com.atlassian.confluence.spaces.*;
import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.user.User;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@ExportAsService({ConfluenceSpaceAnalyzer.class})
@Named
public class ConfluenceSpaceAnalyzerImpl implements ConfluenceSpaceAnalyzer {

    @ComponentImport
    private SpaceManager spaceManager;

    @Inject
    public ConfluenceSpaceAnalyzerImpl(SpaceManager spaceManager) {
        this.spaceManager = spaceManager;
    }

    public List<Space> getGlobalSpaces(User user) {
        return spaceManager.getAllSpaces(SpacesQuery.newQuery().forUser(user).withSpaceType(SpaceType.GLOBAL).build());
    }

    public List<Space> getPersonalSpaces(User user) {
        return spaceManager.getAllSpaces(SpacesQuery.newQuery().forUser(user).withSpaceType(SpaceType.PERSONAL).build());
    }

    public List<Space> getCurrentSpaces(User user) {
        return spaceManager.getAllSpaces(SpacesQuery.newQuery().forUser(user).withSpaceStatus(SpaceStatus.CURRENT).build());
    }

    public List<Space> getArchivedSpaces(User user) {
        return spaceManager.getAllSpaces(SpacesQuery.newQuery().forUser(user).withSpaceStatus(SpaceStatus.ARCHIVED).build());
    }
}


Confluence REST Resource implmentation with one GET endpoint

REST Resource
package com.langhornweb.atlassian.confluence.resource;

import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.user.UserAccessor;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ConfluenceImport;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.atlassian.sal.api.transaction.TransactionTemplate;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.user.User;
import com.google.common.collect.ImmutableMap;
import com.langhornweb.atlassian.confluence.analyzers.ConfluenceSpaceAnalyzer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;

@Path("/spaceinfo")
@Scanned
public class BackendSpaceResource extends AbstractResource {

    private static final Logger LOGGER = LoggerFactory.getLogger(BackendSpaceResource.class);

    private ConfluenceSpaceAnalyzer confluenceSpaceAnalyzer;
    @ConfluenceImport
    private UserAccessor userAccessor;

    @Inject
    public BackendSpaceResource(UserManager userManager, PluginSettingsFactory pluginSettingsFactory, TransactionTemplate transactionTemplate, ConfluenceSpaceAnalyzer confluenceSpaceAnalyzer, UserAccessor userAccessor) {
        super(userManager, pluginSettingsFactory, transactionTemplate);
        this.confluenceSpaceAnalyzer = confluenceSpaceAnalyzer;
        this.userAccessor = userAccessor;
    }

    @Path("/global/{username}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getGlobalSpacesForUser(@Context HttpServletRequest request, @PathParam("username") final String username) {

        if (checkSystemAdminAuthentication(request)) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }

        if (StringUtils.isBlank(username)) {
            return Response.status(Response.Status.BAD_REQUEST).entity("{\"error\":\"username is not defined\"}").build();
        }

        final User user = userAccessor.getUserByName(username);
        if (user == null) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Username {} not found in Confluence...", username);
            }
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        final List<Space> spaces = confluenceSpaceAnalyzer.getGlobalSpaces(user);
        final Map<String, Integer> response = ImmutableMap.<String, Integer>builder().
            put("spacesize", spaces.size()).build();
        return Response.ok().entity(response).build();
    }
}


Register the REST resource into the atlassian-plugin.xml file

In order to register the REST endpoints you must enable rest element in the atlassian-plugin.xml file located under resources in your plugin project.

<rest key="rest" path="/rest-plugin" version="1.0">
    <description>Provides REST resources for the admin UI.</description>
</rest>

This is a very quick and small introduction to component development in Confluence.