Skip to content

Commit 4b9a780

Browse files
committed
Introduce enrich and enrichOutput FuncDSL methods
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
1 parent f95b082 commit 4b9a780

File tree

4 files changed

+694
-0
lines changed

4 files changed

+694
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.serverlessworkflow.fluent.func.dsl;
17+
18+
import io.serverlessworkflow.impl.WorkflowModel;
19+
20+
/**
21+
* A function that enriches a typed value by combining it with the root workflow input.
22+
*
23+
* <p>This is useful when you need to merge a task output or state value with the original workflow
24+
* input, where the value is typed and the root input is a {@link WorkflowModel}.
25+
*
26+
* @param <T> The type of the input value to be enriched
27+
* @param <R> The type of the enriched result
28+
*/
29+
@FunctionalInterface
30+
public interface EnrichWithModelBiFunction<T, R> {
31+
/**
32+
* Applies this function to enrich a typed value with the root workflow input.
33+
*
34+
* @param value the typed value to be enriched (for example a task output or state value)
35+
* @param rootInput the original workflow input as {@link WorkflowModel}
36+
* @return the enriched result combining both inputs
37+
*/
38+
R apply(T value, WorkflowModel rootInput);
39+
}

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import io.serverlessworkflow.fluent.spec.configurers.AuthenticationConfigurer;
3333
import io.serverlessworkflow.impl.TaskContextData;
3434
import io.serverlessworkflow.impl.WorkflowContextData;
35+
import io.serverlessworkflow.impl.WorkflowModel;
36+
import io.serverlessworkflow.impl.jackson.JsonUtils;
3537
import java.net.URI;
3638
import java.util.Collection;
3739
import java.util.List;
@@ -279,6 +281,123 @@ public static FuncPredicateEventConfigurer event(String type) {
279281
return OPS.event(type);
280282
}
281283

284+
/**
285+
* Create an input transformation that enriches the typed last state with the root workflow input
286+
* as WorkflowModel.
287+
*
288+
* <p>This is useful when you want to combine the last task output (with its actual type) with the
289+
* original workflow input as WorkflowModel.
290+
*
291+
* <p>Example usage:
292+
*
293+
* <pre>{@code
294+
* function("processData", (Long input) -> input + 5, Long.class),
295+
* function("combineData", (Long enrichedValue) -> enrichedValue, Long.class)
296+
* .inputFrom(
297+
* FuncDSL.enrichInput((Long lastState, WorkflowModel rootInputModel) -> {
298+
* Long rootInput = rootInputModel.as(Long.class).orElse(0L);
299+
* return lastState + rootInput;
300+
* }),
301+
* Long.class)
302+
* }</pre>
303+
*
304+
* @param fn the enrichment function that receives typed lastState and WorkflowModel rootInput
305+
* @param <T> the type of the last state
306+
* @param <R> the type of the enriched result
307+
* @return a JavaContextFunction that can be used with inputFrom
308+
*/
309+
public static <T, R> JavaContextFunction<T, R> enrichInput(EnrichWithModelBiFunction<T, R> fn) {
310+
return (lastState, workflowContext) -> {
311+
final WorkflowModel rootInput = workflowContext.instanceData().input();
312+
return fn.apply(lastState, rootInput);
313+
};
314+
}
315+
316+
/**
317+
* Create an input transformation that uses only the root workflow input as WorkflowModel.
318+
*
319+
* <p>This is useful when you want to transform the task input based solely on the original
320+
* workflow input, ignoring the last state.
321+
*
322+
* <p>Example usage:
323+
*
324+
* <pre>{@code
325+
* function("processData", (Long input) -> input * 2, Long.class)
326+
* .inputFrom(enrichInput(rootInput -> rootInput.asNumber().orElseThrow()))
327+
* }</pre>
328+
*
329+
* @param fn the function that receives the root workflow input and returns the enriched result
330+
* @param <R> the type of the enriched result
331+
* @return a JavaContextFunction that can be used with inputFrom
332+
*/
333+
public static <R> JavaContextFunction<Object, R> enrichInput(Function<WorkflowModel, R> fn) {
334+
return (lastState, workflowContext) -> {
335+
final WorkflowModel rootInput = workflowContext.instanceData().input();
336+
return fn.apply(rootInput);
337+
};
338+
}
339+
340+
/**
341+
* Create an output transformation that enriches the typed task output with the root workflow
342+
* input as WorkflowModel.
343+
*
344+
* <p>This is useful when you want to combine the task output (converted to a specific type) with
345+
* the original workflow input as WorkflowModel. The task output will be converted to the
346+
* specified type using Jackson conversion before being passed to the enrichment function.
347+
*
348+
* <p>Example usage:
349+
*
350+
* <pre>{@code
351+
* // Task returns Long, but we want to work with it as Integer in the enrichment function
352+
* function("processData", (Long input) -> input + 5, Long.class)
353+
* .outputAs(FuncDSL.enrichOutput((taskOutput, rootInputModel) -> {
354+
* // taskOutput is converted from Long to Integer
355+
* Long rootInput = rootInputModel.as(Long.class).orElse(0L);
356+
* return taskOutput + rootInput.intValue();
357+
* }, Integer.class))
358+
* }</pre>
359+
*
360+
* @param fn the enrichment function that receives typed taskOutput and WorkflowModel rootInput
361+
* @param taskOutputClass the target type to convert the task output to before passing it to the
362+
* enrichment function
363+
* @param <T> the target type for the task output conversion
364+
* @param <R> the type of the enriched result
365+
* @return a JavaContextFunction that can be used with outputAs
366+
*/
367+
public static <T, R> JavaContextFunction<T, R> enrichOutput(
368+
EnrichWithModelBiFunction<T, R> fn, Class<T> taskOutputClass) {
369+
return (taskOutput, workflowContext) -> {
370+
Objects.requireNonNull(taskOutputClass, "taskOutputClass must not be null");
371+
final WorkflowModel rootInput = workflowContext.instanceData().input();
372+
final T typedTaskOutput = JsonUtils.convertValue(taskOutput, taskOutputClass);
373+
return fn.apply(typedTaskOutput, rootInput);
374+
};
375+
}
376+
377+
/**
378+
* Create an output transformation that uses only the root workflow input as WorkflowModel.
379+
*
380+
* <p>This is useful when you want to transform the task output based solely on the original
381+
* workflow input, ignoring the actual task output.
382+
*
383+
* <p>Example usage:
384+
*
385+
* <pre>{@code
386+
* function("processData", (Long input) -> input * 2, Long.class)
387+
* .outputAs(FuncDSL.enrichOutput(rootInput -> rootInput.asNumber().orElseThrow()))
388+
* }</pre>
389+
*
390+
* @param fn the function that receives the root workflow input and returns the enriched result
391+
* @param <R> the type of the enriched result
392+
* @return a JavaContextFunction that can be used with outputAs
393+
*/
394+
public static <R> JavaContextFunction<Object, R> enrichOutput(Function<WorkflowModel, R> fn) {
395+
return (ignore, workflowContext) -> {
396+
final WorkflowModel rootInput = workflowContext.instanceData().input();
397+
return fn.apply(rootInput);
398+
};
399+
}
400+
282401
/**
283402
* Create a {@link FuncCallStep} that calls a simple Java {@link Function} with explicit input
284403
* type.

impl/test/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@
9494
<artifactId>grpc-netty</artifactId>
9595
<scope>test</scope>
9696
</dependency>
97+
<dependency>
98+
<groupId>io.serverlessworkflow</groupId>
99+
<artifactId>serverlessworkflow-experimental-fluent-func</artifactId>
100+
<scope>test</scope>
101+
<version>${project.version}</version>
102+
</dependency>
103+
<dependency>
104+
<groupId>io.serverlessworkflow</groupId>
105+
<artifactId>serverlessworkflow-experimental-lambda</artifactId>
106+
<scope>test</scope>
107+
<version>${project.version}</version>
108+
</dependency>
97109
</dependencies>
98110
<build>
99111
<resources>

0 commit comments

Comments
 (0)