Programmatically connect to Bitbucket/Bamboo from Jira for REST API use

Hi All,

As far as i saw, many people (include me) use REST API calls from Jira to GET/POST data from/to Bitbucket/Bamboo.

Most people are using hard coded credentials with Base64 Encode.

However, putting your credentials  hard coded (even in Base64 Encode format) is not the best practice, so i will share here a way that many users are not familiar with.

By using the following, you will be able to perform your REST API calls from Jira without putting credentials.

This will use the current login user credentials in Jira, to connect to Bitbucket/Bamboo

import com.atlassian.applinks.api.ApplicationLinkRequestFactory;
import com.atlassian.applinks.api.ApplicationLink;
import com.atlassian.applinks.api.ApplicationLinkService;
import com.atlassian.applinks.api.application.bamboo.BambooApplicationType;
import com.atlassian.applinks.api.application.bitbucket.BitbucketApplicationType;

In order to use this technique you will have to link Bitbucket/Bamboo to your Jira link applications.

And here is an example for how to use it:

/*to get the Bitbucket link*/
private
ApplicationLink getPrimaryBitbucketLinkLink() {
ApplicationLinkService applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class);
final ApplicationLink bitbucketLink = applicationLinkService.getPrimaryApplicationLink(BitbucketApplicationType.class);

return bitbucketLink;
}

/*to get the Bamboo link*/
private ApplicationLink getPrimaryBambooLink() {
ApplicationLinkService applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class);
final ApplicationLink bambooLink = applicationLinkService.getPrimaryApplicationLink(BambooApplicationType.class);

return bambooLink;
}

/*REST API GET request*/
private JSONObject getJsonData(ApplicationLink applicationLink, String restURL) throws Exception {
ApplicationLink appLink = applicationLink;
ApplicationLinkRequestFactory authenticatedRequestFactory = appLink.createAuthenticatedRequestFactory();

final Response[] res = new Response[1];

authenticatedRequestFactory
.createRequest(Request.MethodType.GET, restURL)
.addHeader("Content-Type", "application/json")
.execute(new ResponseHandler<Response>() {
@Override
public void handle(Response response) {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
try {
throw new Exception(response.getResponseBodyAsString());
} catch (Exception e) {
e.printStackTrace();
}
}
res[0] = response;
}
});

JSONObject jsonObject = new JSONObject(res[0].getResponseBodyAsString());

return jsonObject;
}

/*call the getJsonData function to get data from where we want*/
getJsonData(getPrimaryBitbucketLinkLink(), "/rest/api/latest/....")

 

Hope this will serve you well as it served me.

Feel free to reply my article and ask questions or share your thought with my methods.

 

Thanks,

Nir Haimov

11 comments

Patrick Flood January 24, 2019

Thanks for this - I'd been banging my head against the right way to make rest calls via the applinks for way too long today!

Like Nir Haimov likes this
Nir Haimov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
January 24, 2019

I was looking days till i figured it out.

That's why i felt like i have to post this article .

Glad it helped you :)

Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 16, 2019

Hi Nir Haimov,

 Thank you for sharing this, if I can ask some more help, 

our requirement is to fill a JIRA custom field by accessing Bitbucket;  compare two tags in Bitbucket using REST API and get the JIRA issues used to commit between those tags. This fill may be required during a workflow transition.

So, how I can add your REST End point to a field behavior or to a Listener which will fill the JIRA custom field?

Thanks in advance

with warm regards

ramki

 

PS: in another use case I have one behavior to a single line text custom field like this

getFieldByName("Product 1 Branch").convertToSingleSelect([
ajaxOptions: [
url : getBaseUrl() + "/rest/scriptrunner/latest/custom/frnversions",
query: true,
formatResponse: "general"
]
])

 

which is accessing own JIRA dB and getting the projectversion based on user input. frnversions is the JIRA REST END Point for me in this case; I am looking for similar use case with Bitbucket access

Nir Haimov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 17, 2019

Hi @Ramakrishnan Srinivasan ,

As I understand you are using Scriptrunner,
I think it will be better and more simple if you use scripted field.

The method I mentioned in my article simply let you connect to Bitbucket/Bamboo without hard coded credentials, getting data with rest api and compare is pure code that need to be written.

Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 18, 2019

Hi Nir Haimov,

 Thank you for the response, yes I am using script runner.

Let me try and give it a shot with your code as base

 

with warm regards

ramki

Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 23, 2019

Use Case: from Atlassian Bitbucket server get commit data between two tags of a given Project and repo in that project; 

Use Script Runner - script console  to run this script

Assumption: JIRA issue entry is mandatory in commits. ; JIRA is configured with BITBUCKET server application link and user has required accesses

 

import org.apache.log4j.Level
import org.apache.log4j.Logger
def log = Logger.getLogger("com.onresolve.jira.groovy")
log.setLevel(Level.DEBUG)

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.bitbucket.BitbucketApplicationType;
import com.atlassian.jira.issue.Issue
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
import groovy.json.JsonSlurper
import com.atlassian.jira.util.json.JSONObject
import net.sf.json.JSONArray

/**
* Retrieve the primary Bitbucket application link
* @return bitbucket server app link
*/
def ApplicationLink getPrimaryBitBucketLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink BBLink = applicationLinkService.getPrimaryApplicationLink(BitbucketApplicationType.class)
BBLink
}

def BitBucketLink = getPrimaryBitBucketLink()
assert BitBucketLink // must have a working app link set up

def authenticatedRequestFactory = BitBucketLink.createAuthenticatedRequestFactory()

def jira_issues_list = []
authenticatedRequestFactory
.createRequest(Request.MethodType.GET, "/rest/api/1.0/projects/<PROJECT KEY>/repos/<REPO NAME>/commits?since=refs/tags/<TAG NAME A>&until=refs/tags/<TAG NAME B>")
.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())
}
else {
//log.info(response.getResponseBodyAsString())
JSONObject jsonObject = new JSONObject(response.getResponseBodyAsString())
log.info(jsonObject)
// Groovy iterate through jsonObject
//https://stackoverflow.com/questions/41522477/how-to-iterate-through-json-response?rq=1
//log.info(jsonObject)
def slurped = new JsonSlurper().parseText(response.getResponseBodyAsString())
log.info(slurped)
def ids = slurped.values*.id
//log.info(ids)
def properties = slurped.values*.properties
//log.info(properties)
properties.each{
//log.info(it["jira-key"])

it["jira-key"].each {
//log.info(it)
jira_issues_list.add(it)

}
}
}
}
})
log.info(jira_issues_list)
def uniq_jira_issues_list = jira_issues_list.unique()
log.info(uniq_jira_issues_list)
//return uniq_jira_issues_list.sort().toListString()

 

Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 23, 2019

Use Case: get all repos of a given Atlassian Bitbucket PROJECT and show them as single Selects in JIRA

Use : Script Runner - REST END POINT and Behavior on that custom field

Assumption: ; JIRA is configured with BITBUCKET server application link and user has required accesses

Create REST END POINT g2_get_repos

Note the line  import javax.ws.rs.core.Response as coreResponse; aliased import so that two different Responses work together; (it could be my understanding issue, I could not make them work together without this aliasing)

import org.apache.log4j.Level
import org.apache.log4j.Logger
def log = Logger.getLogger("com.onresolve.jira.groovy")
log.setLevel(Level.DEBUG)


import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonBuilder
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method

import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response as coreResponse

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.bitbucket.BitbucketApplicationType
import com.atlassian.jira.issue.Issue
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
import groovy.json.JsonSlurper
import com.atlassian.jira.util.json.JSONObject
import net.sf.json.JSONArray

@BaseScript CustomEndpointDelegate delegate

def ApplicationLink getPrimaryBitBucketLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink BBLink = applicationLinkService.getPrimaryApplicationLink(BitbucketApplicationType.class)
BBLink
}

g2_get_repos(httpMethod: "GET") { MultivaluedMap queryParams ->

def query = queryParams.getFirst("query") as String
//log.info("RAMKI query from Behavior" + query)

def restStr = "/rest/api/1.0/projects/"+query+"/repos"

/**
* Retrieve the primary Bitbucket application link
* @return bitbucket app link
*/

def BitBucketLink = getPrimaryBitBucketLink()
assert BitBucketLink // must have a working app link set up

def authenticatedRequestFactory = BitBucketLink.createAuthenticatedRequestFactory()

def thisProjectRepos = []

authenticatedRequestFactory
.createRequest(Request.MethodType.GET, restStr)
.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())
}
else {
def slurped = new JsonSlurper().parseText(response.getResponseBodyAsString())
def slugs = slurped.values*.slug
slugs.each {
//log.info(it)
thisProjectRepos.add(it)
}
}
}
})

//log.info(thisProjectRepos)
def rt = [:]
rt = [
items : thisProjectRepos.collect { repo ->
def repoName = repo
[
value: repoName,
html : repoName,
label: repoName,
]
},
footer: "Choose repo from BB for this G2 Issue's project"
]

return coreResponse.ok(new JsonBuilder(rt).toString()).build()

}

 

Create a single line text field and add a behavior to that filed in initialiser section of the behavior; take care of Project, Issue Type mapping

behavior on custom field where the repos have to be presented as Single Selects

// assumption is JIRA Project Key and Bitbucket Project Keys are same
// this pkey is Bitbucket Project key sent from here
def pkey = underlyingIssue.projectObject.key
//pkey="PROJECT"
//"Select BB Repo." is custom single line text field converted to Single Select by this behavior
getFieldByName("Select BB Repo.").convertToSingleSelect([
ajaxOptions: [
url : getBaseUrl() + "/rest/scriptrunner/latest/custom/g2_get_repos?query="+pkey,
//because we are sending the pkey from here in full, query is set to false
query: false, // if true, keep going back to the sever for each keystroke
formatResponse: "general",
]
])

 

Like eddyg likes this
Nir Haimov
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
April 23, 2019

Hi @Ramakrishnan Srinivasan ,

I never tried to get diff commits between tags.

I did managed to get diff commits between 2 branches on the same repo.

Any way, retrieving the data has nothing to do with the method I published here.

My method simply let you perform REST API calls without the need to hard code your user name and password for authentication.

If what you need is help with getting specific REST response, you should rise a question in the community.

Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 23, 2019

Use Case: get JIRA issues used between two Tags from Atlassian Bitbucket Server into a scripted field in JIRA

Assumption: JIRA issues are entered in all Bitbucket Commits; JIRA is configured with BITBUCKET server application link and user has required accesses

Use Script Runner Scripted Field to create a custom field; add the below scirpt in that script field code section 

Note: you can create two other custom fields in JIRA where the Tag Names are entered; read those values and pass them to TAG NAME A and TAG NAME B here; same for BITBUCKET PROJECT and BITBUCKET REPO NAMEs

 

import org.apache.log4j.Level
import org.apache.log4j.Logger
def log = Logger.getLogger("com.onresolve.jira.groovy")
log.setLevel(Level.DEBUG)

import com.atlassian.applinks.api.ApplicationLink
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.bitbucket.BitbucketApplicationType;
import com.atlassian.jira.issue.Issue
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
import groovy.json.JsonSlurper
import com.atlassian.jira.util.json.JSONObject
import net.sf.json.JSONArray


/**
* Retrieve the primary Bitbucket application link
* @return bitbucket app link
*/
def ApplicationLink getPrimaryBitBucketLink() {
def applicationLinkService = ComponentLocator.getComponent(ApplicationLinkService.class)
final ApplicationLink BBLink = applicationLinkService.getPrimaryApplicationLink(BitbucketApplicationType.class)
BBLink
}


def BitBucketLink = getPrimaryBitBucketLink()
assert BitBucketLink // must have a working app link set up

def authenticatedRequestFactory = BitBucketLink.createAuthenticatedRequestFactory()

def jira_issues_list = []
authenticatedRequestFactory
.createRequest(Request.MethodType.GET, "/rest/api/1.0/projects/<BITBUCKET PROJECT>/repos/<BITBUCKET REPO NAME>/commits?since=refs/tags/<TAG NAME A>&until=refs/tags/<TAG NAME B>")
.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())
}
else {
//log.info(response.getResponseBodyAsString())
JSONObject jsonObject = new JSONObject(response.getResponseBodyAsString())
// Groovy iterate through jsonObject
//https://stackoverflow.com/questions/41522477/how-to-iterate-through-json-response?rq=1
//log.info(jsonObject)
def slurped = new JsonSlurper().parseText(response.getResponseBodyAsString())
//log.info(slurped)
def ids = slurped.values*.id
//log.info(ids)
def properties = slurped.values*.properties["jira-key"]
//log.info(properties)
properties.each{
it.each {
jira_issues_list.add(it)
}
// log.info(it)
//log.info(it["jira-key"])

//it["jira-key"].each {
//log.info(it)
//jira_issues_list.add(it)

// }

}
}
}
})
log.info(jira_issues_list)
def uniq_sorted_jira_issues_list = jira_issues_list.unique().sort()
log.info(uniq_sorted_jira_issues_list)
return uniq_sorted_jira_issues_list
Ramakrishnan Srinivasan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
April 23, 2019

Thanks to Nir Haimov, I added more code here what I could achieve with his initial code; I posted some of the scenarios here, not extensively tested, thought it might be helpful to anyone looking further.

Alexander Lackner October 8, 2021

Tried a lot of stuff without success before finding this. Now everything i wanted to do with Jira Workflows and the Bitbucket API works! 

Thanks a ton! 

Like Nir Haimov likes this

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events