diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java index 739fe1e79..d77a837c7 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java @@ -124,4 +124,20 @@ public T timeout(Consumer timeoutConsumer) { this.task.setTimeout(new TaskTimeout().withTaskTimeoutDefinition(timeoutBuilder.build())); return self(); } + + /** + * Configures additional information about this task, such as descriptions meant for UI + * visualization. Parallel to the `metadata` property in the Spec. + * + * @param metadataConsumer consumer used to populate the task metadata + * @see DSL + * Reference - Task + */ + public T metadata(Consumer metadataConsumer) { + final TaskMetadataBuilder metadataBuilder = new TaskMetadataBuilder(); + metadataConsumer.accept(metadataBuilder); + this.task.setMetadata(metadataBuilder.build()); + return self(); + } } diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskMetadataBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskMetadataBuilder.java new file mode 100644 index 000000000..b55bde903 --- /dev/null +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskMetadataBuilder.java @@ -0,0 +1,57 @@ +/* + * 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.fluent.spec; + +import io.serverlessworkflow.api.types.TaskMetadata; + +/** + * Fluent builder for {@link TaskMetadata}, which holds additional information about a task (for + * example, descriptions meant for UI visualization). + */ +public final class TaskMetadataBuilder { + + public static final String DESCRIPTION = "description"; + + private final TaskMetadata metadata; + + TaskMetadataBuilder() { + this.metadata = new TaskMetadata(); + } + + /** + * Adds an arbitrary metadata entry. + * + * @param key metadata key + * @param value metadata value + */ + public TaskMetadataBuilder put(final String key, final Object value) { + this.metadata.withAdditionalProperty(key, value); + return this; + } + + /** + * Convenience method for the common {@value #DESCRIPTION} metadata entry. + * + * @param description human readable description of the task + */ + public TaskMetadataBuilder description(final String description) { + return put(DESCRIPTION, description); + } + + public TaskMetadata build() { + return this.metadata; + } +} diff --git a/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java index 0be70ded2..cdc17c765 100644 --- a/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java +++ b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java @@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -57,6 +58,7 @@ import io.serverlessworkflow.api.types.RunTaskConfiguration; import io.serverlessworkflow.api.types.SetTask; import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TaskMetadata; import io.serverlessworkflow.api.types.TryTask; import io.serverlessworkflow.api.types.TryTaskCatch; import io.serverlessworkflow.api.types.Use; @@ -97,6 +99,40 @@ void testWorkflowDocumentExplicit() { assertEquals("1.2.3", doc.getVersion()); } + @Test + void testTaskMetadata() { + Workflow wf = + WorkflowBuilder.workflow("flowWithTaskMetadata") + .tasks( + d -> + d.set( + "described", + s -> + s.expr("$.value = 'test'") + .metadata( + m -> + m.description("Exit when counter reaches 10") + .put("exitConditionDescription", "counter == 10") + .put("weight", 42))) + .set("plain", s -> s.expr("$.other = 'value'"))) + .build(); + + List items = wf.getDo(); + assertEquals(2, items.size(), "There should be two tasks"); + + SetTask described = items.get(0).getTask().getSetTask(); + TaskMetadata metadata = described.getMetadata(); + assertNotNull(metadata, "Task metadata should be present"); + Map props = metadata.getAdditionalProperties(); + assertEquals(3, props.size(), "Metadata should hold three entries"); + assertEquals("Exit when counter reaches 10", props.get(TaskMetadataBuilder.DESCRIPTION)); + assertEquals("counter == 10", props.get("exitConditionDescription")); + assertEquals(42, props.get("weight")); + + SetTask plain = items.get(1).getTask().getSetTask(); + assertNull(plain.getMetadata(), "Tasks without metadata() should not have metadata set"); + } + @Test void testUseAuthenticationsBasic() { Workflow wf =