You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cassandra.apache.org by Benedict Elliott Smith <be...@apache.org> on 2019/04/16 10:23:46 UTC

Jira Updates

Some exciting news from the Jira changes (maybe).

We’re done!  We’ve even achieved the stretch goals.

Does anyone have any further suggestions from the new workflow, after having tried it out for a bit?  Speak up while we have the easy capability to make changes!

- Somebody has proposed making Change Category a multi-select list, which would require recreating this.  Does anybody have an opinion on this? 
- Would anybody prefer that one of the new fields be recreated as a project-specific select-list, like Platform and Impacts (see below), so we can manage the options?

Gavin McDonald kindly set us up with two plugins.  With the ScriptRunner plugin, I’ve setup scripted listeners to maintain the Authors field (based on Assignee) and Priority field (based on updates to Severity), as well as initialising both of these for all issues.  Try it out - it works surprisingly well, but please let me know ASAP if there are any bugs.  

The Authors management may be slightly unintuitive, in that if assignee is updated we always overwrite Authors to the value of the assignee.  We could maybe do something more sophisticated by checking what the prior value of Assignee was, but this looked very fiddly.  I think in the vast majority of circumstances this will work as we expect; if there are multiple authors actively working I expect the assignee not to change; if the assignee is cleared we probably want to clear the Authors field; and if it is set to a new value it’s probably the new author.  If somebody has suggestions for a better approach, please chime in.

The other plugin provided us with project-specific select lists - which we’ve used for Platform and Impacts.  These can be modified by any project admin, so we can maintain them ourselves.  Impacts and Platform have a default value of None and All respectively, to avoid UX ugliness with this multi-select list; don’t ask me why this makes them prettier, but it does.  I had originally proposed using these values so that we could force the user to specify a value, but opted to not make the UX ugly, while supporting project ownership of the options.  Opinions on this decision welcome.

For posterity, here are the ScriptRunner scripts I used and setup:


===== LISTENER TO SET PRIORITY WHEN SEVERITY UPDATED ====

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.priority.Priority
import com.atlassian.jira.issue.context.IssueContext
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.customfields.option.Options
import com.atlassian.jira.issue.customfields.manager.OptionsManager

def issue = event.issue as MutableIssue
def severityChange = null != event?.getChangeLog()?.getRelated("ChildChangeItem")?.find {it.field == "Severity"}
if (issue.issueType.name == "Bug" && severityChange) {
    def issueManager = ComponentAccessor.getIssueManager()
    def customFieldManager = ComponentAccessor.getCustomFieldManager()
    def cf = customFieldManager.getCustomFieldObject("customfield_12313820" );

    def constantsManager = ComponentAccessor.getConstantsManager()
    Priority pUrgent = constantsManager.getPriorities().find {it.name == "Urgent"}
    Priority pHigh = constantsManager.getPriorities().find {it.name == "High"}
    Priority pNormal = constantsManager.getPriorities().find {it.name == "Normal"}
    Priority pLow = constantsManager.getPriorities().find {it.name == "Low"}

    def optManager = ComponentAccessor.getOptionsManager(); 
    Options severities = optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL))   
    Option sCritical = severities.getOptionForValue("Critical", null)
    Option sNormal = severities.getOptionForValue("Normal", null)
    Option sLow = severities.getOptionForValue("Low", null)

    def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
    def priority = issue.getPriority()
    def severity = issue.getCustomFieldValue(cf)

    if (severity != null) {
        def newPriority = priority;
        if (severity == sCritical) {
            newPriority = pUrgent
        } else if (severity == sNormal) {
            newPriority = pNormal
        } else if (severity == sLow) {
            newPriority = pLow
        }
        if (newPriority != priority) {
            issue.setPriority(newPriority)
            issueManager.updateIssue(sdUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
        }
    }
}


===== LISTENER TO SET AUTHORS WHEN ASSIGNEE UPDATED ====

import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.index.IssueIndexingService

def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cf = customFieldManager.getCustomFieldObjectByName( "Authors" );

def issue = event.issue as MutableIssue
def assignee = issue.getAssignee()
if (assignee != null) {
    def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf)
    if (authors == null || !authors.contains(assignee)) {
        authors = new ArrayList<ApplicationUser>()
        authors.add(assignee)
        issue.setCustomFieldValue(cf, authors)
        issueManager.updateIssue(sdUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
    }
} else {
    def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf)
    if (authors != null) {
        issue.setCustomFieldValue(cf, new ArrayList<ApplicationUser>())
        issueManager.updateIssue(sdUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
    }
}


===== SCRIPT RO COPY ASSIGNEE TO AUTHORS ====

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder

def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cf = customFieldManager.getCustomFieldObjectByName( "Authors" );

def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def query1 = jqlQueryParser.parseQuery("project = CASSANDRA")
def results1 = searchProvider.search(query1, sdUser, PagerFilter.getUnlimitedFilter())
log.warn("All issues=" + results1.total)
results1.getIssues().each() {
    documentIssue ->
        def issue = issueManager.getIssueObject(documentIssue.id)
    	def assignee = issue.getAssignee()
        if (assignee != null) {
            def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf)
            if (authors == null) {
                authors = new ArrayList<ApplicationUser>()
            } else {
                authors = new ArrayList<ApplicationUser>(authors)
            }
            if (!authors.contains(assignee)) {
                authors.add(assignee)
                issue.setCustomFieldValue(cf, authors)
                issueManager.updateIssue(sdUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
                log.warn(documentIssue.id)
            }
        }
}


===== SCRIPT TO COPY PRIORITY TO SEVERITY ====

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.util.ImportUtils
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueChangeHolder
import com.atlassian.jira.issue.priority.Priority
import com.atlassian.jira.issue.context.IssueContext
import com.atlassian.jira.issue.customfields.option.Option
import com.atlassian.jira.issue.customfields.option.Options
import com.atlassian.jira.issue.customfields.manager.OptionsManager

def issueManager = ComponentAccessor.getIssueManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def cf = customFieldManager.getCustomFieldObject("customfield_12313820" );

def constantsManager = ComponentAccessor.getConstantsManager()
Priority pUrgent = constantsManager.getPriorities().find {it.name == "Urgent"}
Priority pHigh = constantsManager.getPriorities().find {it.name == "High"}
Priority pNormal = constantsManager.getPriorities().find {it.name == "Normal"}
Priority pLow = constantsManager.getPriorities().find {it.name == "Low"}

def optManager = ComponentAccessor.getOptionsManager(); 
Options severities = optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL))   
Option sCritical = severities.getOptionForValue("Critical", null)
Option sNormal = severities.getOptionForValue("Normal", null)
Option sLow = severities.getOptionForValue("Low", null)

def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def query1 = jqlQueryParser.parseQuery("project = CASSANDRA and priority = High")
def results1 = searchProvider.search(query1, sdUser, PagerFilter.getUnlimitedFilter())
log.warn("All issues=" + results1.total)
results1.getIssues().each() {
    documentIssue ->
        def issue = issueManager.getIssueObject(documentIssue.id)
    	def priority = issue.getPriority()
        if (priority != null) {
            def severity = issue.getCustomFieldValue(cf)
            def newSeverity = severity;
            if (priority == pUrgent) {
                newSeverity = sCritical
            } else if (priority == pHigh) {
                newSeverity = sNormal
            } else if (priority == pNormal) {
                newSeverity = sNormal
            } else if (priority == pLow) {
                newSeverity = sLow
            } else {
                log.warn(priority)
            }
            if (newSeverity != severity) {
                issue.setCustomFieldValue(cf, newSeverity)
                issueManager.updateIssue(sdUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false)
            }
        }
}

Re: Jira Updates

Posted by Joshua McKenzie <jm...@apache.org>.
I have no useful feedback on the changes (haven't had a chance to test
them, probably won't soon) - just wanted to say thanks for taking point on
this Benedict.

On Tue, Apr 16, 2019 at 6:23 AM Benedict Elliott Smith <be...@apache.org>
wrote:

> Some exciting news from the Jira changes (maybe).
>
> We’re done!  We’ve even achieved the stretch goals.
>
> Does anyone have any further suggestions from the new workflow, after
> having tried it out for a bit?  Speak up while we have the easy capability
> to make changes!
>
> - Somebody has proposed making Change Category a multi-select list, which
> would require recreating this.  Does anybody have an opinion on this?
> - Would anybody prefer that one of the new fields be recreated as a
> project-specific select-list, like Platform and Impacts (see below), so we
> can manage the options?
>
> Gavin McDonald kindly set us up with two plugins.  With the ScriptRunner
> plugin, I’ve setup scripted listeners to maintain the Authors field (based
> on Assignee) and Priority field (based on updates to Severity), as well as
> initialising both of these for all issues.  Try it out - it works
> surprisingly well, but please let me know ASAP if there are any bugs.
>
> The Authors management may be slightly unintuitive, in that if assignee is
> updated we always overwrite Authors to the value of the assignee.  We could
> maybe do something more sophisticated by checking what the prior value of
> Assignee was, but this looked very fiddly.  I think in the vast majority of
> circumstances this will work as we expect; if there are multiple authors
> actively working I expect the assignee not to change; if the assignee is
> cleared we probably want to clear the Authors field; and if it is set to a
> new value it’s probably the new author.  If somebody has suggestions for a
> better approach, please chime in.
>
> The other plugin provided us with project-specific select lists - which
> we’ve used for Platform and Impacts.  These can be modified by any project
> admin, so we can maintain them ourselves.  Impacts and Platform have a
> default value of None and All respectively, to avoid UX ugliness with this
> multi-select list; don’t ask me why this makes them prettier, but it does.
> I had originally proposed using these values so that we could force the
> user to specify a value, but opted to not make the UX ugly, while
> supporting project ownership of the options.  Opinions on this decision
> welcome.
>
> For posterity, here are the ScriptRunner scripts I used and setup:
>
>
> ===== LISTENER TO SET PRIORITY WHEN SEVERITY UPDATED ====
>
> import com.atlassian.jira.component.ComponentAccessor
> import com.atlassian.jira.event.type.EventDispatchOption
> import com.atlassian.jira.issue.MutableIssue
> import com.atlassian.jira.util.ImportUtils
> import com.atlassian.jira.user.ApplicationUser;
> import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
> import com.atlassian.jira.issue.util.IssueChangeHolder
> import com.atlassian.jira.issue.priority.Priority
> import com.atlassian.jira.issue.context.IssueContext
> import com.atlassian.jira.issue.customfields.option.Option
> import com.atlassian.jira.issue.customfields.option.Options
> import com.atlassian.jira.issue.customfields.manager.OptionsManager
>
> def issue = event.issue as MutableIssue
> def severityChange = null !=
> event?.getChangeLog()?.getRelated("ChildChangeItem")?.find {it.field ==
> "Severity"}
> if (issue.issueType.name == "Bug" && severityChange) {
>     def issueManager = ComponentAccessor.getIssueManager()
>     def customFieldManager = ComponentAccessor.getCustomFieldManager()
>     def cf =
> customFieldManager.getCustomFieldObject("customfield_12313820" );
>
>     def constantsManager = ComponentAccessor.getConstantsManager()
>     Priority pUrgent = constantsManager.getPriorities().find {it.name ==
> "Urgent"}
>     Priority pHigh = constantsManager.getPriorities().find {it.name ==
> "High"}
>     Priority pNormal = constantsManager.getPriorities().find {it.name ==
> "Normal"}
>     Priority pLow = constantsManager.getPriorities().find {it.name ==
> "Low"}
>
>     def optManager = ComponentAccessor.getOptionsManager();
>     Options severities =
> optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL))
>     Option sCritical = severities.getOptionForValue("Critical", null)
>     Option sNormal = severities.getOptionForValue("Normal", null)
>     Option sLow = severities.getOptionForValue("Low", null)
>
>     def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
>     def priority = issue.getPriority()
>     def severity = issue.getCustomFieldValue(cf)
>
>     if (severity != null) {
>         def newPriority = priority;
>         if (severity == sCritical) {
>             newPriority = pUrgent
>         } else if (severity == sNormal) {
>             newPriority = pNormal
>         } else if (severity == sLow) {
>             newPriority = pLow
>         }
>         if (newPriority != priority) {
>             issue.setPriority(newPriority)
>             issueManager.updateIssue(sdUser, issue,
> EventDispatchOption.DO_NOT_DISPATCH, false)
>         }
>     }
> }
>
>
> ===== LISTENER TO SET AUTHORS WHEN ASSIGNEE UPDATED ====
>
> import com.atlassian.jira.issue.Issue
> import com.atlassian.jira.issue.ModifiedValue
> import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
> import com.atlassian.jira.component.ComponentAccessor
> import com.atlassian.jira.user.ApplicationUser;
> import com.atlassian.jira.issue.MutableIssue
> import com.atlassian.jira.event.type.EventDispatchOption
> import com.atlassian.jira.issue.index.IssueIndexingService
>
> def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
> def issueManager = ComponentAccessor.getIssueManager()
> def customFieldManager = ComponentAccessor.getCustomFieldManager()
> def cf = customFieldManager.getCustomFieldObjectByName( "Authors" );
>
> def issue = event.issue as MutableIssue
> def assignee = issue.getAssignee()
> if (assignee != null) {
>     def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf)
>     if (authors == null || !authors.contains(assignee)) {
>         authors = new ArrayList<ApplicationUser>()
>         authors.add(assignee)
>         issue.setCustomFieldValue(cf, authors)
>         issueManager.updateIssue(sdUser, issue,
> EventDispatchOption.DO_NOT_DISPATCH, false)
>     }
> } else {
>     def authors = (List<ApplicationUser>)issue.getCustomFieldValue(cf)
>     if (authors != null) {
>         issue.setCustomFieldValue(cf, new ArrayList<ApplicationUser>())
>         issueManager.updateIssue(sdUser, issue,
> EventDispatchOption.DO_NOT_DISPATCH, false)
>     }
> }
>
>
> ===== SCRIPT RO COPY ASSIGNEE TO AUTHORS ====
>
> import com.atlassian.jira.component.ComponentAccessor
> import com.atlassian.jira.issue.search.SearchProvider
> import com.atlassian.jira.jql.parser.JqlQueryParser
> import com.atlassian.jira.web.bean.PagerFilter
> import com.atlassian.jira.event.type.EventDispatchOption
> import com.atlassian.jira.issue.MutableIssue
> import com.atlassian.jira.issue.index.IssueIndexingService
> import com.atlassian.jira.util.ImportUtils
> import com.atlassian.jira.user.ApplicationUser;
> import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
> import com.atlassian.jira.issue.util.IssueChangeHolder
>
> def issueManager = ComponentAccessor.getIssueManager()
> def customFieldManager = ComponentAccessor.getCustomFieldManager()
> def cf = customFieldManager.getCustomFieldObjectByName( "Authors" );
>
> def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
> def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
> def searchProvider = ComponentAccessor.getComponent(SearchProvider)
> def query1 = jqlQueryParser.parseQuery("project = CASSANDRA")
> def results1 = searchProvider.search(query1, sdUser,
> PagerFilter.getUnlimitedFilter())
> log.warn("All issues=" + results1.total)
> results1.getIssues().each() {
>     documentIssue ->
>         def issue = issueManager.getIssueObject(documentIssue.id)
>         def assignee = issue.getAssignee()
>         if (assignee != null) {
>             def authors =
> (List<ApplicationUser>)issue.getCustomFieldValue(cf)
>             if (authors == null) {
>                 authors = new ArrayList<ApplicationUser>()
>             } else {
>                 authors = new ArrayList<ApplicationUser>(authors)
>             }
>             if (!authors.contains(assignee)) {
>                 authors.add(assignee)
>                 issue.setCustomFieldValue(cf, authors)
>                 issueManager.updateIssue(sdUser, issue,
> EventDispatchOption.DO_NOT_DISPATCH, false)
>                 log.warn(documentIssue.id)
>             }
>         }
> }
>
>
> ===== SCRIPT TO COPY PRIORITY TO SEVERITY ====
>
> import com.atlassian.jira.component.ComponentAccessor
> import com.atlassian.jira.issue.search.SearchProvider
> import com.atlassian.jira.jql.parser.JqlQueryParser
> import com.atlassian.jira.web.bean.PagerFilter
> import com.atlassian.jira.event.type.EventDispatchOption
> import com.atlassian.jira.issue.MutableIssue
> import com.atlassian.jira.issue.index.IssueIndexingService
> import com.atlassian.jira.util.ImportUtils
> import com.atlassian.jira.user.ApplicationUser;
> import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
> import com.atlassian.jira.issue.util.IssueChangeHolder
> import com.atlassian.jira.issue.priority.Priority
> import com.atlassian.jira.issue.context.IssueContext
> import com.atlassian.jira.issue.customfields.option.Option
> import com.atlassian.jira.issue.customfields.option.Options
> import com.atlassian.jira.issue.customfields.manager.OptionsManager
>
> def issueManager = ComponentAccessor.getIssueManager()
> def customFieldManager = ComponentAccessor.getCustomFieldManager()
> def cf = customFieldManager.getCustomFieldObject("customfield_12313820" );
>
> def constantsManager = ComponentAccessor.getConstantsManager()
> Priority pUrgent = constantsManager.getPriorities().find {it.name ==
> "Urgent"}
> Priority pHigh = constantsManager.getPriorities().find {it.name == "High"}
> Priority pNormal = constantsManager.getPriorities().find {it.name ==
> "Normal"}
> Priority pLow = constantsManager.getPriorities().find {it.name == "Low"}
>
> def optManager = ComponentAccessor.getOptionsManager();
> Options severities =
> optManager.getOptions(cf.getRelevantConfig(IssueContext.GLOBAL))
> Option sCritical = severities.getOptionForValue("Critical", null)
> Option sNormal = severities.getOptionForValue("Normal", null)
> Option sLow = severities.getOptionForValue("Low", null)
>
> def sdUser = ComponentAccessor.getUserManager().getUserByKey('robot')
> def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
> def searchProvider = ComponentAccessor.getComponent(SearchProvider)
> def query1 = jqlQueryParser.parseQuery("project = CASSANDRA and priority =
> High")
> def results1 = searchProvider.search(query1, sdUser,
> PagerFilter.getUnlimitedFilter())
> log.warn("All issues=" + results1.total)
> results1.getIssues().each() {
>     documentIssue ->
>         def issue = issueManager.getIssueObject(documentIssue.id)
>         def priority = issue.getPriority()
>         if (priority != null) {
>             def severity = issue.getCustomFieldValue(cf)
>             def newSeverity = severity;
>             if (priority == pUrgent) {
>                 newSeverity = sCritical
>             } else if (priority == pHigh) {
>                 newSeverity = sNormal
>             } else if (priority == pNormal) {
>                 newSeverity = sNormal
>             } else if (priority == pLow) {
>                 newSeverity = sLow
>             } else {
>                 log.warn(priority)
>             }
>             if (newSeverity != severity) {
>                 issue.setCustomFieldValue(cf, newSeverity)
>                 issueManager.updateIssue(sdUser, issue,
> EventDispatchOption.DO_NOT_DISPATCH, false)
>             }
>         }
> }