Just a heads up: On March 24, 2025, starting at 4:30pm CDT / 19:30 UTC, the site will be undergoing scheduled maintenance for a few hours. During this time, the site might be unavailable for a short while. Thanks for your patience.
×I am running:
Jira Server 7.10
ScriptRunner 5.4.47
JMWE 5.7.1
I am attempting to make an API call from either Scriptrunner or JMWE script consoles to populate my DNS server in a post function, both of which return the following:
2019-06-14 11:57:24,167 http-nio-8080-exec-8 url:/secure/WorkflowUIDispatcher.jspa username:esmith WARN esmith 717x46053x1 4cyrf5 (IP_ADDRESS),10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.onresolve.jira.groovy] Failed = [error:Unauthorized]
I turned on ScriptRunner debugging and get the following additional lines:
2019-06-14 11:57:24,157 http-nio-8080-exec-8 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 717x46053x1 4cyrf5 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] BeforeInstantiation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-14 11:57:24,158 http-nio-8080-exec-8 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 717x46053x1 4cyrf5 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] AfterInitialisation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
I have confirmed that I can make curl from the machine that is running Jira, and have also confirmed that the user running Jira can make a successful curl from command line
below is a copy of my code, JMWE says its fine, returns null in test. Scriptrunner barks about the API lines as being undefined but it compiles:
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import groovyx.net.http.ContentType
import static groovyx.net.http.Method.*
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.Issue;
import org.apache.log4j.Category
def Category log = Category.getInstance("com.onresolve.jira.groovy")
log.setLevel(org.apache.log4j.Level.DEBUG)
log.debug "debug statements"
def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
//Call the School URL field from the ticket
def schoolUrl_field = customFieldManager.getCustomFieldObject("customfield_12456")
// Get the value of the School URL field from schoolURL
def schoolUrl_value = issue.getCustomFieldValue(schoolUrl_field).toString()
//define CNAME entry based on School URL field first octet
def siteName = schoolUrl_value.tokenize(".")[0]
//Define domain based on the School URL field second octet
def siteDomain = schoolUrl_value.tokenize(".")[1]
//Get the value of the Shard field
def shard_field = customFieldManager.getCustomFieldObject("customfield_12454")
// Get the value of the Shard field from schoolURL
def shard = issue.getCustomFieldValue(shard_field).toString()
def http = new HTTPBuilder("http://(IP_ADDRESS):8080/api/v1/servers/localhost/zones/${siteDomain}")
def authString = "(AUTH_STRING_HERE)".getBytes().encodeBase64().toString();
http.request(PATCH, ContentType.JSON) {
requestContentType = ContentType.JSON
request.addHeader("X-API-KEY: ${authString}", "ContentType: application/json")
body = [ fields: "{\"rrsets\":\5[{\"name\": \"[siteName].\", \"type\": \"CNAME\", \"ttl\": 300, \"changetype\": \"REPLACE\", \"records\": \5[{\"content\": \"owsoo[shard].[siteDomain].com.\", \"disabled\": false}\5]}\5]}"]
response.success = { resp, JSON ->
log.warn("Successful = " + JSON)
}
response.failure = { resp, JSON ->
log.warn("Failed = " + JSON)
}
}
Any ideas why its failing?
Might be in how you prepare your request.
So I saw two possible mistakes compared to how I use the HTTPBuilder
Try this:
http.request(PATCH, ContentType.JSON) {
headers."X-API-KEY" = authString
headers."ContentType" = "application/json"
body = [
fields: [
rrsets: [
[
name: siteName,
type: "CNAME",
ttl: 300,
changetype:"REPLACE",
records: [
[
content: "${shard}.${siteDomain}.com",
disabled: false
]
]
]
]
]
]
response.success = { resp, JSON ->
log.warn("Successful = " + JSON)
}
response.failure = { resp, JSON ->
log.warn("Failed = " + JSON)
}
}
I've attempted to guess at what your data object should look like, but you may want to adjust it. Also, I split it all up on separate lines for clarity, but you can combine it all.
If you are not familiar with groovy notation for hashmap object, I'd recommend reading the documentation: http://docs.groovy-lang.org/next/html/documentation/working-with-collections.html#Collections-Maps
But in short:
def emptyMap = [:]
def mapWith2KeyValuePairs = [ key1:"value1", key2:"value2"]
def emptyList = []
def mapWithListAsValue = [ mapKeyForList: [listItem1, listItem2] ]
def mapWithListofEmptyMaps = [ mapKeyForList: [ [:],[:] ], anotherMapKey: "anotherMapValue" ]
When I run it again with your bits in there, I get the following:
2019-06-17 16:12:16,125 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] BeforeInstantiation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 16:12:16,126 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] AfterInitialisation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 16:12:16,130 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] *************************************************************************************
2019-06-17 16:12:16,131 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] Script function failed on issue: TPPI-495, actionId: 61, file: <inline script>
groovy.lang.MissingPropertyException: No such property: header for class: groovyx.net.http.HTTPBuilder$RequestConfigDelegate
Possible solutions: headers
at Script375$_run_closure1.doCall(Script375.groovy:50)
at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:432)
at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:383)
at groovyx.net.http.HTTPBuilder$request.call(Unknown Source)
at Script375.run(Script375.groovy:48)
I have attempted to define headers using the following:
headers.
header.
requestHeaders
request.addHeader
request.setHeader
request.addHeader
request.setHeader
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I replied to this yesterday, but for some reason this morning when I refreshed the page, all of my answers were gone:
When I run it again with your bits in there, I get the following:
2019-06-17 16:12:16,125 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] BeforeInstantiation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 16:12:16,126 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] AfterInitialisation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 16:12:16,130 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] *************************************************************************************
2019-06-17 16:12:16,131 http-nio-8080-exec-2 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 972x83788x1 1xj7q05 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] Script function failed on issue: TPPI-495, actionId: 61, file: <inline script>
groovy.lang.MissingPropertyException: No such property: header for class: groovyx.net.http.HTTPBuilder$RequestConfigDelegate
Possible solutions: headers
at Script375$_run_closure1.doCall(Script375.groovy:50)
at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:432)
at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:383)
at groovyx.net.http.HTTPBuilder$request.call(Unknown Source)
at Script375.run(Script375.groovy:48)
I have attempted to define headers using the following:
headers.
header.
requestHeaders
request.addHeader
request.setHeader
request.addHeader
request.setHeader
For some reason it will not let me define headers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Can you show the resulting code?
I made a typo on the second header line... it should be
headers.nameOfYourHeader = "value for header"
If the name of your header contains "-" then put the name in quotes.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I saw they typo but figured i'd leave it in just in case I was bring presumptuous. The issue was that I was not using quotes to define the header that had the dashes (-) in it. Full script is :
@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import static groovyx.net.http.Method.PATCH
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.Issue;
import org.apache.log4j.Category
def Category log = Category.getInstance("com.onresolve.jira.groovy")
log.setLevel(org.apache.log4j.Level.DEBUG)
def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
//Call the School URL field from the ticket
def schoolUrl_field = customFieldManager.getCustomFieldObject("customfield_12456")
// Get the value of the School URL field from schoolURL
def schoolUrl_value = issue.getCustomFieldValue(schoolUrl_field).toString()
//define CNAME entry based on School URL field first octet
def siteName = schoolUrl_value.tokenize(".")[0].toString()
//Define domain based on the School URL field second octet
def siteDomain = schoolUrl_value.tokenize(".")[1].toString()
//Get the value of the Shard field
def shard_field = customFieldManager.getCustomFieldObject("customfield_12454")
// Get the value of the Shard field from schoolURL
def shard = issue.getCustomFieldValue(shard_field).toString()
//Bring them together
def content = "owsoo${shard}.${siteDomain}.com.".toString()
def name = "${siteName}.${siteDomain}.com.".toString()
def http = new HTTPBuilder("http://[IP_ADDRESS][PORT]:8080/api/v1/servers/localhost/zones/[END_OF_URL]")
http.request(PATCH,ContentType.JSON) {
headers."X-API-KEY" = "[API_KEY]"
headers.contentType = "application/json"
body = [
"rrsets": [
[
"name": name,
"type": "CNAME",
"ttl": 300,
"changetype":"REPLACE",
"records": [
[
"content": content,
"disabled": false
]
]
]
]
]
response.success = { resp, JSON ->
log.warn("Successful = " + JSON)
}
response.failure = { resp, JSON ->
log.warn("Failed = " + JSON)
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Glad you got it working.
Couple things I'd like to point out ...
1) I've never had to @grab the http-builder
2) map object keys don't HAVE to be quoted ... only if they include some special characters or you want to use variables.
You could also build it this way
def rrsetsMap = [:] //initialize an empty map
rrsetsMap .name = name //add a new key.vallue pair to the map key=name with value = name
rrsetsMap .type = "CNAME" //add a new key.vallue pair to the map key=type with value = "CNAME"
rrsetsMap .ttl = 300
rrsetsMap .changetype = "REPLACE"
rrsetsMap .records = [[content:content, disabled: false]] //add a new key =records with value = array of maps
def bodyMap= [:] //initialize an empty map
bodyMap.rrsets = [] //add a new key/value pair where the value is an empty array
bodyMap.rrsets << rrsetsMap //add the rrsetMap object created above as a new element in the array
...
//later inside the http.request closure
body = bodyMap
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
1) I did the grab for version 0.7.1 because I was reading that earlier versions of HTTPBuilder dont support PATCH, just GET and POST.
2) Thanks for letting me know, I saw some examples that used quotes and some that didnt so I figured better safe than sorry.
Thanks again for the help, I dont suppose you know how to output the results to a comment? I am attempting to do the following:
def content = "owsoo${shard}.${siteDomain}.com.".toString()
def name = "${siteName}.${siteDomain}.com.".toString()
def http = new HTTPBuilder("http://[IP_ADDRESS][PORT]/api/v1/servers/localhost/zones/${siteDomain}.com")
http.request(PATCH,ContentType.JSON) {
headers."X-API-KEY" = "[API_KEY]"
headers.contentType = "application/json"
body = [
"rrsets": [
[
"name": name,
"type": "CNAME",
"ttl": 300,
"changetype":"REPLACE",
"records": [
[
"content": content,
"disabled": false
]
]
]
]
]
// response.'201' = { resp, JSON ->
// log.warn("Successful = " + JSON)}
// CommentManager.create(issue, currentUser,comment_success, true)
// response.failure = { resp, JSON ->
//log.warn("Failed = " + JSON)}
http.handler.failure = { resp, reader ->
[response:resp, reader:reader]
}
http.handler.success = { resp, reader ->
[response:resp, reader:reader]
}
}
def response = http['response']
def reader = http['reader']
//assert response.status == 201
def dig = "dig ${schoolUrl_value}".execute().text
def comment_success = "${reader.getClass()} \n ${dig}"
def comment_failure = "${reader.getClass()}"
if(response.status == 201)
CommentManager.create(issue, currentUser,comment_success, true)
else
CommentManager.create(issue, currentUser,comment_failure, true)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Most "manager" classes in Jira are accessed using the ComponentAccessor class
import com.atlassian.jira.component.ComponentAccessor
def commentManager = ComponentAcessor.getCommentManager()
//or this works too
def commentManager = ComponentAcessor.commentManager
Then, your create methods should work, just make sure you match the initial case for the manager object:
if(response.status == 201)
commentManager.create(issue, currentUser,comment_success, true)
else
commentManager.create(issue, currentUser,comment_failure, true)
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.
I agree with @PD Sheehan , seems like you are setting your headers incorrectly hence getting unauthorized
I'd try
request.setHeaders(["X-API-KEY": authString, "ContentType": "application/json"])
also, sometimes it seems easier to use RESTClient:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I get the following error when I change that line:
2019-06-17 12:45:09,430 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] BeforeInstantiation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 12:45:09,431 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] AfterInitialisation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 12:45:09,454 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] *************************************************************************************
2019-06-17 12:45:09,454 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] Script function failed on issue: TPPI-495, actionId: 61, file: <inline script>
groovy.lang.MissingMethodException: No signature of method: org.apache.http.client.methods.HttpPatch.setHeaders() is applicable for argument types: (java.util.LinkedHashMap) values: [[X-API-KEY:***HIDDEN_KEY***, ContentType:application/json]]
Possible solutions: setHeaders([Lorg.apache.http.Header;), getHeaders(java.lang.String), setHeader(org.apache.http.Header), setHeader(java.lang.String, java.lang.String), getAllHeaders(), addHeader(org.apache.http.Header)
at Script106$_run_closure1.doCall(Script106.groovy:47)
at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:432)
at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:383)
at groovyx.net.http.HTTPBuilder$request.call(Unknown Source)
at Script106.run(Script106.groovy:43)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I get the following error when I change that line:
2019-06-17 12:45:09,430 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] BeforeInstantiation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 12:45:09,431 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith DEBUG esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.j.groovy.groovyrunner.spring] AfterInitialisation [bean=com.onresolve.jira.groovy.GroovyFunctionPlugin, type=com.onresolve.jira.groovy.GroovyFunctionPlugin]
2019-06-17 12:45:09,454 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] *************************************************************************************
2019-06-17 12:45:09,454 http-nio-8080-exec-11 url:/secure/WorkflowUIDispatcher.jspa username:esmith ERROR esmith 765x61049x1 tbdcmd 10.233.231.47,10.231.220.22 /secure/WorkflowUIDispatcher.jspa [c.o.s.jira.workflow.ScriptWorkflowFunction] Script function failed on issue: TPPI-495, actionId: 61, file: <inline script>
groovy.lang.MissingMethodException: No signature of method: org.apache.http.client.methods.HttpPatch.setHeaders() is applicable for argument types: (java.util.LinkedHashMap) values: [[X-API-KEY:***HIDDEN_KEY***, ContentType:application/json]]
Possible solutions: setHeaders([Lorg.apache.http.Header;), getHeaders(java.lang.String), setHeader(org.apache.http.Header), setHeader(java.lang.String, java.lang.String), getAllHeaders(), addHeader(org.apache.http.Header)
at Script106$_run_closure1.doCall(Script106.groovy:47)
at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:432)
at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:383)
at groovyx.net.http.HTTPBuilder$request.call(Unknown Source)
at Script106.run(Script106.groovy:43)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
yeah, I got mistaken with the simpler version I linked in my first comment, looks like again, what @PD Sheehan said
you define header inside of a closure, so you can't use request variable there.
can only suggest you to check documentation on HTTPBuilder, it got plenty of examples:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Below code worked perfectly:::::
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import groovyx.net.http.ContentType
import static groovyx.net.http.Method.*
def http = new HTTPBuilder('API url')
def authString = "admin:admin".getBytes().encodeBase64().toString();
http.request(POST, ContentType.JSON) {
requestContentType = ContentType.JSON
request.addHeader("authorization: Basic ${authString}", "ContentType: application/json")
body = "{\"requirementKeys\":[\"key value\"]}"
response.success = { resp, JSON ->
log.warn("Successful = " + JSON)
}
response.failure = { resp, JSON ->
log.warn("Failed = " + JSON)
}
}
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.