Confluence Macro to Show User Installed Jira Apps

Overview

When working in Jira it is good to know which apps are installed the capabilities they provide. This script provides a Confluence macro to show a list of apps a user has installed on their Jira instance, and the gadgets and functionality they provide.

Example

I am a project manager preparing a report. I would like to see a full list of all apps installed and their gadgets so I can make use of all available capabilities to customise and enhance my flexible dashboards.

Good to Know

  • This script requires Application Links. If an error like this You don’t have an authorized access token for the remote resource is rendered when macro is executed, this means that an OAuth impersonation needs to be added to the link.

Requirements

Confluence Confluence (6.6 - 6.15)

import com.atlassian.applinks.api.ApplicationLinkResponseHandler
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.jira.JiraApplicationType
import com.atlassian.confluence.xhtml.api.XhtmlContent
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 groovy.json.JsonSlurper
import groovy.xml.MarkupBuilder
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import org.json.JSONObject

// Link to primary linked Jira instance
def appLinkService = ComponentLocator.getComponent(ApplicationLinkService)
def appLink = appLinkService.getPrimaryApplicationLink(JiraApplicationType)
def applicationLinkRequestFactory = appLink.createAuthenticatedRequestFactory()

// HTTP Request to Marketplace
def httpBuilder = new HTTPBuilder("https://marketplace.atlassian.com")

// Content access
def xhtmlContent = ComponentLocator.getComponent(XhtmlContent)

def writer = new StringWriter()
def builder = new MarkupBuilder(writer)

// Application Link check as per https://scriptrunner.adaptavist.com/latest/confluence/interacting-with-other-apps-via-applinks.html
def handler = new ApplicationLinkResponseHandler() {
    @Override
    Map credentialsRequired(Response response) throws ResponseException {
        [:]
    }

    @Override
    Map handle(Response response) throws ResponseException {
        if (response.statusCode == 200) {
            new JsonSlurper().parseText(response.responseBodyAsString) as Map
        } else {
            [:]
        }
    }
}

def req = applicationLinkRequestFactory.createRequest(Request.MethodType.GET, "rest/plugins/latest/")
def pluginListEndpoint = req.execute(handler)

def plugins = pluginListEndpoint["plugins"]

builder.table {
    tbody {
        tr {
            th { p("App Name") }
            th { p("Vendor") }
            th { p("Installed Version") }
            th { p("Status") }
            th { p("License Type") }
            th { p("Expiry Date") }
            th { p("Marketplace Version") }
            th { p("Description") }
        }

        plugins.each { plugin ->
            if (plugin["userInstalled"] && !plugin["applicationPluginType"]) {
                def encodedKey = URLEncoder.encode(plugin["key"].toString(), 'UTF-8')
                def licenseRequest = applicationLinkRequestFactory.createRequest(Request.MethodType.GET, "rest/plugins/1.0/${encodedKey}-key/license")
                def licenseData = licenseRequest.execute(handler)
                def summaryRequest = applicationLinkRequestFactory.createRequest(Request.MethodType.GET, "rest/plugins/1.0/${encodedKey}-key/summary")
                def summaryData = summaryRequest.execute(handler)

                def addonsData
                try {
                    addonsData = httpBuilder.get(
                        path: "/rest/latest/addons/${encodedKey}"
                    ) as JSONObject
                } catch (HttpResponseException ex) {
                    addonsData = new JSONObject("{}")
                }

                def latestVersionData
                try {
                    latestVersionData = httpBuilder.get(
                        path: "/rest/latest/addons/${encodedKey}/versions/latest"
                    ) as JSONObject
                } catch (HttpResponseException ex) {
                    latestVersionData = new JSONObject("{}")
                }

                tr {
                    td { p(plugin["name"]) }
                    td { p(plugin["vendor"]["name"]) }
                    td { p(plugin["version"]) }
                    if (summaryData["enabled"].toString().contains("true")) {
                        td { p { "ac:emoticon"("ac:name": "tick") } }
                    } else {
                        td { p("") }
                    }
                    td { p(licenseData["licenseType"]) }
                    td { p(licenseData["expiryDateString"]) }
                    if (latestVersionData.has("name")) {
                        td { p(latestVersionData["name"]) }
                    } else {
                        td { p("") }
                    }
                    if (addonsData.has("summary")) {
                        td { p(addonsData["summary"]) }
                    } else {
                        td { p("") }
                    }
                }
            }
        }
    }
}

xhtmlContent.convertStorageToView(writer.toString(), context)
Discovered an issue? Report it here

Suggested for you