From 5be6e37c1c70fd9971de4f95db04fe7fecf6f664 Mon Sep 17 00:00:00 2001 From: Maciej Swiderski Date: Mon, 10 Jan 2022 14:55:31 +0100 Subject: [PATCH] allow to use custom workflow template for generatig diagrams Signed-off-by: Maciej Swiderski --- README.md | 16 ++++++ .../api/interfaces/WorkflowDiagram.java | 2 + .../diagram/WorkflowDiagramImpl.java | 13 ++++- .../diagram/utils/WorkflowToPlantuml.java | 5 +- .../CustomTemplateWorkflowDiagramTest.java | 50 +++++++++++++++++++ .../templates/plantuml/custom-template.txt | 46 +++++++++++++++++ 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java create mode 100644 diagram/src/test/resources/templates/plantuml/custom-template.txt diff --git a/README.md b/README.md index 39b5f173..11aaabff 100644 --- a/README.md +++ b/README.md @@ -267,6 +267,22 @@ String diagramSVG = workflowDiagram.getSvgDiagram(); `diagramSVG` includes the diagram SVG source which you can then decide to save to a file, print, or process further. +In case default visualization of the workflow is not sufficient you can provide custom workflow template to be +used while generating the SVG file. Easiest is to start off from the default template and customize it to your needs. + +Custom template must be on the classpath in `templates/plantuml` directory and must use `.txt` extension. Next +template is set on `WorkflowDiagram` instance as shown below. + +``` java +Workflow workflow = Workflow.fromSource(source); + +WorkflowDiagram workflowDiagram = new WorkflowDiagramImpl(); +workflowDiagram.setWorkflow(workflow); +workflowDiagram.setTemplate("custom-template"); + +String diagramSVG = workflowDiagram.getSvgDiagram(); +``` + By default the diagram legend is now shown. If you want to enable it you can do: ``` java diff --git a/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java b/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java index 6bba312c..0c62d4d2 100644 --- a/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java +++ b/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java @@ -22,6 +22,8 @@ public interface WorkflowDiagram { WorkflowDiagram setSource(String source); + WorkflowDiagram setTemplate(String template); + String getSvgDiagram() throws Exception; WorkflowDiagram showLegend(boolean showLegend); diff --git a/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java b/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java index 5a2d4028..1fb5e656 100644 --- a/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java +++ b/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java @@ -26,9 +26,14 @@ public class WorkflowDiagramImpl implements WorkflowDiagram { + public static final String DEFAULT_TEMPLATE = "workflow-template"; + @SuppressWarnings("unused") private String source; + @SuppressWarnings("unused") + private String template = DEFAULT_TEMPLATE; + private Workflow workflow; private boolean showLegend = false; @@ -46,12 +51,18 @@ public WorkflowDiagram setSource(String source) { return this; } + @Override + public WorkflowDiagram setTemplate(String template) { + this.template = template; + return this; + } + @Override public String getSvgDiagram() throws Exception { if (workflow == null) { throw new IllegalAccessException("Unable to get diagram - no workflow set."); } - String diagramSource = WorkflowToPlantuml.convert(workflow, showLegend); + String diagramSource = WorkflowToPlantuml.convert(template, workflow, showLegend); SourceStringReader reader = new SourceStringReader(diagramSource); final ByteArrayOutputStream os = new ByteArrayOutputStream(); reader.generateImage(os, new FileFormatOption(FileFormat.SVG)); diff --git a/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java b/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java index acc112b8..956bcbeb 100644 --- a/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java +++ b/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java @@ -22,11 +22,12 @@ import org.thymeleaf.context.Context; public class WorkflowToPlantuml { - public static String convert(Workflow workflow, boolean showLegend) { + + public static String convert(String template, Workflow workflow, boolean showLegend) { TemplateEngine plantUmlTemplateEngine = ThymeleafConfig.templateEngine; Context context = new Context(); context.setVariable("diagram", new WorkflowDiagramModel(workflow, showLegend)); - return plantUmlTemplateEngine.process("workflow-template", context); + return plantUmlTemplateEngine.process(template, context); } } diff --git a/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java b/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java new file mode 100644 index 00000000..530d4b8c --- /dev/null +++ b/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.diagram.test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.serverlessworkflow.api.Workflow; +import io.serverlessworkflow.api.interfaces.WorkflowDiagram; +import io.serverlessworkflow.diagram.WorkflowDiagramImpl; +import io.serverlessworkflow.diagram.test.utils.DiagramTestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class CustomTemplateWorkflowDiagramTest { + + @ParameterizedTest + @ValueSource(strings = {"/examples/applicantrequest.json", "/examples/applicantrequest.yml"}) + public void testSpecExamplesParsing(String workflowLocation) throws Exception { + + Workflow workflow = Workflow.fromSource(DiagramTestUtils.readWorkflowFile(workflowLocation)); + + assertNotNull(workflow); + assertNotNull(workflow.getId()); + assertNotNull(workflow.getName()); + assertNotNull(workflow.getStates()); + + WorkflowDiagram workflowDiagram = + new WorkflowDiagramImpl().setWorkflow(workflow).setTemplate("custom-template"); + + String diagramSVG = workflowDiagram.getSvgDiagram(); + + Assertions.assertNotNull(diagramSVG); + // custom template uses #0000FF as start node color + Assertions.assertTrue(diagramSVG.contains("#0000FF")); + } +} diff --git a/diagram/src/test/resources/templates/plantuml/custom-template.txt b/diagram/src/test/resources/templates/plantuml/custom-template.txt new file mode 100644 index 00000000..c1794f55 --- /dev/null +++ b/diagram/src/test/resources/templates/plantuml/custom-template.txt @@ -0,0 +1,46 @@ +@startuml +skinparam backgroundColor White +skinparam legendBackgroundColor White +skinparam legendBorderColor White +skinparam state { + StartColor #0000FF + EndColor Orange + BackgroundColor GhostWhite + BackgroundColor<< workflow >> White + BorderColor Black + ArrowColor Black + + BorderColor<< event >> #7fe5f0 + BorderColor<< operation >> #bada55 + BorderColor<< switch >> #92a0f2 + BorderColor<< sleep >> #b83b5e + BorderColor<< parallel >> #6a2c70 + BorderColor<< inject >> #1e5f74 + BorderColor<< foreach >> #931a25 + BorderColor<< callback >> #ffcb8e +} +state "[(${diagram.title})]" as workflow << workflow >> { + +[# th:each="stateDef : ${diagram.modelStateDefs}" ] +[(${stateDef.toString()})] +[/] + +[# th:each="state : ${diagram.modelStates}" ] +[(${state.toString()})] +[/] + +[# th:each="connection : ${diagram.modelConnections}" ] +[(${connection.toString()})] +[/] + +} + +[# th:if="${diagram.showLegend}" ] +legend center +State Types and Border Colors: +| Event | Operation | Switch | Sleep | Parallel | Inject | ForEach | CallBack | +|<#7fe5f0>|<#bada55>|<#92a0f2>|<#b83b5e>|<#6a2c70>|<#1e5f74>|<#931a25>|<#ffcb8e>| +endlegend +[/] + +@enduml \ No newline at end of file