Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagegroovy
titlesynchTestsFromRelatedTestSets_restapi_endpoint.groovy
collapsetrue
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()
    }
	
}


Example


   




Add a custom link to the Tests top menu

Sometimes it may be useful to add a custom link to the Tests top menu.

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.

Just go the Add-ons section in your Jira administration and then "Script Fragments".


Image Added


Add a new "Raw xml module".

Image Added


And add the configuration for the link; this configuration is exactly the same as if you were going to develop your own app, so it follows Atlassian developer documentation guidelines for the "web-item" element.

Image Added


Make sure the "web-item" as a "weight" value higher than 120, to append your link to the end of the options available from the dropdown menu.


Example

Let's say that we want to add a link to "https://getxray.app" in the top Test menu, having the name "My Custom Link".

The "raw xml module" configuration would be something similar to the following snippet. 


Code Block
   <web-item key="xray-topnav-tests-meta-customlink" name="Custom link" section="raven-menu/xray.topnav.meta.section" weight="130">
      <label>My Custom Link</label>
      <link linkId="raven-topnav-test-item-meta.customlink">https://getxray.app</link>
   </web-item>


Image Added