groovybitbucketjirajira-rest-apiatlassian-plugin-sdk

ScriptRunner Merge Check for Bitbucket Get Associated Jira Issue


I would like to set up a Merge Check that will block a merge if the associated Jira Issue has any blocking tasks.

I have searched for the solution before coming here and found the following example. However, right at import com.atlassian.jira.component.ComponentAccessor I get an "unable to resolve class" error. I am running the latest version of Scriptrunner for Bitbucket Server v.7.10.0.

I have attempted to use AppLinks as shown in the following example and see that I am able to access the Jira Api this way, however I am unsure of how to actually determine the Task Id of the issue linked to the triggering Merge Branch.

I am having a hard time finding any up to date documentation that doesn't involve random Adaptavist or Atlasian forumn posts. Does anyone have any insight for how I can get started?


Solution

  • The ComponentAccessor is no longer supported, and my non-descript errors were a result of lower permissions. Once being promoted to admin I was able to access the Script Editor window which allowed me to perform the following...

    To replace the provided "Is Blocked on Jira" example I instead created a Jira Issue Controller class that is responsible for accessing the information using appLinks and Jira API calls.

    import com.atlassian.applinks.api.ApplicationLinkResponseHandler
    import com.atlassian.applinks.api.ApplicationLinkService
    import com.atlassian.applinks.api.application.jira.JiraApplicationType
    import com.atlassian.sal.api.component.ComponentLocator
    import com.atlassian.sal.api.net.Response
    import com.atlassian.sal.api.net.ResponseException
    import groovy.json.JsonSlurper
     
    import static com.atlassian.sal.api.net.Request.MethodType.GET
    
    // This class is responsiblefor retrieving specific issues from the JiraAppLink (api)
    class IssueController{
        private static String issueEndpoint = "/rest/api/2/issue/"
    
        // doc - https://docs.atlassian.com/applinks-api/3.2/com/atlassian/applinks/api/ApplicationLinkResponseHandler.html
        private static ApplicationLinkResponseHandler<Map> handler = new ApplicationLinkResponseHandler<Map>() { 
            // Triggered when a call to a remote server failed because the caller does not have an established session 
            // with the server and will need to authorize requests first by visiting the authorization URL provided by 
            // AuthorisationURIGenerator.
            @Override
            Map credentialsRequired(Response response) throws ResponseException {
                return null
            }
        
            @Override
            Map handle(Response response) throws ResponseException {
                assert response.statusCode == 200
                new JsonSlurper().parseText(response.getResponseBodyAsString()) as Map
            }
        }
    
        static RestObjects.JiraIssue GetJiraIssue(String issueKey){
            def appLinkService = ComponentLocator.getComponent(ApplicationLinkService)
            def appLink = appLinkService.getPrimaryApplicationLink(JiraApplicationType)
            def appLinkUrl = appLink.getDisplayUrl().toString()
            // doc - https://docs.atlassian.com/applinks-api/3.2/com/atlassian/applinks/api/ApplicationLinkRequestFactory.html
            def applicationLinkRequestFactory = appLink.createAuthenticatedRequestFactory()
            // doc - https://docs.atlassian.com/applinks-api/3.2/com/atlassian/applinks/api/ApplicationLinkRequest.html
            def linkRequest = applicationLinkRequestFactory.createRequest(GET, issueEndpoint + issueKey )
            def response = linkRequest.execute(handler)
            return new RestObjects.JiraIssue(response);
        }
    }
    

    This class returns a custom object RestObjects.JiraIssue I use to parse the specific API response Map into custom Classes. A short example of the JiraIssue class and how I am able to access the linked blocking issues is shown below.

    package JiraApp.RestObjects
    
    class JiraIssue
    {
        String key
        Fields fields
    
        //public
        JiraIssue(keyStr){
            this.key = keyStr
        }
        JiraIssue(Map apiResponseMap)
        {
            this.key = apiResponseMap["key"]
            this.fields = new Fields(apiResponseMap["fields"] as Map)
        }
    
        //Returns list of the keys linked blocking issues 
        String[] blockingIssueKeys(){
            String[] keys = []
            fields.issueLinks.each { 
                link -> 
                    if(link.type.name == "Blocks" && link.getInwardIssue() != null)
                    {
                        def inwardIssue = link.getInwardIssue()
                        keys += inwardIssue.key
                    }
            }
            return keys;
        }
    
        class Fields{
            Fields(Map fieldsMap){.....
    

    The rest of this class uses nested classes to parse the input Maps for their fields into usable objects. It also contains objects/functions I have created for my specific application.

    I hope this answer is helpful to someone, the documentation on ScriptRunner for bitbucket is limited.