 |
Subscribe to this site |
|
IssueLib

Published 4 months ago
by
dimok
package jiraSRimport com.opensymphony.workflow.InvalidInputException
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.project.Project
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.issue.IssueInputParameters
import com.atlassian.jira.issue.IssueInputParametersImpl
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.util.SimpleErrorCollection
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.query.Query
import com.atlassian.jira.issue.search.SearchRequest
import com.atlassian.jira.issue.search.SearchResults
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager
import com.atlassian.jira.security.JiraAuthenticationContext
import com.atlassian.jira.issue.attachment.CreateAttachmentParamsBean
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.issue.history.ChangeItemBean
import org.apache.commons.io.FilenameUtils
import org.apache.commons.io.FileUtils
import com.atlassian.jira.util.io.InputStreamConsumer
import jiraSR.UserLib as UL
import jiraSR.FieldLib as F
import jiraSR.ProjectLib as P
import jiraSR.WorkflowLib as WL
import groovy.util.logging.Log4j
@Log4j
class IssueLib implements jiraSR.TransitionIssue, IssueFieldLib{
static{
log.setLevel(jiraSR._config.Jira.logLevel)
log.debug("IssueLib initialized\n${IssueLib.classLoader.class.properties.collect{it}.findAll{kv->kv.key in ['name','classLoader']}.join('\n')}")
}
static def cfv(Issue issue,String field,Boolean throwNotFound = true){F.cfv(issue,field,throwNotFound)}
static String cfv1(Issue issue,String field){F.cfv1(issue,field)}
/** Attachments */
interface StreamHandler<U>{U call(InputStream inputStream)}
static <T> InputStreamConsumer<T> inputStreamBuilder(StreamHandler<T> handler){
new InputStreamConsumer<T>(){
public T withInputStream(final InputStream inputStream) throws IOException,FileNotFoundException{
handler(inputStream)
}
}
}
static Collection<String> getAttachmentText(Attachment a){
Closure<Collection<String>> c = {InputStream stream ->
a.filename.eq("*.pdf")
? jiraSR.PdfLib.getAllText(stream)
: [stream.getText('UTF-8')]
}
getAttachmentFromStream(a,c)
}
static List<Map> getAttachmentExcelTable(Issue issue, Closure<List<Attachment>> filter){
def atts = issue.attachments
atts = filter(atts)
atts.collectMany{ getAttachmentFromStream(it){
jiraSR.ExcelLib.getTable(it)
}}
}
static <T> T getAttachmentFromStream(Attachment atachment,StreamHandler<T> c){
ComponentAccessor.attachmentManager.streamAttachmentContent(atachment, inputStreamBuilder(c))
}
static def saveAttachmentToFile(Attachment attachment,String path, String outFileName = ''){
assert attachment
String fileName = outFileName ?: attachment.filename
String filePath = FilenameUtils.concat(path,fileName)
assert filePath && path && fileName
log.debug([savingAttachment:fileName,to:filePath])
File targetFile = new File(filePath)
getAttachmentFromStream(attachment){stream ->
FileUtils.copyInputStreamToFile(stream, targetFile);
filePath
}
}
static ChangeItemBean createAttachment(InputStream stream,String fileName,String issue){
createAttachment(stream,fileName,getIssue(issue))
}
static ChangeItemBean createAttachment(InputStream stream,String fileName,Issue issue){
def attPath = getScriptPath()+"/temp/${fileName}"
def attFile = new File(attPath)
FileUtils.copyInputStreamToFile(stream,attFile)
CreateAttachmentParamsBean bean = new CreateAttachmentParamsBean.Builder(
attFile, fileName, "application/octet-stream", UL.ByName("ice_admin"), issue).build();
ComponentAccessor.getAttachmentManager().createAttachment(bean);
}
static Collection<Attachment> deleteAttachment(String issue, String fileName, Integer maxCount = 100000){
deleteAttachment(getIssue(issue), fileName,maxCount)
}
static Collection<Attachment> deleteAttachment(Issue issue, String fileName, Integer maxCount = 100000){
getAttachments(issue,fileName, maxCount).each{deleteAttachment(it)}
}
static void deleteAttachment(Attachment a){ComponentAccessor.getAttachmentManager().deleteAttachment(a)}
static Collection<Attachment> getAttachments(Issue issue, String fileName, Integer maxCount = 100000){
issue.attachments.findAll{
it.filename.toLowerCase() == fileName.toLowerCase()
}.take(maxCount)
}
static def copyAttachments(Issue issueSrc,Issue issueDest){
ComponentAccessor.attachmentManager.copyAttachments(issueSrc,jiraSR.UserLib.Current(),issueDest.key)
}
static def copyAttachments(Issue issueSrc,String namePattern,Issue issueDest,Boolean replace = true, Boolean move = false){
def a = issueSrc.attachments.findAll{it.filename.eq(namePattern)}
.collect{a ->
getAttachmentFromStream(a){s ->
if(replace) deleteAttachment(issueDest,a.filename)
createAttachment(s,a.filename,issueDest)
}
if(move)deleteAttachment(a)
}
if(!a) throw new RuntimeException("Issue ${issueSrc} dos not have '$namePattern' attachment(s)")
a
}
static List<Attachment> findAttachments(Issue issue, String pattern){
issue.attachments.findAll{it.filename.eq(pattern)}
}
static Collection<Issue> PromoteChildTasksByJql(String user,Issue issue,String jqlChild, Boolean success,Collection<String> directions){
SearchChildTasksByJql(user,issue,jqlChild)
.collect{task-> PromoteIssue(user,task,success,directions)}
}
static Collection<Issue> SearchChildTasksByJql(String user,Issue issue,String jqlChild){
def jqlParent="""issueFunction in subtasksOf("key = ${issue}")"""
def jql = """${jqlParent} AND ${jqlChild} """.replaceFirst(~/(?i)AND\s*AND/){String _->"AND"}
def res = SearchIssues(user,jql)
assert !res.second, "${res.second}\nJQL: {${jql}}"
return res.first
}
static Collection<Issue> getTaskParent(Issue issue){
if(!issue)return []
def tasks = linkedIssuesByType2(issue,"",true,false,false)
.findAll{it.second.issueLinkType.name in ["Task","jira_subtask_link"]}
.first
.sort{it.priority?.sequence ?: 1000}
.sort{it.resolution}
}
static Collection<Issue> getChildTasks(Issue issue){
if(!issue)return []
def tasks = linkedIssuesByType2(issue,"",false,true,false)
.findAll{it.second.issueLinkType.name in ["Task","jira_subtask_link"]}
.first
.sort{it.priority?.sequence ?: 1000}
.sort{it.resolution}
}
static Iterable<Issue> findRootDuplicates(Issue issue){
def notMe = issue.key ? "AND key != "+issue.key : ""
def pkey = issue.projectObject.key
def ist = issue.issueType.name
def root = issue.rootNumber
def jql = """project = $pkey AND issuetype = '$ist' AND "Root Number" ~ $root $notMe AND resolution = Unresolved """
searchIssues(jql).assert('it.second'){!it.second}.first
}
/// With maxResults
/**
* With maxResults
*
* @param user
* @param maxResults
* @param jql
* @param Collection<Issue> page => doSomething(page)
* @return The native cell
*/
static String SearchIssues(String user,Integer maxRes,String jql,Closure onPage){
SearchIssues(jiraSR.UserLib.ByName(user),maxRes,jql,onPage)
}
static String SearchIssues(ApplicationUser user,Integer maxRes,String jql,Closure onPage){
def count = 0
SearchIssues(user,jql,{Collection<Issue> page->
onPage(page)
count += page.size()
count >= maxRes
})
}
/// String user, no pageSize
static String SearchIssues(String user, String jqlSearch, Closure<Boolean> onPage){
SearchIssues(jiraSR.UserLib.ByName(user),jqlSearch,onPage)
}
/// Default pageSize
static String SearchIssues(ApplicationUser user, String jqlSearch, Closure<Boolean> onPage){
SearchIssues(user,jqlSearch,5000,onPage)
}
/// String user
static String SearchIssues(String user, String jqlSearch,Integer pageSize, Closure<Boolean> onPage){
SearchIssues(jiraSR.UserLib.ByName(user),jqlSearch,pageSize, onPage)
}
/// Full
static String SearchIssues(ApplicationUser user, String jqlSearch,Integer pageSize, Closure<Boolean> onPage){
SearchService searchService = ComponentAccessor.getComponent(SearchService.class)
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)
if( !parseResult.isValid())return parseResult.errors.toString()// + parseResult.errors.collect{ it.toString() }
def query = parseResult.getQuery()
PagerFilter pager = new PagerFilter(pageSize)
SearchResults results = searchService.search(user, query, pager)
if(onPage(results.results))return ""
def pageCount = Math.ceil((Double)(results.total / pageSize)) as Integer
def pageRange = 1..pageCount//;return pageRange.collect{it}
Collection<PagerFilter> pageFilters = pageRange.collect{ new PagerFilter(it*pageSize,pageSize) }
pageFilters.find{ onPage(searchService.search(user, query, it).results) }
}
static Tuple2<Collection<Issue>,String> SearchIssues(String user, String jqlSearch){
SearchIssues(jiraSR.UserLib.ByName(user),jqlSearch)
}
static Tuple2<Collection<Issue>,String> searchIssues(String jqlSearch, Boolean asCurrentUser = false){
SearchIssues(asCurrentUser ? jiraSR.UserLib.Current() : jiraSR.UserLib.ByName(getApi_admin()), jqlSearch)
}
static Tuple2<Collection<Issue>,String> searchIssues(String project, String issueType, String jqlSearch,Boolean escape = true){
def jql = """ project = "${project}" AND issuetype = "${issueType}" AND """+jqlSearch
SearchIssues(jiraSR.UserLib.ByName(getApi_admin()),escape ? escapeJql(jql) : jql)
}
static Tuple2<Collection<Issue>,String> searchIssues(String project, String jqlSearch,Boolean escape = true){
def jql = """ project = "${project}" AND """+jqlSearch
SearchIssues(jiraSR.UserLib.ByName(getApi_admin()),escape ? escapeJql(jql) : jql)
}
static Tuple2<Collection<Issue>,String> SearchIssues(ApplicationUser user, String jqlSearch){
//log.debug([jqlSearch:jqlSearch])
SearchService searchService = ComponentAccessor.getComponent(SearchService.class)
SearchService.ParseResult parseResult = searchService.parseQuery(user, jqlSearch)
if( !parseResult.isValid())return new Tuple2([], parseResult.errors.toString()+(parseResult.query ?: jqlSearch))// + parseResult.errors.collect{ it.toString() }
def jqlQuery = parseResult.getQuery()
//log.debug([jqlQuery:jqlQuery])
return new Tuple2(searchService
.search(user, parseResult.getQuery(), PagerFilter.getUnlimitedFilter())
.results.collect{ ComponentAccessor.issueManager.getIssueObject(it.id)},"")
}
static Tuple2<Collection<Issue>,String> searchFilter(String filterName,Boolean throwNotFound = true){
def request = UserLib.getFilter(filterName,throwNotFound)
searchIssues(getApi_user(),request)
}
static Tuple2<Collection<Issue>,String> searchIssues(SearchRequest request){searchIssues(getApi_user(),request)}
static Tuple2<Collection<Issue>,String> searchIssues(ApplicationUser user, SearchRequest request){
Query query = request.query
//log.debug([query:query])
SearchService searchService = ComponentAccessor.getComponent(SearchService.class)
return new Tuple2(searchService
.search(user, query, PagerFilter.getUnlimitedFilter())
.results.collect{ ComponentAccessor.issueManager.getIssueObject(it.id)},"")
}
static List<Issue> findIssue(String project,String issueType, String field, String value, Closure<Boolean> filter = {true}){
findIssue("project='$project' AND issueType='$issueType'", field,value,filter)
}
static List<Issue> findIssue(String subquery, String field, String value, Closure<Boolean> filter = {true}){
def jql = subquery + " AND " + buildExactJql(field, value)
//log.error([findIssue:[jql:jql]])
def search = searchIssues(jql)
if(search.second)throw new Exception(search.second)
search.first
.findAll{Issue issue -> getIssueValue(issue,field).eq(value)}
.findAll(filter)
}
static Issue findIssueByFilter(String filter,Issue issue){
def s = searchIssues("filter = '$filter' AND key = $issue.key", true)
if(s.second) throw new RuntimeException(s.second)
return s.first.find()
}
public static Collection<String> deleteIssue(String userName,String ticket) {
DeleteIssue(userName,ticket)
}
public static Collection<String> DeleteIssue(String userName,String ticket) {
DeleteIssue(userName,GetIssue(ticket))
}
public static Collection<String> deleteIssue(String userName,Issue issue) {
DeleteIssue(userName,issue)
}
public static Collection<String> DeleteIssue(String userName,Issue issue) {
DeleteIssue(jiraSR.UserLib.ByName(userName),issue)
}
public static Collection<String> deleteIssue(Issue issue) {
DeleteIssue(jiraSR.UserLib.ByName(getApi_admin()),issue)
}
public static Collection<String> deleteIssue(ApplicationUser user,Issue issue) {
DeleteIssue(user,issue)
}
public static Collection<String> DeleteIssue(ApplicationUser user,Issue issue) {
assert user
assert issue
def issueService = ComponentAccessor.issueService
def createValidationResult = issueService.validateDelete(user,issue.getId())
def res = [[user:user.name,issue:issue].toString()]
if (createValidationResult.isValid()){
def errorCollection = issueService.delete(user, createValidationResult);
res += errorCollection.errorMessages + errorCollection.errors.collect{ it.toString() }
}else
res += createValidationResult.errorCollection.errorMessages + createValidationResult.errorCollection.errors.collect{ it.toString() }
return res.size() > 1 ? res : res.take(0)
}
static Issue updateAssignee(Issue issue,String assignee, Boolean fireEvent = true) {
updateAssignee(getApi_admin(),issue,assignee,fireEvent)
}
static Issue updateAssignee(String user,Issue issue,String assignee, Boolean fireEvent = true) {
updateAssignee(jiraSR.UserLib.ByName(user),issue,jiraSR.UserLib.ByName(assignee),fireEvent)
}
static Issue updateAssignee(ApplicationUser user,Issue issue,ApplicationUser assignee, Boolean fireEvent = true) {
(issue as MutableIssue).assignee = assignee
def issueManager = ComponentAccessor.issueManager
issueManager.updateIssue(user, issue as MutableIssue, EventDispatchOption.ISSUE_UPDATED, fireEvent)
return issue
}
static Issue updateDueDate(String user,Issue issue,Date date, Boolean fireEvent = true) {
updateDueDate(jiraSR.UserLib.ByName(user),issue,date,fireEvent)
}
static Issue updateDueDate(ApplicationUser user,Issue issue,Date date, Boolean fireEvent = true) {
(issue as MutableIssue).dueDate = new java.sql.Timestamp(date.time)
return UpdateIssue(user,issue,fireEvent)
}
static Issue updateReporter(String user,String issue,String reporter, Boolean fireEvent = true) {
updateReporter(UL.ByName(user),getIssue(issue),UL.ByName(reporter),fireEvent)
}
static Issue updateReporter(ApplicationUser user,Issue issue,ApplicationUser reporter, Boolean fireEvent = true) {
(issue as MutableIssue).reporter = reporter
def issueManager = ComponentAccessor.issueManager
issueManager.updateIssue(user, (MutableIssue)issue, EventDispatchOption.ISSUE_UPDATED, fireEvent)
return issue
}
static Issue updateDescription(Issue issue,String description, Boolean fireEvent = true) {
updateDescription(UL.ByName(getApi_admin()),issue,description,fireEvent)
}
static Issue updateDescription(ApplicationUser user,Issue issue,String description, Boolean fireEvent = true) {
(issue as MutableIssue).description = description
def issueManager = ComponentAccessor.issueManager
issueManager.updateIssue(user, (MutableIssue)issue, EventDispatchOption.ISSUE_UPDATED, fireEvent)
return issue.key.issue
}
static Issue updateIssue(Issue issue, Boolean fireEvent = true) {
ApplicationUser user = jiraSR.UserLib.ByName(getApi_admin())
def issueManager = ComponentAccessor.issueManager
issueManager.updateIssue(user, (MutableIssue)issue, EventDispatchOption.ISSUE_UPDATED, fireEvent)
return issue.key.issue
}
public static Issue GetIssue(String key){ComponentAccessor.issueManager.getIssueObject(key.toUpperCase())}
public static Issue GetIssue(Issue issue){getIssue(issue.toString())}
public static String ShowIssue(String ticket){ ShowIssue(GetIssue(ticket))}
public static String ShowIssue(Issue issue){"$issue[$issue.status.name]"}
public static CustomField GetCustomField(String name){
def cf = ComponentAccessor.customFieldManager.getCustomFieldObjectByName(name)
if(cf == null)throw new Exception("Custom field ${name} not found")
return cf
}
static Collection<CustomField> GetCustomFields(Long projectId,String issueType, String name){
def res = ComponentAccessor.customFieldManager.getCustomFieldObjects(projectId,issueType).
findAll{it.fieldName.toLowerCase() == name.toLowerCase()}
assert res.size() < 2
return res
}
public static List<IssueLink> IssueLinksByType(Issue issue,String linkType,Boolean inward,Boolean outward){
def issueLinkManager = ComponentAccessor.issueLinkManager
List<IssueLink> links =
(
issueLinkManager.getInwardLinks(issue.id).findAll{inward}
+issueLinkManager.getOutwardLinks(issue.id).findAll{outward}
).findAll{ !linkType || it.issueLinkType.name.toLowerCase() == linkType.toLowerCase() }
return links
}
public static List<Issue> LinkedIssuesByType(Issue issue,String linkType,Boolean inward,Boolean outward, Boolean openOnly){
def issueLinkManager = ComponentAccessor.issueLinkManager
def links = IssueLinksByType(issue,linkType,inward,outward)
def issues = links
.collectMany{ [it.sourceObject,it.destinationObject] }
//.unique{ it.id}
.findAll{ it.id != issue.id}
return openOnly? issues.findAll{it.resolution== null} : issues
}
static List<Tuple2<Issue,IssueLink>> linkedIssues(Issue issue){
linkedIssuesByType2(issue,"",true,true,false)
}
static List<Tuple2<Issue,IssueLink>> linkedIssues(Issue issue,Boolean inward,Boolean outward, Boolean openOnly){
linkedIssuesByType2(issue,"",inward,outward,openOnly)
}
static List<Tuple2<Issue,IssueLink>> linkedIssuesByType2(Issue issue,String linkType,Boolean inward,Boolean outward, Boolean openOnly){
def issueLinkManager = ComponentAccessor.issueLinkManager
def links = IssueLinksByType(issue,linkType,inward,outward)
def issues = links
.collectMany{ [new Tuple2(it.sourceObject,it),new Tuple2(it.destinationObject,it)] }
//.unique{ it.first.id}
.findAll{ it.first.id != issue.id}
return openOnly? issues.findAll{it.first.resolution== null} : issues
}
static List<Tuple2<Issue,IssueLink>> isBlockedBy(Issue issue){
linkedIssuesByType2(issue,"",true,false,true).findAll{it.second.issueLinkType.name == "Blocks"}
}
static Collection<Issue> issuesBlockedBy(Issue issue){
LinkedIssuesByType(issue,"Blocks",false,true,true)
}
static Collection<Issue[]> getLinkedTree(Issue issue){
def li = {Issue i -> linkedIssuesByType2(i,"",true,true,false).findAll{i.parentId !=it.second.sourceId && it.second.issueLinkType.name != 'Epic-Story Link'}.collect{[it.first,i]} as List<Issue[]>}
def liNew = {List<Issue> c -> c.collectMany(li).findAll{!c.contains(it[0])|| it[0]==issue}.findAll{it[0].issueType.name != 'Epic'}.groupBy{it[0].id}.values().collectMany{it}}
def lis = {List<Issue> c ->
def newList = liNew(c)
if(!newList)return false
c.addAll(0, newList)
}
List<Issue[]> links = li(issue)
links.addAll(0,[[issue,issue]])
5.times{links.addAll(links.size(),liNew(links.collect{it[0]}))}
return links.drop(1).unique{it[0]}
}
static Iterable<Issue> getAnyParents(Issue issue,int level = 5){
def epic = issue.cfv("Epic Link",false)
def parents = [epic,*getTaskParents(issue)].findAll()
def hasEpic = parents.any{it.issueType.name == 'Epic'}
if(hasEpic || !level || !parents)return parents
def grands = parents.collectMany{getAnyParents(it,level-1)}
parents + grands
}
static Iterable<Issue> getTaskParents(Issue issue){
def links = linkedIssuesByType2(issue,"Task",true,false,false)*.first
(links << issue.parentObject).findAll()
}
static Issue getParentEpic(Issue issue){[issue,*getAnyParents(issue)].find{it.epic}}
static void setIssueInputParameters(Issue parent
,IssueInputParameters ip
,String projectKey
,String issueType
,Map<String,Object> fields){
try{
def temp = ComponentAccessor.issueFactory.getIssue()
def isProp = {String key-> temp.hasProperty(key)}
def props = fields.findAll{ isProp(it.key)}
Closure<Object> toString = {
it instanceof java.sql.Date
? it.format(getDate_format())
: it instanceof Iterable
? it
:getJiraValue(it)?.toString()}
def customFields = fields.findAll{!isProp(it.key)}
.collectMany{F.getCustomFieldValueInternalByProject2(projectKey,issueType,it.key,toString(it.value))}
customFields.findAll{it.second}
.each{ip.addCustomFieldValue(it.first,it.second.toString())}
customFields.findAll{parent && !it.second}.each{
String fv = F.getCustomFieldValueInternal(it.first,parent)
ip.addCustomFieldValue(it.first,fv)
}
props.each{ ip.properties.actionParameters[it.key]= [it.value ?: valueAsString(parent,it.key)] as String[]}
if(fields.containsKey("dueDate") ){
def dd = fields["dueDate"] ?: parent?.dueDate?.format(getDate_format())
if(dd)ip.setDueDate(dd.toString())
}
if(fields.containsKey("components") ){
def pcm = ComponentAccessor.projectComponentManager
def dd0 = (fields["components"] ?: '')
.trim().split(",").findAll()
.collect{[it,P.getComponent(projectKey, it)]}
.each{
if(!it[1] && !parent) throw new Exception("Component [${it[0]}] does not exist in project $projectKey")
}
.collect{it[1].id}
Long[] dd = dd0 ?: parent?.components?.id
if(dd)ip.setComponentIds(dd)
}
}catch(Exception exc){
throw new Exception(fields.toString(),exc)
}
//assert !ip.properties.actionParameters
}
public static Tuple2<MutableIssue,Collection<String>> CreateMutableIssue(String userName,String projectKey, String issueTypeName, Map<String,String> fields) {
CreateMutableIssue(userName,userName, projectKey, issueTypeName, fields)
}
public static Tuple2<MutableIssue,Collection<String>> CreateMutableIssue(String userReporter,String userAssignee,String projectKey, String issueTypeName, Map<String,String> fields) {
CreateMutableIssue("ice_admin",userReporter,userAssignee,projectKey,issueTypeName,fields)
}
public static Tuple2<MutableIssue,Collection<String>> CreateMutableIssue(String userCreator,String userReporter,String userAssignee,String projectKey, String issueTypeName, Map<String,Object> fields) {
ApplicationUser user = jiraSR.UserLib.ByName(userCreator)
assert user, userCreator
ApplicationUser reporter = jiraSR.UserLib.ByName(userReporter)
assert reporter, userReporter
ApplicationUser assignee = jiraSR.UserLib.ByName(userAssignee)
assert assignee, userAssignee
def project = jiraSR.ProjectLib.ByKey(projectKey)
assert project, projectKey
String itid = jiraSR.ProjectLib.IssueType(project,issueTypeName)?.id
assert itid, [projectKey:projectKey,issueTypeName:issueTypeName]
def issueService = ComponentAccessor.issueService
def ip = issueService.newIssueInputParameters()
ip.setProjectId(project.id)
.setIssueTypeId(itid)
.setSummary(issueTypeName)
.setReporterId(userReporter)
.setAssigneeId(userAssignee)
ip.setApplyDefaultValuesWhenParameterNotProvided(true)
setIssueInputParameters(null,ip,projectKey,issueTypeName,fields)
def context = [[userCreator:user.name,userReporter:userReporter,userAssignee:userAssignee,projectKey:projectKey, issueTypeName:issueTypeName]]
def createValidationResult = issueService.validateCreate(user, ip);
if (createValidationResult.isValid()){
def createResult = issueService.create(reporter, createValidationResult);
if (!createResult.isValid())
return new Tuple2(null,context + createResult.errorCollection.errorMessages + createResult.errorCollection.errors.collect{ it.toString() })
return new Tuple2(createResult.issue,[])
}else
return new Tuple2(null,context + createValidationResult.errorCollection.errorMessages + createValidationResult.errorCollection.errors.collect{ it.toString() })
}
public static Collection<String> updateIssue(Issue issue,Issue source, Collection<String> fields) {
updateIssue(issue,fields.collectEntries{[(it):F.cfv1(source,it)]})
}
public static Collection<String> updateIssue(Issue issue,Map<String,String> fields) {
updateIssue(getApi_admin(),issue,fields)
}
public static Collection<String> updateIssue(String userName,Issue issue,Map<String,String> fields) {
ApplicationUser user = jiraSR.UserLib.ByName(userName)
assert user, userName
def project = issue.projectObject
def issueService = ComponentAccessor.issueService
def ip = issueService.newIssueInputParameters()
ip.setSkipScreenCheck(true)
setIssueInputParameters(issue,ip,project.key,issue.issueType.name,fields)
def updateValidationResult = issueService.validateUpdate(user, issue.id, ip);
if (updateValidationResult.isValid())
{
def updateResult = issueService.update(user, updateValidationResult);
if (!updateResult.isValid())
{
return updateResult.errorCollection.errorMessages + updateResult.errorCollection.errors.collect{ it.toString() }
}
}else
return updateValidationResult.errorCollection.errorMessages + updateValidationResult.errorCollection.errors.collect{ it.toString() }
}
public static Issue createIssueSure(String projectKey,String issueTypeName,Map fields) {
def t = createIssue(projectKey,issueTypeName,fields)
if(t.second)throw new RuntimeException(t.second.join("\n"))
t.first
}
public static Tuple2<Issue,Collection<String>> createIssue(String projectKey,String issueTypeName,Map fields) {
createIssue(null,'',projectKey,issueTypeName,fields)
}
public static Tuple2<Issue,Collection<String>> createIssue(
Issue parent
,String userCreator
,String projectKey
,String issueTypeName
,Map fields
,Boolean throwError = false) {
CreateIssue(parent,userCreator,projectKey,issueTypeName,fields,throwError)
}
/**
Create issue or sub-task
@param parent null to create an issue. Existing issue to create a sub-task.
@param projectKey Is ignored if parent provided
@param fields Can be property (summary, reporter etc) or custom field. If value is "" the value will be taken from parent (if provided)
*/
public static Tuple2<Issue,Collection<String>> CreateIssue(
Issue parent
,String userCreator
,String projectKey
,String issueTypeName
,Map fields
,Boolean throwError = false) {
ApplicationUser user = jiraSR.UserLib.ByName(userCreator ?: getApi_admin())
assert user, userCreator
assert parent || projectKey
def project = projectKey ? jiraSR.ProjectLib.ByKey(projectKey) : parent?.projectObject
def issueType = jiraSR.ProjectLib.IssueType(project,issueTypeName)
String itid = jiraSR.ProjectLib.IssueType(project,issueTypeName)?.id
assert itid, [projectKey:project.key,issueTypeName:issueTypeName]
def isSubTask = parent && issueType.subTask
def issueService = ComponentAccessor.issueService
def ip = issueService.newIssueInputParameters()
ip.setProjectId(project.id)
.setIssueTypeId(itid)
.setSummary(issueTypeName)
ip.setApplyDefaultValuesWhenParameterNotProvided(true)
ip.setSkipScreenCheck(true)
setIssueInputParameters(parent,ip,project.key,issueTypeName,fields)
def context = [userCreator:user.name,projectKey:project.key, issueTypeName:issueTypeName]
JiraAuthenticationContext authContext = ComponentAccessor.jiraAuthenticationContext
def loggedInUser = authContext.loggedInUser
authContext.setLoggedInUser(user)
def createValidationResult = isSubTask
? issueService.validateSubTaskCreate(user,parent.id,ip)
: issueService.validateCreate(user, ip);
try{
if (createValidationResult.isValid()){
def createResult = issueService.create(user, createValidationResult);
if (!createResult.isValid()){
String m=context + [execution:createResult.errorCollection.errorMessages + createResult.errorCollection.errors.collect{ it.toString() }]
if(throwError) throw new Exception(m);
return new Tuple2(null,[m])
}
if(isSubTask)
ComponentAccessor.subTaskManager.createSubTaskIssueLink(parent, createResult.issue, user);
return new Tuple2(createResult.issue,[])
}else{
String m = context + [validation:createValidationResult.errorCollection.errorMessages + createValidationResult.errorCollection.errors.collect{ it.toString() }]
if(throwError) throw new Exception(m);
return new Tuple2(null,[m])
}
}finally{
authContext.setLoggedInUser(loggedInUser)
}
}
static Tuple2<Issue,Collection<String>> CreateSubTask(String user,Issue parentIssue,String subTaskName,Map<String,String> fields){
CreateSubTask(jiraSR.UserLib.ByName(user),parentIssue,subTaskName,fields)
}
static Tuple2<Issue,Collection<String>> CreateSubTask(ApplicationUser user,Issue parentIssue,String subTaskName,Map<String,String> fields){
def issueService = ComponentAccessor.getIssueService();
def issueInputParameters = issueService.newIssueInputParameters();
def subTaskManager = ComponentAccessor.getSubTaskManager()
def projectId = parentIssue.projectId
def issueType = parentIssue.projectObject.issueTypes.find{ it.isSubTask() && it.name.toLowerCase() == subTaskName.toLowerCase()}
def isProp = {String key-> parentIssue.hasProperty(key)}
def props = fields.findAll{ isProp(it.key)}
def customFields = fields.findAll{!isProp(it.key)}
.collect{ [fieldName:it.key,fieldValue:it.value.toString()
,customFieldId:GetCustomFields(projectId,issueType.name,it.key)[0]?.id]}
def missingField = customFields.find{!it.customFieldId}
assert !missingField
assert issueType, [subTaskName:subTaskName,error:"Not Found"]
issueInputParameters
.setProjectId(projectId)
.setIssueTypeId(issueType.id)
//.setReporterId(reporter)
//.setAssigneeId(assignee)
//.setSummary(summary)
issueInputParameters.setApplyDefaultValuesWhenParameterNotProvided(true)
props.each{ issueInputParameters.properties.actionParameters[it.key]= [it.value] as String[]}
customFields.each{issueInputParameters.addCustomFieldValue(it.customFieldId,it.fieldValue)}
//return issueInputParameters.properties
def parentIssueId = parentIssue.id
IssueService.CreateValidationResult createValidationResult =
issueService.validateSubTaskCreate(user, parentIssueId, issueInputParameters);
if (!createValidationResult.isValid())
return new Tuple2(null,createValidationResult.errorCollection.errorMessages + createValidationResult.errorCollection.errors.collect{ it.toString() })
IssueService.IssueResult createResult = issueService.create(user, createValidationResult);
if (!createResult.isValid())
return new Tuple2(null,createResult.errorCollection.errorMessages + createResult.errorCollection.errors.collect{ it.toString() })
subTaskManager.createSubTaskIssueLink(parentIssue, createResult.getIssue(), user);
return new Tuple2(createResult.issue,[])
}
static void CleanLastComments(Issue issue,Integer skip, String search = null){
def commentManager = ComponentAccessor.commentManager
List<Comment> comments = commentManager.getComments(issue)
comments
.take(skip<0?comments.size()+skip:0)
.drop(skip>=0?skip:0)
.each {comment ->
if (!search || comment.body.contains(search)) {
commentManager.delete(comment)
}
}
}
static def getIssueValue(Issue issue,String valueName, boolean throwNotFound = true){
if(issue.hasProperty(valueName))return getJiraValue(issue[valueName])
jiraSR.FieldLib.cfv1(issue,valueName,throwNotFound)
}
static String getStepAssignee(Issue issue,Integer actionId){
def stepTo = getActionDestinationStep(issue,actionId).name
getStepToAssignee(issue,stepTo)
}
static String getStepAssignee(Issue issue){
def step = issue.status?.name
step ? getStepAssignee(issue,step) : ''
}
static String getStepAssignee(Issue issue,String step){
def ass = WL.getStepAssignees(issue.projectObject)
.findAll{issue.issueType.name.eq(it.issueType) && it.step.eq(step)}
.collect{it.assigneeName}
.find{!it.eq('Assigned (Business)')}
}
static Issue startWatching(Issue issue, String userName){startWatching(issue,jiraSR.UserLib.ByName(userName))}
static Issue startWatching(Issue issue, ApplicationUser user){ComponentAccessor.watcherManager.startWatching(user, issue)}
/******** the end *********/
}
// https://docs.atlassian.com/software/jira/docs/api/7.2.1/com/atlassian/jira/issue/package-summary.html
// https://library.adaptavist.com/entity/create-a-sub-task-and-link-to-parent-issue-in-jira
// https://developer.atlassian.com/server/jira/platform/performing-issue-operations/