Page History
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
triggerJenkinsBuild(httpMethod: "GET") { MultivaluedMap queryParams ->
def issueId = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
def flag = [
type : 'success',
title: "Build scheduled",
close: 'auto',
body : "A new build has been scheduled related with "+issueId
]
URL url;
def jobName = "java-junit-calc" // could come from a CF in the Test Plan
def jenkinsHostPort = "192.168.56.102:8081" // could be defined elsewhere
def token = "iFBDOBhNhaxL4T9ass93HRXun2JF161Z" // could also come from a CF in the Test Plan
def username = "admin" // probably, would need to be stored elsewhere
def password = "fa02840152aa2e4da3d8db933ec708d6" // probably, would need to be stored elsewhere
def baseURL = "http://${jenkinsHostPort}/job/${jobName}/buildWithParameters?token=${token}&TESTPLAN=$issueId"
url = new URL(baseURL);
def body_req = []
def authString = "${username}:${password}".bytes.encodeBase64().toString()
URLConnection connection = url.openConnection();
connection.requestMethod = "POST"
connection.doOutput = true
connection.addRequestProperty("Authorization", "Basic ${authString}")
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8")
connection.outputStream.withWriter("UTF-8") { new StreamingJsonBuilder(it, body_req) }
connection.connect();
log.debug(connection.getResponseCode())
log.debug(connection.getResponseMessage())
if (connection.getResponseCode() == 201) {
Response.ok(JsonOutput.toJson(flag)).build()
} else {
//Response.status(Response.Status.NOT_FOUND).entity("Problem scheduling job!").build();
}
} |
Example
...
ScriptRunner configuration
Jenkins configuration
In Jenkins, we need to generate an API token for some user, which can be done from the profile settings page.
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.web.bean.PagerFilter;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.user.UserPropertyManager
import com.atlassian.jira.propertyset.JiraPropertySetFactory;
import com.google.common.collect.ImmutableMap;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.module.propertyset.PropertySetManager;
import com.atlassian.jira.util.BuildUtils
import com.atlassian.jira.util.BuildUtilsInfo
import com.atlassian.jira.util.BuildUtilsInfoImpl
import com.atlassian.plugin.PluginAccessor
import com.atlassian.plugin.PluginManager
import com.atlassian.jira.bc.license.JiraLicenseService
import com.atlassian.jira.bc.license.JiraLicenseServiceImpl
import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.issue.IssueManager
import com.opensymphony.workflow.InvalidInputException
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
issueManager = ComponentAccessor.getIssueManager()
searchService = ComponentAccessor.getComponent(SearchService.class);
serviceAccount = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
customFieldManager = ComponentAccessor.getCustomFieldManager()
Object getIssues(jqlQuery){
// A list of GenericValues representing issues
List<Issue> searchResults = null;
SearchService.ParseResult parseResult = searchService.parseQuery(serviceAccount, jqlQuery);
if (parseResult.isValid()) {
// throws SearchException
SearchResults results = searchService.search(serviceAccount, parseResult.getQuery(), PagerFilter.getUnlimitedFilter());
searchResults = results.getIssues();
return searchResults;
}
return []
}
Object getFieldValue(issue,customField) {
//def cField = customFieldManager.getCustomFieldObject(customField)
def cField = customFieldManager.getCustomFieldObjectByName(customField)
def cFieldValue = issue.getCustomFieldValue(cField)
return cFieldValue
}
String replaceLast(String string, String substring, String replacement)
{
int index = string.lastIndexOf(substring);
if (index == -1)
return string;
return string.substring(0, index) + replacement + string.substring(index+substring.length());
}
triggerJenkinsBuildWithTestList(httpMethod: "GET") { MultivaluedMap queryParams ->
// the details of getting and modifying the current issue are ommitted for brevity
def issueId = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
def flag = [
type : 'success',
title: "Build scheduled",
close: 'auto',
body : "A new build has been scheduled related with "+issueId
]
URL url;
def jobName = "java-junit-calc-triggered" // could be defined in a CF in the Test Plan
def jenkinsHostPort = "192.168.56.102:8081" // could be defined elsewhere
def token = "iFBDOBhNhaxL4T9ass93HRXun2JF161Z" // could also come from a CF in the Test Plan
def username = "admin" // probably, would need to be stored elsewhere
def password = "fa02840152aa2e4da3d8db933ec708d6" // probably, would need to be stored elsewhere
//def baseURL = "http://${username}:${password}@${jenkinsHostPort}/job/${jobName}/build?token=${token}"
//def baseURL = "http://${jenkinsHostPort}/job/${jobName}/build?token=${token}"
jql = "issue in testPlanTests('${issueId}') and \"Test Type\" = Generic"
issues = getIssues(jql)
// we're assuming that we have Junit based Tests.. so we need to do some conversion beforehand, so maven can process the list of tests to be run
def testlist = issues.collect { getFieldValue(it,"Generic Test Definition")}
def testlist2 = testlist.collect { replaceLast(it,".","%23") }
def baseURL = "http://${jenkinsHostPort}/job/${jobName}/buildWithParameters?token=${token}&TESTPLAN=${issueId}&TESTLIST=${testlist2.join(',')}"
url = new URL(baseURL);
def body_req = []
def authString = "${username}:${password}".bytes.encodeBase64().toString()
URLConnection connection = url.openConnection();
connection.requestMethod = "POST"
connection.doOutput = true
connection.addRequestProperty("Authorization", "Basic ${authString}")
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8")
connection.outputStream.withWriter("UTF-8") { new StreamingJsonBuilder(it, body_req) }
connection.connect();
//connection.getContent();
log.debug(connection.getResponseCode())
log.debug(connection.getResponseMessage())
if (connection.getResponseCode() == 201) {
Response.ok(JsonOutput.toJson(flag)).build()
} else {
//Response.status(Response.Status.NOT_FOUND).entity("Problem scheduling job!").build();
}
} |
Example
...
ScriptRunner configuration
Jenkins configuration
In Jenkins, we need to generate an API token for some user, which can be done from the profile settings page.
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.nio.charset.StandardCharsets
@BaseScript CustomEndpointDelegate delegate
triggerBambooBuild(httpMethod: "GET") { MultivaluedMap queryParams ->
def issueId = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
def flag = [
type : 'success',
title: "Build scheduled",
close: 'auto',
body : "A new build has been scheduled related with "+issueId
]
URL url;
// curl --user admin:admin -X POST -d "default&ExecuteAllStages=true" http://yourbambooserver/rest/api/latest/queue/XRAY-JUNITCALC
def projectKey = "XRAY" // could come from a CF in the Test Plan
def planKey = "JUNITCALC" // could come from a CF in the Test Plan
def bambooHostPort = "192.168.56.102:8085" // could be defined elsewhere
def username = "admin" // probably, would need to be stored elesewhere
def password = "admin" // probably, would need to be stored elesewhere
def baseURL = "http://${bambooHostPort}/rest/api/latest/queue/${projectKey}-${planKey}"
String urlParameters = "default&ExecuteAllStages=true&bamboo.TESTPLAN=${issueId}";
byte[] postData = urlParameters.getBytes( StandardCharsets.UTF_8 );
int postDataLength = postData.length;
url = new URL(baseURL);
def body_req = []
def authString = "${username}:${password}".bytes.encodeBase64().toString()
URLConnection connection = url.openConnection();
connection.requestMethod = "POST"
connection.doOutput = true
connection.addRequestProperty("Authorization", "Basic ${authString}")
connection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty( "charset", "utf-8");
connection.setRequestProperty( "Content-Length", Integer.toString( postDataLength ));
connection.setUseCaches( false );
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write( postData );
connection.connect();
//connection.getContent();
log.debug(connection.getResponseCode())
log.debug(connection.getResponseMessage())
if (connection.getResponseCode() == 200) {
Response.ok(JsonOutput.toJson(flag)).build()
} else {
//Response.status(Response.Status.NOT_FOUND).entity("Problem scheduling job!").build();
}
} |
Example
ScripRunner configuration
Bamboo configuration
The project itself is a normal one; the only thing relevant to mention is that this project is a parameterized one, so it receives a TESTPLAN variable, that in our case will be coming from Jira.
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.web.bean.PagerFilter;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.user.UserPropertyManager
import com.atlassian.jira.propertyset.JiraPropertySetFactory;
import com.google.common.collect.ImmutableMap;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.module.propertyset.PropertySetManager;
import com.atlassian.jira.util.BuildUtils
import com.atlassian.jira.util.BuildUtilsInfo
import com.atlassian.jira.util.BuildUtilsInfoImpl
import com.atlassian.plugin.PluginAccessor
import com.atlassian.plugin.PluginManager
import com.atlassian.jira.bc.license.JiraLicenseService
import com.atlassian.jira.bc.license.JiraLicenseServiceImpl
import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.issue.IssueManager
import com.opensymphony.workflow.InvalidInputException
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.nio.charset.StandardCharsets
@BaseScript CustomEndpointDelegate delegate
issueManager = ComponentAccessor.getIssueManager()
searchService = ComponentAccessor.getComponent(SearchService.class);
serviceAccount = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
customFieldManager = ComponentAccessor.getCustomFieldManager()
Object getIssues(jqlQuery){
// A list of GenericValues representing issues
List<Issue> searchResults = null;
SearchService.ParseResult parseResult = searchService.parseQuery(serviceAccount, jqlQuery);
if (parseResult.isValid()) {
// throws SearchException
SearchResults results = searchService.search(serviceAccount, parseResult.getQuery(), PagerFilter.getUnlimitedFilter());
searchResults = results.getIssues();
return searchResults;
}
return []
}
Object getFieldValue(issue,customField) {
def cField = customFieldManager.getCustomFieldObjectByName(customField)
def cFieldValue = issue.getCustomFieldValue(cField)
return cFieldValue
}
String replaceLast(String string, String substring, String replacement)
{
int index = string.lastIndexOf(substring);
if (index == -1)
return string;
return string.substring(0, index) + replacement + string.substring(index+substring.length());
}
triggerBambooBuildWithTestList(httpMethod: "GET") { MultivaluedMap queryParams ->
def issueId = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
def flag = [
type : 'success',
title: "Build scheduled",
close: 'auto',
body : "A new build has been scheduled related with "+issueId
]
jql = "issue in testPlanTests('${issueId}') and \"Test Type\" = Generic"
issues = getIssues(jql)
// // we're assuming that we have Junit based Tests.. so we need to do some conversion beforehand, so maven can process the list of tests to be run
def testlist = issues.collect { getFieldValue(it,"Generic Test Definition")}
def testlist2 = testlist.collect { replaceLast(it,".","%23") }
URL url;
// curl --user admin:admin -X POST -d "default&ExecuteAllStages=true&bamboo.TESTLIST=com.xpand.java.CalcTest#CanAddNumbers" http://yourbambooserver/rest/api/latest/queue/XRAY-JUNITCALCPARAMS
def projectKey = "XRAY" // could come from a CF in the Test Plan
def planKey = "JUNITCALCPARAMS" // could come from a CF in the Test Plan
def stage = "default" // could be hardcoded or come from a CF in the Test Plan
def bambooHostPort = "192.168.56.102:8085" // could be defined elsewhere
def username = "admin" // probably, would need to be stored elsewhere
def password = "admin" // probably, would need to be stored elsewhere
def baseURL = "http://${bambooHostPort}/rest/api/latest/queue/${projectKey}-${planKey}"
String urlParameters = "${stage}&ExecuteAllStages=true&bamboo.TESTPLAN=${issueId}&bamboo.TESTLIST=${testlist2.join(',')}";
byte[] postData = urlParameters.getBytes( StandardCharsets.UTF_8 );
int postDataLength = postData.length;
url = new URL(baseURL);
def body_req = []
def authString = "${username}:${password}".bytes.encodeBase64().toString()
URLConnection connection = url.openConnection();
connection.requestMethod = "POST"
connection.doOutput = true
connection.addRequestProperty("Authorization", "Basic ${authString}")
connection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty( "charset", "utf-8");
connection.setRequestProperty( "Content-Length", Integer.toString( postDataLength ));
connection.setUseCaches( false );
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write( postData );
connection.connect();
//connection.getContent();
log.debug(connection.getResponseCode())
log.debug(connection.getResponseMessage())
if (connection.getResponseCode() == 200) {
Response.ok(JsonOutput.toJson(flag)).build()
} else {
//Response.status(Response.Status.NOT_FOUND).entity("Problem scheduling job!").build();
}
} |
Example
ScripRunner configuration
Bamboo configuration
...
- receive the source issue key that triggered the synchronization request (e.g. Test Plan, Test Execution, Sub Test Execution)
- obtain the linked Test Sets, using JQL
- obtain the Tests on each Test Set, using JQL
- submit a REST API request to Xray specific endpoints to add the Test to the entity (e.g Test Plan)
ScripRunner configuration
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import groovy.json.JsonSlurper;
import groovy.json.StreamingJsonBuilder;
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.nio.charset.StandardCharsets
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLinkManager
import com.atlassian.jira.issue.link.IssueLinkType
import com.atlassian.jira.issue.link.IssueLinkTypeManager
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.builder.JqlQueryBuilder
import com.atlassian.jira.user.util.UserUtil
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.web.bean.PagerFilter;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.user.UserPropertyManager
import com.atlassian.jira.propertyset.JiraPropertySetFactory;
import com.google.common.collect.ImmutableMap;
import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.module.propertyset.PropertySetManager;
import com.atlassian.jira.util.BuildUtils
import com.atlassian.jira.util.BuildUtilsInfo
import com.atlassian.jira.util.BuildUtilsInfoImpl
import com.atlassian.plugin.PluginAccessor
import com.atlassian.plugin.PluginManager
import com.atlassian.jira.bc.license.JiraLicenseService
import com.atlassian.jira.bc.license.JiraLicenseServiceImpl
import org.apache.log4j.Level
import org.apache.log4j.Logger
import com.atlassian.jira.issue.IssueManager
import groovy.json.StreamingJsonBuilder
@BaseScript CustomEndpointDelegate delegate
boolean associateTestsToXrayIssue(endpoint, issueKey,listOfTestKeys){
def jiraBaseUrl = com.atlassian.jira.component.ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
def endpointUrl = "${jiraBaseUrl}/rest/raven/1.0/api/${endpoint}/${issueKey}/test"
log.debug("issueKey: "+issueKey)
log.debug("listOfTestKeys: "+listOfTestKeys)
log.debug("jirabaseurl: "+jiraBaseUrl)
log.debug("endpoint: "+endpointUrl)
url = new URL(endpointUrl);
def body_req = [ "add": listOfTestKeys ]
// you should use a specific user for this purpose
username = "admin"
password = "admin"
def authString = "${username}:${password}".bytes.encodeBase64().toString()
URLConnection connection = url.openConnection();
connection.requestMethod = "POST"
connection.doOutput = true
connection.addRequestProperty("Authorization", "Basic ${authString}")
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8")
connection.outputStream.withWriter("UTF-8") { new StreamingJsonBuilder(it, body_req) }
connection.connect();
log.debug(connection.getResponseCode())
log.debug(connection.getResponseMessage())
if (connection.getResponseCode() == 200) {
// OK
return true;
} else {
// error
return false;
}
}
boolean associateTestsToTestPlan(testPlanKey, listOfTestKeys){
return associateTestsToXrayIssue("testplan", testPlanKey, listOfTestKeys)
}
boolean associateTestsToTestExecution(testExecutionKey, listOfTestKeys){
return associateTestsToXrayIssue("testexec", testExecutionKey, listOfTestKeys)
}
Object getIssues(jqlQuery){
// A list of GenericValues representing issues
List<Issue> searchResults = null;
SearchService.ParseResult parseResult = searchService.parseQuery(serviceAccount, jqlQuery);
if (parseResult.isValid()) {
// throws SearchException
SearchResults results = searchService.search(serviceAccount, parseResult.getQuery(), PagerFilter.getUnlimitedFilter());
searchResults = results.getIssues();
return searchResults;
}
return []
}
synchTestsFromRelatedTestSets(httpMethod: "GET") { MultivaluedMap queryParams ->
// issue_key may refer to a Test Plan or to a Test Execution
def issue_key = queryParams.getFirst("issueId") as String // use the issueId to retrieve this issue
projectManager = ComponentAccessor.getProjectManager()
componentManager = ComponentManager.getInstance();
searchService = ComponentAccessor.getComponent(SearchService.class);
issueManager = ComponentAccessor.getIssueManager()
customFieldManager = ComponentAccessor.getCustomFieldManager()
userUtil = ComponentAccessor.getUserUtil();
serviceAccount = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
pluginAccessor = componentManager.getPluginAccessor();
MutableIssue issue = issueManager.getIssueObject(issue_key)
Logger.getLogger("com.onresolve").setLevel(Level.DEBUG)
// assume that Test Plan/Execution is linked to Test Sets using the issue link "relates to"; customize if needed
jql = "issue in linkedIssues('${issue_key}', 'relates to')"
testset_issues = getIssues(jql)
def had_errors = false
def success = false
testset_issues.each {
// process only "Test Set" issues
if (it.issueType.name == "Test Set") {
jql = "issue in testSetTests('${it.key}')"
issues = getIssues(jql)
test_keys = issues.collect{ it.key }
//log.debug(test_keys)
if (issue.issueType.name == "Test Plan") {
success = associateTestsToTestPlan(issue_key, test_keys)
} else if ((issue.issueType.name == "Sub Test Execution") || (issue.issueType.name == "Test Execution")) {
success = associateTestsToTestExecution(issue_key, test_keys)
}
if (!success) {
had_errors = true
}
}
}
def flag = []
if (success) {
flag = [
type : 'success',
title: "Test Sets Synchronization",
close: 'auto',
body : "Tests have been synchronized for " + issue_key
]
Response.ok(JsonOutput.toJson(flag)).build()
} else {
flag = [
type : 'success',
title: "Test Sets Synchronization",
close: 'auto',
body : "Tests have been synchronized for " + issue_key
]
Response.serverError().entity([error: "some Tests were no synchronized"]).build()
}
} |
...
As an example, you may have an internal documentation/Confluence space with some valuable information concerning Xray usage in your organization.
ScripRunner configuration
Adding an entry to the top menu is easy.
...











