Switching Worfklow Scheme programatically

I am attempting to clone an existing workflow scheme, and all its workflows, then assign the newly cloned workflow scheme to the original project. So far, I have been successful with cloning everything, and I am able to create a workflow scheme that exists in the Database. I am then able to assign that workflow scheme to my project manually without issue.

My next step is trying to figure out how to migrate workflow schemes programmatically, with the consideration that there are no actual changes to the workflow involved, and thus there does not need to be any mapping of statuses during the transition.

I am on Jira 8.5.4, and running this in the script console provided by the Scriptrunner plugin.

Here is my code for generating the copy:

// Imports
import com.atlassian.jira.component.ComponentAccessor 
import com.atlassian.jira.user.ApplicationUsers 
import com.atlassian.jira.scheme.Scheme 
import com.atlassian.jira.bc.workflow.WorkflowSchemeService
import com.atlassian.jira.workflow.migration.DefaultMigrationHelperFactory

// Tool Functions
def generator = { String alphabet, int n ->
  new Random().with {
    (1..n).collect { alphabet[ nextInt( alphabet.length() ) ] }.join()

def randomKey = generator( ('A'..'Z').join(), 6 )

// Building Classes via Methods
def projectManager = ComponentAccessor.getProjectManager()
def workflowManager = ComponentAccessor.getWorkflowManager() 
def workflowSchemeManager = ComponentAccessor.getWorkflowSchemeManager() 
def workflowSchemeService = ComponentAccessor.getComponent(WorkflowSchemeService) 

// User Inputs & Building objects via Methods
def project = projectManager.getProjectByCurrentKey('PROJECTKEY') 
def authUser = ApplicationUsers.byKey('Username')

// Get Assignable Workflow Scheme Object
def targetWorkflowScheme = workflowSchemeManager.getWorkflowSchemeObj(project)
// Create a new WorkflowScheme Builder give it a name and description
def newWorkflowScheme = workflowSchemeService.assignableBuilder().setDescription("${project}").setName("${project.getName()} - ${randomKey}")//"${targetWorkflowScheme.getName()} - ${project.getName()} - ${randomKey}")

// Get Map of issue type assignments and workflow names associated with Assignable Workflow Scheme Object. [IssuetypeID:Workflow]. Null key means it is default workflow
def targetWorkflowsMap = targetWorkflowScheme.getMappings() 

// Copy each workflow, assign it as either default or to its issue type
for (i in targetWorkflowsMap) {
    log.warn('next wf')
    def wf = workflowManager.getWorkflow(i.value)  
    def wfName =  "${wf.getName()} - ${project.getName()} - ${randomKey}" //"${wf.getName()} - ${project.getName()} - ${randomKey}"
    def wfDesc = 'test'
    def wfNew = workflowManager.copyWorkflow(user = authUser, clonedWorkflowName = wfName, clonedWorkflowDescriptions = wfDesc, workflowToClone = wf)
    //workflowManager.createWorkflow(user = authUser, workflow = wfNew)
    if (i.getKey() == null) {
        log.warn ('default')
        newWorkflowScheme.setDefaultWorkflow(workflowName = wfName)
    } else {
        log.warn('not default')
        newWorkflowScheme.setMapping(issueTypeId = i.getKey(), workflowName = wfName)

// Build and get Scheme object
def newScheme = workflowSchemeService.createScheme(user = authUser, workflowScheme = newWorkflowScheme.build())
def newSchemeValue = newScheme.getReturnedValue()

// Logging Final Workflow Scheme Name, ID, and existence in the DB
log.warn("Old Scheme ID: "+"${targetWorkflowScheme.getId()}")
log.warn("New Scheme Name: " + newSchemeValue.getName())
log.warn("New Scheme ID: " + newSchemeValue.getId())
log.warn("Workflow exists in the DB?: " + "${workflowSchemeManager.schemeExists(name = newSchemeValue.getName())}")

Next, are my various attempts to actually assign the workflow to the project:

  1. Using the migration helper
def migrationHelperFactory = ComponentAccessor.getComponent(DefaultMigrationHelperFactory)

def migrationHelper = migrationHelperFactory.createMigrationHelper(project, newScheme)

assert migrationHelper.doQuickMigrate()//attempt migrate issues if needed

  1. I also tried to force it, but this breaks the workflows involved:
// Looping for the Scheme Object, log it, remove all workflow schemes from the project, then add the new scheme to the project.

for ( i in workflowSchemeManager.getUnassociatedSchemes()){
    if (i.getId() == newScheme.getId()){
        log.warn (i)
        workflowSchemeManager.addSchemeToProject(project = project, scheme = i)
        log.warn("Scheme Added: " + "${i}")

Here I had to pull the scheme from all unassociated schemes, because the addSchemeToProject method didnt like it when I passed it an assignable workflow scheme I created earlier.

Any general advice is appreciated!

Hey @JoshuaReddish,

There may be a little problem with alternative 1; newScheme is of type ServiceOutcome<AssignableWorkflowScheme> so we may try passing newSchemeValue, which is assigned as def newSchemeValue = newScheme.getReturnedValue() in the main script.

Last part (the migration) building on the main part that’s already working for you, may look like this;

def migrationHelperFactory = ComponentAccessor.getComponent(DefaultMigrationHelperFactory)
def migrationHelper = migrationHelperFactory.createMigrationHelper(project, newSchemeValue)

or, if we also want to see the logs generated, we can implement a simple sink like below and use it instead

def migrationHelperFactory = ComponentAccessor.getComponent(DefaultMigrationHelperFactory)
def migrationHelper = migrationHelperFactory.createMigrationHelper(project, newSchemeValue)
def mySink = [makeProgress: {taskProgress, currentSubTask, message -> log.warn(taskProgress + ", " + currentSubTask + ", " + message)}] as com.atlassian.jira.task.TaskProgressSink

Hope this helps. Cheers.

Hi @KurtcebeEroglu,
Thanks you so much for your answer! This worked perfectly. Looks like i just needed to do a migrate instead of a doQuickMigrate. This helps us immensely!

Thank you and Atlassian for your incredible support of your application!



1 Like