We are currently trying to use ScriptRunner to execute a workflow transition on a remote JIRA instance via a post function and are running into some interesting issues. Basically, we have a custom workflows on two JIRA instances (say A and B). When we execute a specific workflow transition on an issue in A, we want to also execute a workflow transition on a remotely linked issue in B.
I have attached the script we are using. It starts off by declaring parameters that are going to be used in the remainder of the script. It then grabs all remote issue links for the current issue, and for each one of them, calls the executeTransition function. That function will grab all transitions for the remote issue using the JIRA REST API, find the appropriate transition, and then make a POST call to the JIRA REST API to execute the workflow transition.
Here is the API call https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition. We also use cURL in order to accomplish this (I haven't found a better way of making POST requests using the native groovy language).
Here's the issue we are running into: the script works everywhere EXCEPT in JIRA instance A. I have a local copy of JIRA and the script works just fine there. We even made a dummy field that would spit out the cURL commands generated by the script (specifically the one in line 63), then tried executing those commands in command line on a local machine AND in bash on the server that hosts JIRA instance A. The cURL command generated by the script works in both cases, it just doesn't work when we call curl.execute() (line 64) in the script.
We're not sure why it's working only outside of that specific instance of JIRA and were wondering if you have any clue as to what could be causing this.
Thank you for any help you can provide!
package clearleap import com.atlassian.jira.ComponentManager import com.atlassian.jira.bc.issue.link.RemoteIssueLinkService import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.link.RemoteIssueLink import groovy.json.JsonSlurper // === BEGIN PARAMETERS === def curlCmd = "curl" def username = "USERNAME" def password = "PASSWORD" def transitionName = "TRANSITION_NAME" def protocol = "https://" // === END PARAMETERS === RemoteIssueLinkService linkService = ComponentManager.getComponentInstanceOfType(RemoteIssueLinkService.class) String requestAuth = "" String requestData = "" String request = "" String curl = "" InputStream response; Map responseObj; def executeTransition = { RemoteIssueLink link -> String url = link.getUrl() if(!url || url.length() == 0 || !url.contains(protocol)) return String remoteBaseUrl = url.substring(0, url.indexOf("/", url.indexOf(protocol) + protocol.length())) String remoteIssueId = url.substring(url.lastIndexOf("/") + 1) request = "$remoteBaseUrl/rest/api/2/issue/$remoteIssueId/transitions" curl = "$curlCmd -u $username:$password $request" response = curl.execute().inputStream responseObj = new JsonSlurper().parse(response) as Map ArrayList<Map> transitions = responseObj.transitions as ArrayList<Map> String transitionId = null transitions.each { Map transition -> if(transition.name == transitionName) { transitionId = transition.id } } if(transitionId == null || transitionId.length() == 0) return requestAuth = "Basic " + "$username:$password".bytes.encodeBase64() requestData = """ { \\"transition\\": { \\"id\\": \\"$transitionId\\" } } """ request = "$remoteBaseUrl/rest/api/2/issue/$remoteIssueId/transitions" curl = "$curlCmd -H \"Content-Type: application/json\" -H \"Authorization: $requestAuth\" -X POST -d \"$requestData\" -u $username:$password $request" curl.execute() } def loggedInUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() def getLinksResult = linkService.getRemoteIssueLinksForIssue(loggedInUser, issue) getLinksResult.getRemoteIssueLinks().forEach { RemoteIssueLink link -> executeTransition(link) }
I don't know... I would have guessed an SSL cert problem but if works from bash it should work from jira.
Did you try both curl commands including with the data that's it's supposed to send?
Capture the curl output with curl.execute().text, then log that. You might need to check the docs for the execute method to see if stdout and stderr goes to different places. curl -v may help too.
Another way is to install Fiddler, then tell curl to use a proxy which is the machine you install fiddler on, port 8888. Then you will see clearly what the issue is. But unless you can use http, not https, you have to dick around with certificates so you can effectively do a man-in-the-middle attack.
The native way to do this by the way is using HttpBuilder, which is included in SR. Not sure that that will help though, although the error reporting will be easier.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you for the suggestions - I apologize for the late response, some competing priorities got in the way of our work on this. Yes, we tried the commands with the data its supposed to send, and receive back the expected data in the response when run from bash. We've seen this on two completely separate systems now, so we'll give some of the troubleshooting approaches you suggest a try. Thanks again for the input and suggestions!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks Jamie. I have changed the script to use HTTPBuilder instead of cURL. Much cleaner! It looks like, however, I am running into a similar issue as this one: https://answers.atlassian.com/questions/9385129. Here is my HTTPBuilder setup:
def http = new HTTPBuilder(remoteUrl) http.client.addRequestInterceptor(new HttpRequestInterceptor() { void process(HttpRequest httpRequest, HttpContext httpContext) { httpRequest.addHeader("Authorization", "Basic " + "$username:$password".bytes.encodeBase64().toString()) } }) http.request(POST, JSON) { req -> headers.Accept = 'application/json' body = { transition { id: transitionId } } };
For some reason I get a 400 Bad Request as well.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The error should be in the response entity... you can grab it by using a failure handler (or a debugging proxy). Have a look at the HttpBuilder docs.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I was able to find the error!! I added a response.failure handler and printed out the details of the failure context. For some reason, it was not setting the transition id to any value. I looked at the JsonGroovyBuilder docs and tried setting body in several ways that should have been parsed correctly, but for some reason the transition id was always null, even if I hard-coded a value instead of using the transitionId variable. Eventually, I just had to settle on using a raw string and it worked:
body = """ { "transition": { "id": "$transitionId" } } """
Thanks Jamie, you were a great help in getting this done!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Irtiza,
Can you Please share the complete code sample for making http call from a post-function.
Thanks,
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Tarun,
Here is the part of the script that revolves around making an HTTP POST request in a post-function.
First, we create an instance of HTTPBuilder, which is built into ScriptRunner. The important part is adding the request interceptor so the target URL will authenticate properly.
def username = "USERNAME" def password = "PASSWORD" def getBuilder = { String url = "DESIRED URL TO PING" def http = new HTTPBuilder(url) http.client.addRequestInterceptor(new HttpRequestInterceptor() { void process(HttpRequest httpRequest, HttpContext httpContext) { httpRequest.addHeader("Authorization", "Basic " + "$username:$password".bytes.encodeBase64().toString()) } }) return http }
Then, we use the HTTPBuilder object to ping the desired URL and get a response. This is just a sample of what the body of the request should look like.
def http = getBuilder() def result = http.request(POST, JSON) { req -> headers.Accept = 'application/json' body = """ { "transition": { "id": "$transitionId" } } """ response.success = { resp, json -> return json } response.failure = { resp -> return resp.statusLine } };
If you are making a GET request, you can follow the same procedure, just change the request type from POST to GET. The result will be a map object which you can parse accordingly.
Let me know if this helps.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
syntax errors.... imports ?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.