How does atlassian format workflows programatically? Edited

Hey there,

i am importing workflows via my own scriptrunner restendpoints (including xml-parsing, statusmapping etc.) programatically into an jira instance.

So far works like a charm. Problem is the visual workflow designer displays all the status and transitions nearly unreadable under each other.

I was wondering if i could somehow programatically format them.

I came across the fact that the jira default workflow (atlassian-jira/WEB-INF/classes/jira-workflow.xml) is formatted well, but that there are no styling information in the xml file. Because of that i searched the whole atlassian-jira sources, but could not find anything that does that formatting.

If anyone knows how-/where it´s done, please share.

2 answers

1 accepted

1 vote
Accepted answer

If you have the JIRA Sources (I'm currently working with atlassian-jira-software-7.6.1-source), have a look at the following files:

  • jira-project/jira-components/jira-core/src/main/osworkflow/jira-workflow.xml
  • jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/upgrade/tasks/UpgradeTask_Build6123.java
  • jira-project/jira-components/jira-core/src/main/resources/system-classic-workflow-layout.json

They are mapping a JSON layout file on a XML workflow file...

If I will manage to get the JSON for an existing workflow, I will let you know. 

So, here we go:

/**
* Create a workflow based on an exported workflow XML file.
*
* You can provide a layout file in layout format v5 with the following pattern:
* - my-workflow.xml (workflow XML file)
* - my-workflow-format.json (workflow JSON format file)
*
* @param name the workflow name
* @param description the workflow description
* @param pathXml the path to the workflow xml file
* @return the created workflow
*/
@Nullable
public JiraWorkflow createWorkflowFromXML(
@Nonnull final String name,
@Nonnull final String description,
@Nullable final String pathXml) {

if (pathXml == null || pathXml.isEmpty() || !pathXml.endsWith(".xml")) {
return null;
}

final String xml = readFile(pathXml);
if (xml == null) {
return null;
}

final String layoutKey = DigestUtils.md5Hex(name);
final String pathFormatJson = pathXml.replace(".xml", "-format.json");
final String formatJson = readFile(pathFormatJson);

try {
final WorkflowDescriptor descriptor = WorkflowUtil.convertXMLtoWorkflowDescriptor(xml);
final ConfigurableJiraWorkflow workflow = new ConfigurableJiraWorkflow(name, descriptor, workflowManager);
workflow.setDescription(description);
workflowManager.createWorkflow(authenticationContext.getLoggedInUser(), workflow);

eventPublisher.publish(new WorkflowImportedFromXmlEvent(workflow));

if (formatJson != null) {
final Map<String, Object> entryFields = ImmutableMap.of(
"entityName", "com.atlassian.jira.plugins.jira-workflow-designer",
"entityId", 1,
"propertyKey", "jira.workflow.layout.v5:" + layoutKey,
"type", 6);
final Long layoutId = ofBizDelegator.createValue("OSPropertyEntry", entryFields).getLong("id");
final Map<String, Object> textFields = ImmutableMap.of(
"id", layoutId,
"value", formatJson);
ofBizDelegator.createValue("OSPropertyText", textFields);
}

return workflow;
} catch (FactoryException e) {
String message = "Error parsing workflow XML: " + e.getMessage();
log.error(message, e);
} catch (WorkflowException e) {
String message = "Error saving workflow: " + e.getMessage();
log.error(message, e);
}

return null;
}

/**
* Find the layout for a designed workflow.
*
* This json layout file can be used to format a workflow that is exported as XML
*
* @param name the workflow name
* @return the json layout
*/
@Nullable
public String getWorkflowFormatJson(
@Nonnull final String name) {

String layoutKey = DigestUtils.md5Hex(name);

Map<String, String> filter = ImmutableMap.of(
"entityName", "com.atlassian.jira.plugins.jira-workflow-designer",
"propertyKey", "jira.workflow.layout.v5:" + layoutKey);

Optional<Long> layoutId = ofBizDelegator.findByLike("OSPropertyEntry", filter).stream()
.map(gv -> gv.getLong("id"))
.findFirst();

if (layoutId.isPresent()) {
GenericValue osPropertyText = ofBizDelegator.findById("OSPropertyText", layoutId.get());

if (osPropertyText != null) {
return (String) osPropertyText.get("value");
}
}

return null;
}

@Nullable
private String readFile(String filename) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(filename);

try {
return IOUtils.toString(stream, "utf-8");
} catch (IOException e) {
log.error("Could not read file" + filename, e);
} finally {
IOUtils.closeQuietly(stream);
}

return null;
}

Note that replaced some custom stuff and did not test the code again. Also note that there are different format versions for the workflows and this code currently only works with version v5.

Cheers

That's handy!

Thanks @Patrick Hobusch for sharing your knowledge. I will give it a shot. 

0 votes

I've never seen a way to format the workflow diagram programmatically

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted Oct 16, 2018 in Jira

Looking for anyone who made the switch to Data Center

The Jira Marketing team is putting together an ebook on migrating to Data Center. We're looking for pro tips on how you staffed your project team and organized your Proof of Concept. Share yo...

1,392 views 17 10
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you