Hello all,
I need inspiration. I'm looking for a JQL query (including ScriptRunner). We're currently developing several components which each have their own versioning. So each ticket that modifies a component gets a fixVersion like "compA_1.0". If two components are touched, it's "compA_1.0, compB_2.4". So far, so good.
Now we also have "Delivery"-type tickets which contain all fixVersions of all items belonging to a rollout as their own fixVersion: ""compA_1.0, compB_2.4, compC_3.0".
Now I know how to query for all tickets belonging to a certain fixVersion, but I don't want to handle that list of releases each time I run the query. Is there any way to get
"all the tickets which have a fixVersion that is in the fixVersions of the Delivery ticket with id=ABC"?
Thanks in advance
No, there are no JQL that can do this out of the box (including with scriptrunner).
If somehow there was an actual link between the delivery ticket and the fix tickets, then you could use that.
The JQL you are looking for would require a custom JQL function.
Here is a custom JQL function script that you can start with.
package com.onresolve.jira.groovy.jql
import com.atlassian.jira.JiraDataType
import com.atlassian.jira.JiraDataTypes
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.jql.operand.QueryLiteral
import com.atlassian.jira.jql.query.QueryCreationContext
import com.atlassian.jira.jql.validator.NumberOfArgumentsValidator
import com.atlassian.jira.project.version.Version
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 com.onresolve.jira.groovy.jql.entities.AbstractEntityFunction
class VersionsFromIssueFunction extends AbstractEntityFunction implements JqlValuesFunction {
@Override
List<String> getUnsupportedPredicates() {
return []
}
@Override
String getUsageMessage() {
return ""
}
@Override
String getAuthorFieldName() {
return null
}
@Override
String getCreatedFieldName() {
return null
}
@Override
protected String getCategory() {
return null
}
@Override
String getDescription() {
return "Find issues with version field matching one of the versions listed on the same field in another ticket"
}
@Override
String getFunctionName() {
return "versionsInIssue"
}
@Override
JiraDataType getDataType() {
JiraDataTypes.VERSION
}
@Override
MessageSet validate(ApplicationUser user, FunctionOperand operand, TerminalClause terminalClause) {
def messages = new NumberOfArgumentsValidator(1, 2, getI18n()).validate(operand)
def issueKey = operand.args[0].toUpperCase()
def issue = ComponentAccessor.issueManager.getIssueObject(issueKey)
if (!issue) {
messages.addMessage(MessageSet.Level.ERROR, "Issue '$issueKey' not found or not accessible by the current user")
}
def versionFieldMap = getVersionField(operand, terminalClause)
if (versionFieldMap.type == 'cf') {
def cf = versionFieldMap.cf as CustomField
if (!cf) {
messages.addMessage(MessageSet.Level.ERROR, "No custom field found matching '$versionFieldMap.name'. Use fixVersion, affectedVersion or a valid Version picker custom field")
} else {
def validCfKeys = ['customfieldtypes:version', 'customfieldtypes:multiversion']
if (!validCfKeys.any { cf.customFieldType.key.endsWith(it) }) {
messages.addMessage(MessageSet.Level.ERROR, "'$cf.name' is not a valid Version picker custom field.")
}
}
}
messages
}
@Override
List<Map> getArguments() {
[
[description: 'Issue key to lookup versions from.', optional: false],
[description: 'Version field to lookup version from. Uses the same field if not specified', optional: true]
]
}
Map getVersionField(FunctionOperand operand, TerminalClause terminalClause) {
def versionFieldMap = [name: terminalClause.name, type: 'cf']
if (operand.args.size() > 1) {
log.info "Second argument detected. Using ${ operand.args[1]} as the Version field to lookup"
versionFieldMap.name = operand.args[1]
}
if (versionFieldMap.name.toLowerCase() == 'fixversion') {
versionFieldMap.name = 'fixVersions'
versionFieldMap.type = 'system'
} else if (versionFieldMap.name.toLowerCase() == 'affectedversion') {
versionFieldMap.name = 'affectedVersions'
versionFieldMap.type = 'system'
} else {
if (versionFieldMap.name.startsWith('cf[')) {
def cfId = versionFieldMap.name.tokenize('[]')[1]
versionFieldMap.cf = ComponentAccessor.customFieldManager.getCustomFieldObject(cfId as Long)
} else {
versionFieldMap.cf = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName(versionFieldMap.name)[0]
}
}
log.info "Version field Map = $versionFieldMap"
versionFieldMap
}
@Override
List<QueryLiteral> getValues(QueryCreationContext queryCreationContext, FunctionOperand operand, TerminalClause terminalClause) {
def issueKey = operand.args[0].toUpperCase()
def issue = ComponentAccessor.issueManager.getIssueObject(issueKey)
List<Version> versions = []
def versionFieldMap = getVersionField(operand, terminalClause)
if (versionFieldMap.type == 'system') {
versions = issue[versionFieldMap.name] as List<Version>
} else {
versions = issue.getCustomFieldValue(versionFieldMap.cf as CustomField) as List<Version>
}
log.info "Returning versions: $versions"
return versions.collect { new QueryLiteral(operand, it.id) }
}
}
You'll have to save that file as <jirahome>/com/onresolve/jira/groovy/jql/VersionsFromIssueFunction.groovy
Then go to <jiraBaseUrl>/plugins/servlet/scriptrunner/admin/jqlfunctions and click on "scan".
Thanks for all the effort, but I'm at user/project admin level in our (Jira) organisation and can't implement custom scripts, just JQL queries. I guess I'll have to take your word for it that it isn't possible and convince our Jira admins that such a script might be useful.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Exty Ypsilonty Welcome to the community. If you are working in Data Center, then this sounds like a good candidate for a custom JQL function, which you can implement in ScriptRunner.
I don't have a suggestion for you if you are in the Cloud.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.