Hello,
I’m trying to create jira workflows using Java APIs, and followed the following article - Create Workflow through Java API - Jira Development / Jira Server - The Atlassian Developer Community
I implemented it with help of Rest plugin module, with a post method, when called triggers the below code, in which I’ve hard coded the new steps and the transitions of the new workflow.
public Response createWorkflow(@PathParam("workflowName") String workflowName){
WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager();
ApplicationUser user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
String workflowDescription = "A custom workflow created using Jira API";
try{
// Create a new workflow based on the default Jira workflow template
JiraWorkflow baseWorkflow = workflowManager.getWorkflow("jira");
// Create a draft workflow from the base workflow to modify
JiraWorkflow newWorkflow = workflowManager.copyWorkflow(user, workflowName, workflowDescription, baseWorkflow);
// Get the workflow descriptor to add steps/actions
WorkflowDescriptor workflowDescriptor = newWorkflow.getDescriptor();
String[] statuses = {"To Do(Test)", "Done(Test)"};
Map<String, StepDescriptor> stepMap = new HashMap<>();
List<StepDescriptor> steps = new ArrayList<>(workflowDescriptor.getSteps());
StepDescriptor lastStep = steps.get(steps.size()-2);
stepMap.put(lastStep.getName(), lastStep);
for(String status : statuses){
// Create a new step descriptor
StepDescriptor newStep = DescriptorFactory.getFactory().createStepDescriptor();
newStep.setId(WorkflowUtil.getNextId(workflowDescriptor.getSteps()));
newStep.setName(status);
// Add the step to the workflow descriptor
workflowDescriptor.addStep(newStep);
stepMap.put(status, newStep); // Map status name to StepDescriptor
}
Map<String, String> transitions = new HashMap<>();
transitions.put("Reopened -> To Do(Test)", "Work In Progress");
transitions.put("To Do(Test) -> Done(Test)", "Work done");
for (Map.Entry<String, String> transition : transitions.entrySet()) {
String[] transitionSteps = transition.getKey().split("->");
if (transitionSteps.length == 2) {
String fromStatus = transitionSteps[0].trim();
String toStatus = transitionSteps[1].trim();
String transitionName = transition.getValue();
// Get the "from" and "to" steps from the map
StepDescriptor fromStep = stepMap.get(fromStatus);
StepDescriptor toStep = stepMap.get(toStatus);
if (fromStep != null) {
// Create a new action (transition)
ActionDescriptor actionDescriptor = DescriptorFactory.getFactory().createActionDescriptor();
actionDescriptor.setId(fromStep.getActions().size() + 1); // Increment action ID
actionDescriptor.setName(transitionName);
// Define the result of this action: transition to the next step
ResultDescriptor resultDescriptor = DescriptorFactory.getFactory().createResultDescriptor();
resultDescriptor.setStep(toStep.getId());
resultDescriptor.setOldStatus(fromStep.getName());
resultDescriptor.setStatus(toStep.getName());
// Add result to the action
actionDescriptor.setParent(fromStep);
actionDescriptor.setUnconditionalResult(resultDescriptor);
// Link the action to the step by adding it to the step's action list
fromStep.getActions().add(actionDescriptor);
}
}
}
// Save the workflow
workflowManager.updateWorkflow(user, newWorkflow);
System.out.println("Workflow created: " + workflowName);
return Response.ok("Workflow created successfully: " + newWorkflow.getName()).build();
} catch (Exception e) {
e.printStackTrace();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Error occurred while creating workflow: " + e.getMessage())
.build();
}
}
The post methods response is success without any error and also checked in DB the new workflow got created, but the workflows get disappeared from the UI, even the previous workflows, which are already present.
Then I checked the logs file in the target folder where I found a Runtime exception as shown below
2024-11-26 12:11:52,406+0530 http-nio-2990-exec-7 url: /jira/rest/workflowplugin/1.0/new-workflow/TP-Test-Workflow; user: admin ERROR admin 731x2176x1 1v63t1m 0:0:0:0:0:0:0:1 /rest/workflowplugin/1.0/new-workflow/TP-Test-Workflow [c.a.event.internal.AsynchronousAbleEventDispatcher] There was an exception thrown trying to dispatch event [com.atlassian.jira.event.WorkflowUpdatedEvent@b0f1a9ac] from the invoker [com.atlassian.event.internal.ComparableListenerInvoker@2cecdf61]
java.lang.RuntimeException: Step with id '7' does not have a valid linked status.. Listener: com.atlassian.jira.auditing.AuditingEventListener event: com.atlassian.jira.event.WorkflowUpdatedEvent
at com.atlassian.event.internal.SingleParameterMethodListenerInvoker.invoke(SingleParameterMethodListenerInvoker.java:53)
at com.atlassian.diagnostics.internal.platform.monitor.event.EventSystemMonitor.invokeMonitored(EventSystemMonitor.java:105)
at com.atlassian.diagnostics.internal.platform.monitor.event.MonitoredListenerInvoker.invoke(MonitoredListenerInvoker.java:38)
at com.atlassian.event.internal.ComparableListenerInvoker.invoke(ComparableListenerInvoker.java:48)
at com.atlassian.event.internal.AsynchronousAbleEventDispatcher.lambda$null$0(AsynchronousAbleEventDispatcher.java:37)
at com.atlassian.event.internal.AsynchronousAbleEventDispatcher.dispatch(AsynchronousAbleEventDispatcher.java:85)
at com.atlassian.diagnostics.internal.platform.monitor.event.MonitoredEventDispatcher.dispatch(MonitoredEventDispatcher.java:36)
at com.atlassian.event.internal.EventPublisherImpl.publish(EventPublisherImpl.java:114)
at com.atlassian.event.internal.LockFreeEventPublisher.publish(LockFreeEventPublisher.java:40)
at com.atlassian.jira.workflow.OSWorkflowManager.updateWorkflow(OSWorkflowManager.java:627)
at com.examplecom.plugins.rest.WorkflowRestResource.createWorkflow(WorkflowRestResource.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
... 19 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter$JerseyOsgiServletContainer.doFilter(RestDelegatingServletFilter.java:171)
... 1 filtered
at com.atlassian.plugins.rest.module.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:75)
... 36 filtered
at com.atlassian.jira.plugin.mobile.web.filter.MobileAppRequestFilter.doFilter(MobileAppRequestFilter.java:59)
... 4 filtered
at com.atlassian.jira.plugin.mobile.login.MobileLoginSuccessFilter.doFilter(MobileLoginSuccessFilter.java:54)
... 3 filtered
at com.atlassian.diagnostics.internal.platform.monitor.http.HttpRequestMonitoringFilter.doFilter(HttpRequestMonitoringFilter.java:54)
... 8 filtered
at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
... 24 filtered
at com.atlassian.labs.httpservice.resource.ResourceFilter.doFilter(ResourceFilter.java:59)
... 22 filtered
at com.atlassian.oauth2.scopes.web.ReadWriteScopeFilter.doFilter(ReadWriteScopeFilter.java:46)
... 3 filtered
at com.atlassian.ratelimiting.internal.filter.RateLimitFilter.doFilter(RateLimitFilter.java:73)
... 3 filtered
at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
... 17 filtered
at com.atlassian.jira.security.JiraSecurityFilter.lambda$doFilter$0(JiraSecurityFilter.java:66)
... 1 filtered
at com.atlassian.jira.security.JiraSecurityFilter.doFilter(JiraSecurityFilter.java:64)
... 16 filtered
at com.atlassian.plugins.rest.module.servlet.RestSeraphFilter.doFilter(RestSeraphFilter.java:38)
... 3 filtered
at com.atlassian.pats.web.filter.TokenBasedAuthenticationFilter.doFilter(TokenBasedAuthenticationFilter.java:82)
... 3 filtered
at com.atlassian.oauth2.provider.core.web.AccessTokenFilter.doFilter(AccessTokenFilter.java:82)
... 19 filtered
at com.atlassian.jira.servermetrics.CorrelationIdPopulatorFilter.doFilter(CorrelationIdPopulatorFilter.java:30)
... 5 filtered
at com.atlassian.plugins.authentication.impl.web.filter.loginform.DisableNativeLoginAuthFilter.doFilter(DisableNativeLoginAuthFilter.java:55)
... 3 filtered
at com.atlassian.plugins.authentication.impl.basicauth.filter.DisableBasicAuthFilter.doFilter(DisableBasicAuthFilter.java:70)
... 8 filtered
at com.atlassian.ratelimiting.internal.filter.RateLimitPreAuthFilter.doFilter(RateLimitPreAuthFilter.java:71)
... 3 filtered
at com.atlassian.web.servlet.plugin.request.RedirectInterceptingFilter.doFilter(RedirectInterceptingFilter.java:21)
... 4 filtered
at com.atlassian.troubleshooting.thready.filter.AbstractThreadNamingFilter.doFilter(AbstractThreadNamingFilter.java:46)
... 3 filtered
at com.atlassian.web.servlet.plugin.LocationCleanerFilter.doFilter(LocationCleanerFilter.java:36)
... 26 filtered
at com.atlassian.jira.servermetrics.MetricsCollectorFilter.doFilter(MetricsCollectorFilter.java:25)
... 24 filtered
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.IllegalStateException: Step with id '7' does not have a valid linked status.
at com.atlassian.jira.workflow.AbstractJiraWorkflow.getLinkedStatusId(AbstractJiraWorkflow.java:248)
at com.atlassian.jira.workflow.AbstractJiraWorkflow.getLinkedStatus(AbstractJiraWorkflow.java:225)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at com.atlassian.jira.workflow.AbstractJiraWorkflow.getLinkedStatusObjects(AbstractJiraWorkflow.java:260)
at java.base/java.util.Optional.map(Optional.java:265)
at com.atlassian.jira.auditing.handlers.WorkflowEventHandlerImpl.getLinkedStatuses(WorkflowEventHandlerImpl.java:120)
at com.atlassian.jira.auditing.handlers.WorkflowEventHandlerImpl.computeChangedValues(WorkflowEventHandlerImpl.java:99)
at com.atlassian.jira.auditing.handlers.WorkflowEventHandlerImpl.workflowUpdated(WorkflowEventHandlerImpl.java:86)
at com.atlassian.jira.auditing.handlers.WorkflowEventHandlerImpl.onWorkflowUpdatedEvent(WorkflowEventHandlerImpl.java:51)
at com.atlassian.jira.auditing.AuditingEventListener.onWorkflowDeletedEvent(AuditingEventListener.java:483)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.atlassian.event.internal.SingleParameterMethodListenerInvoker.invoke(SingleParameterMethodListenerInvoker.java:42)
... 299 more
Please feel free to point out anything that is not valid or any other additions to the code.
Please help me with creating a new workflow from scratch using JAVA APIs, if anyone has success in creating workflows using java apis.