Run all effectors by schedule
This script can be used in ScriptRunner to run effectors by schedule. See ScriptRunner Jobs.
Run effectors
/*
* This script allows to start specified or all effectors for chosen structure.
* It will ping started effector process until it finishes.
* It will NOT acknowledge the process at the end so the process will be available in the structure status bar process list.
*/
// params
def structureId = 14 // target structure ID
def effectorIds = [2, 1] // effector IDs to run or empty to run all effectors in structure
def processCheckInterval = 1000; // milliseconds
// Structure imports
import com.almworks.jira.structure.api.StructurePluginHelper
import com.almworks.jira.structure.api.auth.StructureAuth
import com.almworks.jira.structure.api.effector.instance.EffectorInstanceManager
import com.almworks.jira.structure.api.effector.process.EffectorProcessManager
import com.almworks.jira.structure.api.error.StructureException
import com.almworks.jira.structure.api.forest.ForestSpec
import com.almworks.jira.structure.api.forest.ForestService
import com.almworks.jira.structure.api.forest.ForestSource
import com.almworks.jira.structure.api.StructureComponents
import com.almworks.jira.structure.api.item.CoreIdentities
import com.almworks.jira.structure.api.permissions.PermissionLevel
import com.almworks.jira.structure.api.process.ProcessHandleManager
import com.almworks.jira.structure.api.process.ProcessStatus
import com.almworks.jira.structure.api.row.RowManager
import com.almworks.jira.structure.api.structure.StructureManager
import com.almworks.jira.structure.api.util.StructureUtil
// ScriptRunner imports
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
// Atlassian import (might be available without the import in some instances, but better safe than sorry)
import com.atlassian.jira.component.ComponentAccessor
import static java.util.Arrays.asList
// switch to plugin
@Grab(group = 'com.almworks.jira.structure', module = 'structure-api', version = '*')
@WithPlugin('com.almworks.jira.structure')
class EffectorRunner {
StructureComponents SC = ScriptRunnerImpl.getPluginComponent(StructureComponents)
ForestService FS = SC.getForestService()
RowManager RM = SC.getRowManager()
StructureManager SM = SC.getStructureManager()
ProcessHandleManager PHM = SC.getProcessHandleManager()
// next components are not available from StructureComponents in Structure 6.x, but who knows :)
EffectorInstanceManager EIM = ScriptRunnerImpl.getPluginComponent(EffectorInstanceManager)
EffectorProcessManager EPM = ScriptRunnerImpl.getPluginComponent(EffectorProcessManager)
def user = StructureAuth.getUser()
ForestSource source
def structureId
def effectorIds
def log
def processCheckInterval
EffectorRunner(def log, def structureId, def effectorIds, def processCheckInterval) {
this.structureId = structureId
this.effectorIds = effectorIds
this.log = log
this.processCheckInterval = processCheckInterval
try {
// check for structure existence and permissions
SM.getStructure(structureId, PermissionLevel.VIEW)
source = FS.getForestSource(ForestSpec.skeleton(structureId))
} catch (StructureException ex) {
log.error("Cant create forest source for structure id=${structureId} for user ${user}.\n${trimStackTrace(ex)}")
}
}
def run() {
def caption = "See logs for results."
if (source == null) return caption
def structureName = FS.getDisplayName(ForestSpec.skeleton(structureId))
def effectors = getEffectorInstances()
if (effectors.isEmpty()) {
log.warn("No effectors were found.")
return caption
}
if (!effectorIds.isEmpty() && effectorIds.size != effectors.size()) {
return caption
}
log.warn("Starting to execute effectors for structure '${structureName}' for user ${user}")
// print effectors list to run
effectors.each {ef ->
log.warn("\teffector: id=${ef.getId()} ${ef.getModuleKey()} ${ef.getParameters()}")
}
// start effectors process
def processId = runEffectors(effectors)
if (processId == null) return caption
log.warn("Effector process id=${processId} started.")
// await process finish
if (awaitFinish(processId)) {
// print produced effect records and errors
printResults(processId)
log.warn("Done.")
} else {
log.error("Failed.")
}
return caption
}
def printResults(def processId) {
def records = EPM.getEffectRecords(processId)
if (records.isEmpty()) {
log.warn("No effect records were produced.")
} else if (records.size() > 100) { // just not to spam
log.warn("There are ${records.size()} applied effects.")
} else {
records.each {record ->
// choose right log level for errors and applied effects
if (record.isError()) {
log.error(StructureUtil.getTextInCurrentUserLocale(record.getEffectMessage()))
} else {
log.warn(StructureUtil.getTextInCurrentUserLocale(record.getEffectMessage()))
}
}
}
}
// This is blocking method will run until effectors process finish.
def awaitFinish(def processId) {
def handleId
try {
def process = EPM.getProcess(processId)
handleId = process.getProcessHandleId()
} catch (StructureException ex) {
log.error("Failed to get process info\n.${trimStackTrace(ex)}")
return false
}
def info = PHM.getInfo(handleId)
if (info == null) return false
def prevProgress = 0
while (info != null && info.getStatus() != ProcessStatus.FINISHED && sleep()) {
info = PHM.getInfo(handleId)
if (info.percentComplete != prevProgress) {
def process = EPM.getProcess(processId)
log.warn("\tprocess complete ${info.getPercentComplete()}%. Status ${process.getStatus()}")
prevProgress = info.getPercentComplete()
}
}
return info != null
}
def sleep() {
try {
Thread.sleep(processCheckInterval)
return true
} catch (InterruptedException ex) {
log.warn(${trimStackTrace(ex)})
Thread.currentThread().interrupt()
return false
}
return true
}
def runEffectors(def effectors) {
try {
return EPM.startProcess(effectors, structureId, false)
} catch (StructureException ex) {
log.error("Effector process validation failed.\n${trimStackTrace(ex)}")
return null
}
}
def getEffectorInstances() {
def ids = effectorIds.isEmpty() ? findEffectors() : effectorIds
def instances = []
ids.each {id ->
try {
instances.add(EIM.getEffectorInstance(id))
} catch (StructureException ex) {
log.error("Unable to find effector with id=${id}.\n${trimStackTrace(ex)}")
}
}
return instances
}
def findEffectors() {
def effectorIds = []
source.getLatest().getForest().forEach {row ->
def rowId = row.left()
def itemId = RM.getRow(rowId).getItemId()
if (CoreIdentities.isEffector(itemId)) {
effectorIds.add(itemId.getLongId())
}
}
return effectorIds
}
def trimStackTrace(Exception e) {
return asList(e.stackTrace).subList(0, 20).join('\n ') + '\n ...'
}
}
new EffectorRunner(log, structureId, effectorIds, processCheckInterval).run()