Skip to content

Commit 2878293

Browse files
committed
Introduce input, output methods to FuncDSL
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
1 parent a1e3d37 commit 2878293

2 files changed

Lines changed: 266 additions & 0 deletions

File tree

experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,4 +1322,109 @@ public static FuncCallHttpStep post(
13221322

13231323
return http(name).POST().endpoint(endpoint, auth).body(body);
13241324
}
1325+
1326+
/**
1327+
* Extracts and deserializes the workflow input data into the specified type from a workflow
1328+
* context.
1329+
*
1330+
* <p>This utility method provides type-safe access to the workflow's initial input.
1331+
*
1332+
* <p>Use this method when you have access to the {@link WorkflowContextData} and need to retrieve
1333+
* the original input that was provided when the workflow instance was started.
1334+
*
1335+
* <p><b>Usage Example:</b>
1336+
*
1337+
* <pre>{@code
1338+
* inputFrom((object, WorkflowContextData workflowContext) -> {
1339+
* OrderRequest order = input(workflowContext, OrderRequest.class);
1340+
* return new Input(order);
1341+
* });
1342+
* }</pre>
1343+
*
1344+
* @param <T> the type to deserialize the input into
1345+
* @param context the workflow context containing instance data and input
1346+
* @param inputClass the class object representing the target type for deserialization
1347+
* @return the deserialized workflow input object of type T
1348+
*/
1349+
public static <T> T input(WorkflowContextData context, Class<T> inputClass) {
1350+
return context
1351+
.instanceData()
1352+
.input()
1353+
.as(inputClass)
1354+
.orElseThrow(
1355+
() ->
1356+
new IllegalStateException(
1357+
"Workflow input is missing or cannot be deserialized into type "
1358+
+ inputClass.getName()
1359+
+ " when calling FuncDSL.input(WorkflowContextData, Class<T>)."));
1360+
}
1361+
1362+
/**
1363+
* Extracts and deserializes the task input data into the specified type from a task context.
1364+
*
1365+
* <p>This utility method provides type-safe access to a task's input.
1366+
*
1367+
* <p>Use this method when you have access to the {@link TaskContextData} and need to retrieve the
1368+
* input provided to that task.
1369+
*
1370+
* <p><b>Usage Example:</b>
1371+
*
1372+
* <pre>{@code
1373+
* inputFrom((Object obj, TaskContextData taskContextData) -> {
1374+
* OrderRequest order = input(taskContextData, OrderRequest.class);
1375+
* return order;
1376+
* });
1377+
* }</pre>
1378+
*
1379+
* @param <T> the type to deserialize the input into
1380+
* @param taskContextData the task context from which to retrieve the task input
1381+
* @param inputClass the class object representing the target type for deserialization
1382+
* @return the deserialized task input object of type T
1383+
*/
1384+
public static <T> T input(TaskContextData taskContextData, Class<T> inputClass) {
1385+
return taskContextData
1386+
.input()
1387+
.as(inputClass)
1388+
.orElseThrow(
1389+
() ->
1390+
new IllegalStateException(
1391+
"Workflow input is missing or cannot be deserialized into type "
1392+
+ inputClass.getName()
1393+
+ " when calling FuncDSL.input(TaskContextData, Class<T>)."));
1394+
}
1395+
1396+
/**
1397+
* Extracts and deserializes the output data from a task into the specified type.
1398+
*
1399+
* <p>This utility method provides type-safe access to a task's output.
1400+
*
1401+
* <p>Use this method when you need to access the result/output produced by a task execution. This
1402+
* is particularly useful in subsequent tasks that need to process or transform the output of a
1403+
* previous task in the workflow.
1404+
*
1405+
* <p><b>Usage Example:</b>
1406+
*
1407+
* <pre>{@code
1408+
* .exportAs((object, workflowContext, taskContextData) -> {
1409+
* Long output = output(taskContextData, Long.class);
1410+
* return output * 2;
1411+
* })
1412+
* }</pre>
1413+
*
1414+
* @param <T> the type to deserialize the task output into
1415+
* @param taskContextData the task context containing the output data
1416+
* @param outputClass the class object representing the target type for deserialization
1417+
* @return the deserialized task output object of type T
1418+
*/
1419+
public static <T> T output(TaskContextData taskContextData, Class<T> outputClass) {
1420+
return taskContextData
1421+
.output()
1422+
.as(outputClass)
1423+
.orElseThrow(
1424+
() ->
1425+
new IllegalStateException(
1426+
"Task output is missing or cannot be deserialized into type "
1427+
+ outputClass.getName()
1428+
+ " when calling FuncDSL.output(TaskContextData, Class<T>)."));
1429+
}
13251430
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverless.workflow.impl.executors.func;
17+
18+
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.function;
19+
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.input;
20+
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.output;
21+
22+
import io.serverlessworkflow.api.types.Workflow;
23+
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;
24+
import io.serverlessworkflow.impl.WorkflowApplication;
25+
import io.serverlessworkflow.impl.WorkflowDefinition;
26+
import io.serverlessworkflow.impl.WorkflowModel;
27+
import org.assertj.core.api.SoftAssertions;
28+
import org.junit.jupiter.api.Test;
29+
30+
public class FuncDSLDataFlowTransformationHelpersTest {
31+
32+
@Test
33+
void test_input_with_inputFrom() {
34+
35+
SoftAssertions softly = new SoftAssertions();
36+
37+
Workflow workflow =
38+
FuncWorkflowBuilder.workflow("reviewSubmissionWithModel")
39+
.tasks(
40+
function(
41+
"add5",
42+
(Long input) -> {
43+
softly.assertThat(input).isEqualTo(10L);
44+
return input + 5;
45+
},
46+
Long.class),
47+
function("returnEnriched", (Long enrichedValue) -> enrichedValue, Long.class)
48+
.inputFrom(
49+
(object, workflowContext) -> {
50+
softly.assertThat(object).isEqualTo(15L);
51+
Long input = input(workflowContext, Long.class);
52+
softly.assertThat(input).isEqualTo(10L);
53+
return object + input;
54+
},
55+
Long.class))
56+
.build();
57+
58+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
59+
WorkflowDefinition def = app.workflowDefinition(workflow);
60+
WorkflowModel model = def.instance(10L).start().join();
61+
Number number = model.asNumber().orElseThrow();
62+
softly.assertThat(number.longValue()).isEqualTo(25L);
63+
}
64+
65+
softly.assertAll();
66+
}
67+
68+
@Test
69+
void test_input_with_outputAs() {
70+
71+
SoftAssertions softly = new SoftAssertions();
72+
73+
Workflow workflow =
74+
FuncWorkflowBuilder.workflow("enrichOutputWithModelTest")
75+
.tasks(
76+
function(
77+
"add5",
78+
(Long input) -> {
79+
softly.assertThat(input).isEqualTo(10L);
80+
return input + 5;
81+
},
82+
Long.class)
83+
.outputAs(
84+
(object, workflowContext, taskContextData) -> {
85+
softly.assertThat(object).isEqualTo(15L);
86+
Long input = input(workflowContext, Long.class);
87+
softly.assertThat(input).isEqualTo(10L);
88+
return input + object;
89+
},
90+
Long.class))
91+
.build();
92+
93+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
94+
WorkflowDefinition def = app.workflowDefinition(workflow);
95+
96+
WorkflowModel model = def.instance(10L).start().join();
97+
Number number = model.asNumber().orElseThrow();
98+
99+
softly.assertThat(number.longValue()).isEqualTo(25L);
100+
}
101+
102+
softly.assertAll();
103+
}
104+
105+
@Test
106+
void test_output_with_exportAs() {
107+
108+
SoftAssertions softly = new SoftAssertions();
109+
110+
Workflow workflow =
111+
FuncWorkflowBuilder.workflow("enrichOutputWithInputTest")
112+
.tasks(
113+
function(
114+
"add5",
115+
(Long input) -> {
116+
softly.assertThat(input).isEqualTo(10L);
117+
return input + 5;
118+
},
119+
Long.class)
120+
.exportAs(
121+
(object, workflowContext, taskContextData) -> {
122+
Long taskOutput = output(taskContextData, Long.class);
123+
softly.assertThat(taskOutput).isEqualTo(15L);
124+
return taskOutput * 2;
125+
}))
126+
.build();
127+
128+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
129+
WorkflowDefinition def = app.workflowDefinition(workflow);
130+
WorkflowModel model = def.instance(10L).start().join();
131+
Number number = model.asNumber().orElseThrow();
132+
softly.assertThat(number.longValue()).isEqualTo(15L);
133+
}
134+
135+
softly.assertAll();
136+
}
137+
138+
@Test
139+
void test_input_with_inputFrom_fluent_way() {
140+
SoftAssertions softly = new SoftAssertions();
141+
142+
Workflow workflow =
143+
FuncWorkflowBuilder.workflow("enrichOutputWithInputTest")
144+
.tasks(
145+
function("sumFive", (Long input) -> input + 5, Long.class)
146+
.inputFrom(
147+
(object, workflowContext, taskContextData) ->
148+
input(taskContextData, Long.class) * 2))
149+
.build();
150+
151+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
152+
WorkflowDefinition def = app.workflowDefinition(workflow);
153+
WorkflowModel model = def.instance(10L).start().join();
154+
Number number = model.asNumber().orElseThrow();
155+
156+
softly.assertThat(number.longValue()).isEqualTo(25L);
157+
}
158+
159+
softly.assertAll();
160+
}
161+
}

0 commit comments

Comments
 (0)