我正在尝试使用Script Runner添加自定义JIRA REST端点。
REST端点需要设置用户属性:提供用户列表,并为每个用户提供用户属性列表。
这是我到目前为止拼凑的剧本:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.bc.user.search.UserSearchService
import com.atlassian.sal.api.user.UserManager
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import groovy.transform.BaseScript
import javax.servlet.http.HttpServletRequest
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
@BaseScript CustomEndpointDelegate delegate
setUserProperties(httpMethod: "POST", groups: ["jira-administrators"])
{ MultivaluedMap queryParams, String body, HttpServletRequest request ->
def userPropertyManager = ComponentAccessor.getUserPropertyManager()
def userManager = ComponentAccessor.getUserManager()
def userSearchService = ComponentAccessor.getComponent(UserSearchService.class)
def json = new JsonSlurper().parseText(body)
def propertyCount = 0
def customerList = new ArrayList<>()
json.each {
aCustomer ->
def user = userManager.getUserByName(aCustomer.name.toString())
aCustomer.properties.each {
aProperty ->
userPropertyManager.getPropertySet(user).setString("jira.meta." + aProperty.key.toString(), aProperty.value.toString())
propertyCount++
}
customerList.add(user)
}
def response = new ArrayList<>()
response.add( new JsonSlurper().parseText('{ "properties_changed": ' + propertyCount + ' }') )
response.add( customerList )
return Response.ok( new JsonBuilder(response).toPrettyString() ).build();
}
内联脚本编辑器抱怨这一行: def user = userManager.getUserByName(aCustomer.name.toString())
,更确切地说,aCustomer.name
下面有一条红色波浪线,错误是
[Static type checking] - No such property: name for class:
java.lang.object
@ line 28, column 44.
击> <击> 撞击>
编辑:根据@ GlennV的回答,这并不重要,可以忽略。
此脚本的先前(工作)迭代,在另一台服务器上运行,使用电子邮件地址,请参阅How to get a user by email in JIRA Script Runner。但是,如上所述,现在我不是通过电子邮件地址而是通过用户名获取用户。
这是我用来测试的userprops.json
文件:
[
{
"name": "felicity.smoak",
"email": "felicity.smoak@queenconsolidated.com",
"properties": {
"Company": "Queen Consolidated",
"Foo": "bar"
}
},
{
"name": "johnny.cash",
"email": "johnny.cash@nashville.com",
"properties": {
"Company": "Nashville",
"Nickname": "The Man In Black"
}
}
]
我使用这个curl
命令进行测试:
curl -X POST \
-i \
-H "X-Atlassian-Token: nocheck" \
-H "Content-type: text/json" \
--data "@userprops.json" \
-u "$JIRA_USER":"$JIRA_PASSWORD" \
http://"$JIRA_SERVER"/rest/scriptrunner/latest/custom/setUserProperties
$JIRA_USER
,$JIRA_PASSWORD
和$JIRA_SERVER
只是我用来为Stack Overflow匿名化的占位符。实际值是正确的。
这是输出:
HTTP/1.1 404 Not Found
Server: nginx
Date: Wed, 07 Sep 2016 14:55:38 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 0
Connection: keep-alive
X-AREQUESTID: 0123456789
X-ASEN: SEN-123456789
Set-Cookie: JSESSIONID=1A2B3C4D5E6F1A2B3C4D5E6F1A2B3C4D5E6F; Path=/; Secure; HttpOnly
X-Seraph-LoginReason: OK
Set-Cookie: atlassian.xsrf.token=ABCD-EFGH-IJKL-MNOP|abcdef0123456789abcdef0123456789abcdef0123456789|lin; Path=/; Secure
X-ASESSIONID: xyz123
X-AUSERNAME: amedee.vangasse
Cache-Control: no-cache, no-store, no-transform
X-Content-Type-Options: nosniff
所以我找不到根据Scriptrunner documentation找到它的REST端点。
如果我使用任何我知道的网址是错误的,那么我会得到一个预期的“哎呀,你找到了一个死链接。”错误页面。只有使用正确的URL我才能得到404 +没有其他输出。
此外,当我添加doSomething
示例并尝试获取它时,我得到404和内容长度为0.对任何其他错误的URL执行GET会得到404和内容长度为很少KiB。
-Dplugin.script.roots=
参数未在setenv.sh
中定义,也不是,因为脚本是内联的。只有文件系统上的脚本才需要设置脚本根,并且仅适用于默认scripts
目录之外的脚本,如Scriptrunner documentation中所述。
还在Atlassian Answers上询问。
编辑:我按照Glenn的建议将customerList.add(user)
向下移了一行,我在atlassian-jira.log
中收到了此错误:
2016-09-09 11:01:43,683 http-nio-8086-exec-23 ERROR amedee.vangasse 661x42160x1 qq01uq 213.224.9.178,127.0.0.1 /rest/scriptrunner/latest/custom/customadmin/com.onresolve.scriptrunner.canned.common.rest.CustomRestEndpoint [c.a.p.r.c.error.jersey.ThrowableExceptionMapper] Uncaught exception thrown by REST service: Cannot get property 'name' on null object
java.lang.NullPointerException: Cannot get property 'name' on null object
at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:60)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:172)
at org.codehaus.groovy.runtime.callsite.NullCallSite.getProperty(NullCallSite.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:296)
at com.onresolve.scriptrunner.runner.RestEndpointManager$_onFullSystemStart_closure5.doCall(RestEndpointManager.groovy:141)
... 3 filtered
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1021)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:442)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2030)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2015)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2056)
at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at com.onresolve.scriptrunner.runner.RestEndpointManager.onFullSystemStart(RestEndpointManager.groovy:131)
at com.onresolve.scriptrunner.runner.rest.common.UserCustomScriptEndpoint.refresh(UserCustomScriptEndpoint.groovy:364)
at com.onresolve.scriptrunner.runner.rest.common.UserCustomScriptEndpoint.saveConfiguredItems(UserCustomScriptEndpoint.groovy:359)
at com.onresolve.scriptrunner.runner.rest.common.UserCustomScriptEndpoint.addListener(UserCustomScriptEndpoint.groovy:127)
... 3 filtered
at java.lang.reflect.Method.invoke(Method.java:497)
at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
... 13 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter$JerseyOsgiServletContainer.doFilter(RestDelegatingServletFilter.java:154)
... 1 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:68)
... 86 filtered
at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:70)
... 16 filtered
at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:37)
... 73 filtered
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
这也不是nginx代理的问题,因为当我进入服务器并在localhost上curl
时,我得到相同的404。
答案 0 :(得分:2)
Script Runner使用Groovy的CompileStatic提供有关编译错误的提示。当有足够的类型信息时,这可以正常工作。
在上面的代码中,您的 json 实例不包含有关其包含的元素的任何类型信息,因此Groovy(在编译时)不知道 aCustomer 实例有一个名称字段,因此它会对此发出警告。但是,您的代码没有任何问题,因此如果您向REST资源发送请求,它应该可以正常工作。
如果您的请求返回的状态代码不是2xx,则可能还有其他错误。
关于404.这意味着 Not Found ,因此您发送请求的URL中存在错误,或者您的REST脚本无法正确用于Script Runner。我会先检查$ JIRA_SERVER的值。如果您将其粘贴到浏览器中,则应正确加载JIRA。如果可以,那么我将检查Script Runner是否知道需要查找脚本的路径。是否在setenv.sh(或setenv.bat)中定义了 -Dplugin.script.roots = 参数?有关此内容的更多详细信息,请here。