We have a ticket structure in which there can be several levels of hierarchies (Initiatives, Sub-Initiatives, Epics, Tasks, Sub-Tasks). Initiatives, Sub-Initiatives and Epics are linked with "is parent of" links. Epics and Tasks can either be linked by the "is parent of" link or by the Epic link (or both).
Now, I need a simple JQL to return all the tickets in an Initiative, e.g. `issueFunction in featureFilter("key=KEY-123")`. I can achieve that by combining several "issueFunction in linkedIssuesOfRecursive" functions. However, that JQL is rather complex and the key of the Initiative has to be put in several places, so this is quite cumbersome and error prone.
I've tried to achieve the desired result with a custom JQL in script runner. For this, I modified the example in the documentation, see below. As long as I don't use any scriptrunner jql in the TEMPLATE_QUERY, this seems to work fine but it fails as soon as I start using other scriptrunner jqls. However, I need the "linkedIssuesOfRecursive".
So my questions are:
- Can I somehow use other scriptrunner jqls in a custom jql?
- Is the code for the "linkedIssuesOfRecursive" available somwhere? (Maybe I could then just re-implement it in a way that fulfills my need.)
- Of course, I'm also happy if someone would offer a different solution.
package com.onresolve.jira.groovy.jql
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.jql.query.LuceneQueryBuilder
import com.atlassian.jira.jql.query.QueryCreationContext
import com.atlassian.jira.jql.validator.NumberOfArgumentsValidator
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.util.MessageSet
import com.atlassian.query.clause.TerminalClause
import com.atlassian.query.operand.FunctionOperand
import org.apache.lucene.search.Query
import java.text.MessageFormat
class JqlAliasFunction extends AbstractScriptedJqlFunction implements JqlQueryFunction {
/**
* Modify this query as appropriate.
*
* See {@link java.text.MessageFormat} for details
*/
public static final String TEMPLATE_QUERY =
"issueFunction in linkedIssuesOfRecursive('{0}', 'is parent of') OR {0}"
/*
"issueFunction in linkedIssuesOfRecursive('key in ({0})', 'is parent of') OR issueFunction in linkedIssuesOfRecursive('issueFunction in linkedIssuesOfRecursive(\"key in ({0})\", \"is parent of\")', 'is Epic of') OR key ={0}"
*/
JqlQueryParser queryParser = ComponentAccessor.getComponent(JqlQueryParser)
LuceneQueryBuilder luceneQueryBuilder = ComponentAccessor.getComponent(LuceneQueryBuilder)
@Override
String getDescription() {
"Filter for all children (recursively) including epic links."
}
@Override
MessageSet validate(ApplicationUser user, FunctionOperand operand, TerminalClause terminalClause) {
def messageSet = new NumberOfArgumentsValidator(1, 1, getI18n()).validate(operand)
if (messageSet.hasAnyErrors()) {
return messageSet
}
def query = mergeQuery(operand)
messageSet = searchService.validateQuery(user, query)
messageSet
}
@Override
List getArguments() {
[
[
description: "query to define the feature",
optional : false,
]
]
}
@Override
String getFunctionName() {
"featureFilter"
}
@Override
Query getQuery(QueryCreationContext queryCreationContext, FunctionOperand operand, TerminalClause terminalClause) {
def query = mergeQuery(operand)
luceneQueryBuilder.createLuceneQuery(queryCreationContext, query.whereClause)
}
private com.atlassian.query.Query mergeQuery(FunctionOperand operand) {
def queryStr = MessageFormat.format(TEMPLATE_QUERY, operand.args.first())
queryParser.parseQuery(queryStr)
}
}