Hi,
I have an curl command to trigger an external url. Need some help on script runner groovy script for below command format.
curl --location --request POST 'https://XXXX.XXXX.com:443/api/1/rest/slsched/feed/XXXXXXXXXX/projects/SXXXXXX%20xxxxx/JIRA-Access-TEST%20Task' \ --header 'Authorization: Bearer xxxxxxxxxxxxxxxxxxxx' \ --header 'Content-Type: application/json' \ --data-raw '[ { "Request": { "issueKey": "JJIM-26", "issueId": "This is passthorugh response" } } ]'
Thanks in advance.
Regards,
Aditya.D
Hi
You could execute the curl command as a shell command using the "execute()" method available on groovy strings.
def cmd = """curl --location --request POST 'https://XXXX.XXXX.com:443/api/1/rest/slsched/feed/XXXXXXXXXX/projects/SXXXXXX%20xxxxx/JIRA-Access-TEST%20Task' \ --header 'Authorization: Bearer xxxxxxxxxxxxxxxxxxxx' \ --header 'Content-Type: application/json' \ --data-raw '[ { "Request": { "issueKey": "JJIM-26", "issueId": "This is passthorugh response" } } ]'"""
def process = command.execute()
def sOut = new StringBuilder()
def sErr = new StringBuilder()
process.consumeProcessOutput(sOut,sErr)
process.waitForOrKill(60000) //milliseconds... timeout
Then you could process the sOut strings
Or you can use some native html processing classes.
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.ContentType
def httpBuilder = new HTTPBuilder(https://XXXX.XXXX.com:443)
def response = httpBuilder.request(Method.POST, ContentType.JSON){
uri.path = "/api/1/rest/slsched/feed/XXXXXXXXXX/projects/SXXXXXX%20xxxxx/JIRA-Access-TEST%20Task"
headers.Authorization = "Bearer xxxxxxxxxxxxxxxxxxxx"
body = [Request:[issueKey:"JJIM-26", issueId:""]]
response.success = { response, json ->
return json
}
response.failure = { resp, data ->
log.error " httprequest failed: $data"
return null
}
}
This will give you some native parsing of the response and response error handling etc.
You can either escape the \ or change the type of groovy string. The dollar-slashy string is perfect for this use case:
(also, I had an error in the script where I called the command string "cmd" but then tried to execute as if I had called it "command"
Here it is fully corrected
def cmd = $/curl --location --request POST 'https://XXXX.XXXX.com:443/api/1/rest/slsched/feed/XXXXXXXXXX/projects/SXXXXXX%20xxxxx/JIRA-Access-TEST%20Task' \ --header 'Authorization: Bearer xxxxxxxxxxxxxxxxxxxx' \ --header 'Content-Type: application/json' \ --data-raw '[ { "Request": { "issueKey": "JJIM-26", "issueId": "This is passthorugh response" } } ]'/$
def process = cmd.execute()
def sOut = new StringBuilder()
def sErr = new StringBuilder()
process.consumeProcessOutput(sOut,sErr)
process.waitForOrKill(60000) //milliseconds... timeout
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks again Peter-Dave for valuable script. Could you help me out with two more improvements.
1. First : "Issue key" & "issue id" has to come dynamically. I'm placing this script in post function. As soon status changed, script should pick the "issuekey" and "issuekey id" of that issue.
2. Second : No transaction success information on issue page. Is it possible to print transaction success code/ log info on issue page like activity tab/history/worklog. Some where we can refer as soon as transition is completed.
BR,
Aditya.D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I think given your use case, I would strongly recommend using the httpbuilder approach rather than the curl approach.
Then, with post function, there are a couple of things you can do for telling the user this took place.
1) You can display a flag
2) You can generate a fake history record
Here is a full example:
import com.atlassian.jira.component.ComponentAccessor
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import com.onresolve.scriptrunner.runner.util.UserMessageUtil
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.history.ChangeLogUtils
def apiBase = "https://XXXX.XXXX.com:443"
def apiPath = "/api/1/rest/slsched/feed/XXXXXXXXXX/projects/SXXXXXX%20xxxxx/JIRA-Access-TEST%20Task"
def httpBuilder = new HTTPBuilder(apiBase)
def response = httpBuilder.request(Method.POST, ContentType.JSON){
uri.path = apiPath
headers.Authorization = "Bearer xxxxxxxxxxxxxxxxxxxx"
body = [Request:[issueKey:issue.key, issueId:"this is passed through response"]]
response.success = { response, json ->
return json
}
response.failure = { resp, data ->
log.error " httprequest failed: $data"
return null
}
}
def flag = [type:'success',close:'auto']
def message = ""
if(response){
//here you can incorporate some data from the response into the message
message = "Succesfully posted to external api ($apiBase) with response: $response.issueId"
} else {
message = "There was an error posting data to external API. Please consult the Jira log"
flag.type = 'error'
flag.close = 'manual'
}
//use the next two line to display a flag to the user
flag.body = message
UserMessageUtil.flag(flag)
//use the following lines to create the fake issue history
//such fake issue history need to be given a "field name". That name doesn't have to exist or match an existing field
def historyFieldName = "POST RESULT" // <-change this to whatever makes sense to you
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def changeHolder = new DefaultIssueChangeHolder();
def changeItemBean = new ChangeItemBean(ChangeItemBean.CUSTOM_FIELD, historyFieldName , "", "", "", message)
changeHolder.addChangeItem(changeItemBean);
def changeGroup = ChangeLogUtils.createChangeGroup(currentUser, issue, issue, changeHolder.getChangeItems(), false);
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Peter-Dave,
Facing some error on the script, Could you check and fix them. Also IssueId has to get dynamically while post function option. Could you include the request in the code.
Attached the error screen shots for reference.
BR,
Aditya.D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
If you look at line 17
body = [Request:[issueKey:issue.key]]
The issue object is provided by the script context. You can click on the question mark below the script editor to see all the variables provided by the scriptrunner script context. So in this case, we are setting the issueKey value in the body object to the key property of the issue object.
Most of these errors you see in the script are normal. The editor attempts to determine what type each variable is and if these types are appropriate for where they are used. But since groovy is not a strictly typed language, there are some things that the editor can't confirm. So it displays an error so that the coder can review and make sure it's appropriate.
Ultimately, you have to test the execution and review the logs to see what works.
The only error that I can't confirm if it will work or not is the one on line 32 since I haven't seen what your response json will look like to be sure if "$response.issueId" is valid. You can remove the .issueId part at least to start to get the full response.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks Dave-Peter for detailed explanation. I run the code and got below error
2021-07-14 16:52:06,427 ERROR [runner.ScriptBindingsManager]: httprequest failed: [response_map:[error_list:[[message:Unknown task: SchneiderDev/projects/Siddharth%20Shankar/JIRA-Access-TEST%20Task]]], http_status_code:404]
BR,
Aditya.D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
That looks like an error from your external endpoint.
http_status_coded:404 means "not found"
Are you sure this URI is correct?
SchneiderDev/projects/Siddharth%20Shankar/JIRA-Access-TEST%20Task
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks again Peter-Dave for the response.
I was able to run the code successful. But found one bug in the code. Post call is not passing the issuekey and issueid. Could you please check?
I have code like :
body = [Request:[issueKey:issue.key, issueId:"$response.issueId"]]
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I don't know why you are attempting to use $response in the body... presumably that doesn't exist.
Are you trying to pass the id of the curret issue?
If so, it should look like this:
body = [Request:[issueKey:issue.key, issueId: issue.id as String]
What do you get when you output this?
log.info new groovy.json.JsonBuilder(body).toPrettyString()
Can you give a full example of what you expect the body to look like?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello Dave-Peter,
When i update the change , still error persists.
Here is the log :
2021-07-26 11:29:40,805 ERROR [workflow.AbstractScriptWorkflowFunction]: Workflow script has failed on issue JJIM-38 for user 'admin'. View here: https://jira-development.schneider-electric.com:8443/secure/admin/workflows/ViewWorkflowTransition.jspa?workflowMode=live&workflowName=JJIM%3A+Simplified+workflow&descriptorTab=postfunctions&workflowTransition=41&highlight=1
groovy.lang.MissingPropertyException: No such property: body for class: Script258
at Script258.run(Script258.groovy:53)
Root cause found :
We are trying to pass "issue.key" and "issue.id" along post request. Post API is calling without triggering without boby value ({"errorMessages":["The field 'key' does not support searching for EMPTY values."],"errors":{}})
Full script for quick reference :
Note : Masking some values for security reasons.
import com.atlassian.jira.component.ComponentAccessor
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import com.onresolve.scriptrunner.runner.util.UserMessageUtil
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.history.ChangeLogUtils
def apiBase = "https://elastic.xxxxxxxx.com:443"
def apiPath = "/api/1/rest/xxxxxxx/feed/xxxxxxxxx/JAMA/JIRA-JAMA_issueSync/JIRA-JAMA_issueSync_OnDemand_TrigTask"
def httpBuilder = new HTTPBuilder(apiBase)
def response = httpBuilder.request(Method.POST, ContentType.JSON){
uri.path = apiPath
headers.Authorization = "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
body = [Request:[issueKey:issue.key, issueId: issue.id as String]]
response.success = { response, json ->
return json
}
response.failure = { resp, data ->
log.error " httprequest failed: $data"
return null
}
}
def flag = [type:'success',close:'auto']
def message = ""
if(response){
//here you can incorporate some data from the response into the message
message = "Succesfully posted to external api ($apiBase) with response: $response.issueId"
println "${issue.key}"
} else {
message = "There was an error posting data to external API. Please consult the Jira log"
flag.type = 'error'
flag.close = 'manual'
}
//use the next two line to display a flag to the user
flag.body = message
UserMessageUtil.flag(flag)
//use the following lines to create the fake issue history
//such fake issue history need to be given a "field name". That name doesn't have to exist or match an existing field
def historyFieldName = "POST RESULT" // <-change this to whatever makes sense to you
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def changeHolder = new DefaultIssueChangeHolder();
def changeItemBean = new ChangeItemBean(ChangeItemBean.CUSTOM_FIELD, historyFieldName , "", "", "", message)
changeHolder.addChangeItem(changeItemBean);
def changeGroup = ChangeLogUtils.createChangeGroup(currentUser, issue, issue, changeHolder.getChangeItems(), false);
log.info new groovy.json.JsonBuilder(body).toPrettyString()
Thanks,
Aditya.D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The error you shared from your log points to the last line of the script.
This is not surprising, that line should be in the same scope where the body variable is defined.
Move that line to line #17 and try again.
Share the logs that this generates
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Peter-Dave,
Tried with below script, got the attached logs. Script run successful, but the problem remains same.
One request : Is it possible for you to join on teams call for quick resolution??
Note : Masking some value for security reasons.
import com.atlassian.jira.component.ComponentAccessor
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import com.onresolve.scriptrunner.runner.util.UserMessageUtil
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.history.ChangeLogUtils
def apiBase = "https://elastic.xxxxxxxx.com:443"
def apiPath = "/api/1/rest/xxxxxxx/feed/xxxxxxxxx/JAMA/JIRA-JAMA_issueSync/JIRA-JAMA_issueSync_OnDemand_TrigTask"
def httpBuilder = new HTTPBuilder(apiBase)
def response = httpBuilder.request(Method.POST, ContentType.JSON){
uri.path = apiPath
headers.Authorization = "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
body = [Request:[issueKey:issue.key, issueId: issue.id as String]]
log.info new groovy.json.JsonBuilder(body).toPrettyString()
response.success = { response, json ->
return json
}
response.failure = { resp, data ->
log.error " httprequest failed: $data"
return null
}
}
def flag = [type:'success',close:'auto']
def message = ""
if(response){
//here you can incorporate some data from the response into the message
message = "Succesfully posted to external api ($apiBase) with response: $response.issueId"
println "${issue.key}"
} else {
message = "There was an error posting data to external API. Please consult the Jira log"
flag.type = 'error'
flag.close = 'manual'
}
//use the next two line to display a flag to the user
flag.body = message
UserMessageUtil.flag(flag)
//use the following lines to create the fake issue history
//such fake issue history need to be given a "field name". That name doesn't have to exist or match an existing field
def historyFieldName = "POST RESULT" // <-change this to whatever makes sense to you
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def changeHolder = new DefaultIssueChangeHolder();
def changeItemBean = new ChangeItemBean(ChangeItemBean.CUSTOM_FIELD, historyFieldName , "", "", "", message)
changeHolder.addChangeItem(changeItemBean);
def changeGroup = ChangeLogUtils.createChangeGroup(currentUser, issue, issue, changeHolder.getChangeItems(), false);
Regards,
Aditya.D
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Aditya
I hope you understand that I do not work for nor do I represent Atlassian or Adaptivist. I'm simply a user of their products and I choose to volunteer some of my time to help others in the community as they helped me when I was getting started.
So, joining a team call is strongly out of the question.
That being said, I can suggest that you add some additional debugging messages so you will get a better idea of what's going on, and perhaps you can find the reason yourself.
If not, feel free to post the logs output.
Here is a full script with all the logs:
import com.atlassian.jira.component.ComponentAccessor
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
import groovyx.net.http.ContentType
import com.onresolve.scriptrunner.runner.util.UserMessageUtil
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.issue.history.ChangeLogUtils
import groovy.json.JsonBuilder
import org.apache.log4j.Level
log.setLevel(Level.DEBUG) //remove this after running and manually update your log levels in jira to keep log file managable
log.info "Starting posfunction for $issue.key"
def apiBase = "https://elastic.xxxxxxxx.com:443"
def apiPath = "/api/1/rest/xxxxxxx/feed/xxxxxxxxx/JAMA/JIRA-JAMA_issueSync/JIRA-JAMA_issueSync_OnDemand_TrigTask"
def payload = [Request:[issueKey:issue.key, issueId: issue.id as String]]
def payLoadJson = new JsonBuilder(payload).toPrettyString()
def httpBuilder = new HTTPBuilder(apiBase)
def response = httpBuilder.request(Method.POST, ContentType.JSON){
uri.path = apiPath
headers.Authorization = "Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
body = payload
response.success = { response, json ->
log.info "httprequest successful"
return json
}
response.failure = { resp, data ->
log.error " httprequest failed: $data"
return null
}
}
log.info "http request finsished"
def flag = [type:'success',close:'auto']
def message = ""
if(response){
log.info "http request had a valid response: $response"
//here you can incorporate some data from the response into the message
message = "Succesfully posted to external api ($apiBase) with response: $response.issueId"
} else {
message = "There was an error posting data to external API. Please consult the Jira log"
flag.type = 'error'
flag.close = 'manual'
}
//use the next two line to display a flag to the user
flag.body = message
UserMessageUtil.flag(flag)
log.info "creating history entry"
//use the following lines to create the fake issue history
//such fake issue history need to be given a "field name". That name doesn't have to exist or match an existing field
def historyFieldName = "POST RESULT" // <-change this to whatever makes sense to you
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def changeHolder = new DefaultIssueChangeHolder();
def changeItemBean = new ChangeItemBean(ChangeItemBean.CUSTOM_FIELD, historyFieldName , "", "", "", message as String)
log.info "built a changeItemBean: $changeItemBean"
changeHolder.addChangeItem(changeItemBean);
def changeGroup = ChangeLogUtils.createChangeGroup(currentUser, issue, issue, changeHolder.getChangeItems(), false);
log.info "changeItemBean has been submitted and changeGroup was created: $changeGroup"
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I've gone through this post, is something which more relates to my requirement. I've JIRA cloud site and Jenkins cloud with jobs are all configured parameterized jobs for different environments. Now i want to trigger build from JIRA ticket based on ticket status change. I am not able to do using send web request method on native jira cloud automation options. I dont use any adaptavist and script runner plugin. How can i achieve this automation part. If it is achieve through code via api, can you provide the steps for where we have to configure and code part.
Advance thanks for your inputs. If it works out well, it will reduce our big hectic works.
Thanks
Gnanavelu
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@gnanavelu_murthy I only have experience with calling groovy scripts from adaptavist scriptrunner and only on server/dc instance. I've never used jira cloud and don't know of any ways to trigger groovy scripts without adaptavist scriptrunner.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks for reply @Peter-Dave Sheehan
Can you provide script for want to populate multiselect dropdown list in subtask create screen the options should change based on parent ticket value of single select choice.
I tried this in subtask create transition---validators using script runner validator, but not able to achieve.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.