Skip to content

Commit 392a3c5

Browse files
Improve WaitForStateCompletion API with strongly typing experience, and rename getSimpleWorkflowResultWithWait to waitForWorkflowCompletion (#216)
1 parent 08a5d25 commit 392a3c5

File tree

5 files changed

+147
-13
lines changed

5 files changed

+147
-13
lines changed

Diff for: src/main/java/io/iworkflow/core/Client.java

+86-10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.iworkflow.core.mapper.StateMovementMapper;
44
import io.iworkflow.core.persistence.PersistenceOptions;
5+
import io.iworkflow.gen.models.ErrorSubStatus;
56
import io.iworkflow.gen.models.ExecuteApiFailurePolicy;
67
import io.iworkflow.gen.models.KeyValue;
78
import io.iworkflow.gen.models.SearchAttribute;
@@ -251,16 +252,38 @@ private List<SearchAttribute> convertToSearchAttributeList(final Map<String, Sea
251252
}
252253

253254
/**
255+
* A long poll API to wait for the workflow completion
256+
* If the workflow is not COMPLETED, throw the {@link WorkflowUncompletedException}.
257+
*
258+
* @param workflowId required, the workflowId
259+
*/
260+
public void waitForWorkflowCompletion(
261+
final String workflowId) {
262+
this.getSimpleWorkflowResultWithWait(Void.class, workflowId);
263+
}
264+
265+
/**
266+
* A long poll API to wait for the workflow completion
254267
* For most cases, a workflow only has one result(one completion state).
255268
* Use this API to retrieve the output of the state with waiting for the workflow to complete.
256-
* If the workflow is not COMPLETED, throw the {@link feign.FeignException.FeignClientException}.
269+
* If the workflow is not COMPLETED, throw the {@link WorkflowUncompletedException}.
257270
*
258271
* @param valueClass required, the type class of the output
259272
* @param workflowId required, the workflowId
260-
* @param workflowRunId optional, can be empty
261273
* @param <T> type of the output
262274
* @return the output result
263275
*/
276+
public <T> T waitForWorkflowCompletion(
277+
final Class<T> valueClass,
278+
final String workflowId) {
279+
return this.getSimpleWorkflowResultWithWait(valueClass, workflowId);
280+
}
281+
282+
/**
283+
* Use {@link #waitForWorkflowCompletion(Class, String)} instead
284+
* It's just a renaming.
285+
*/
286+
@Deprecated
264287
public <T> T getSimpleWorkflowResultWithWait(
265288
final Class<T> valueClass,
266289
final String workflowId,
@@ -269,15 +292,10 @@ public <T> T getSimpleWorkflowResultWithWait(
269292
}
270293

271294
/**
272-
* For most cases, a workflow only has one result(one completion state).
273-
* Use this API to retrieve the output of the state with waiting for the workflow to complete.
274-
* If the workflow is not COMPLETED, throw the {@link feign.FeignException.FeignClientException}.
275-
*
276-
* @param valueClass required, the type class of the output
277-
* @param workflowId required, the workflowId
278-
* @param <T> type of the output
279-
* @return the output result
295+
* Use {@link #waitForWorkflowCompletion(Class, String)} instead
296+
* It's just a renaming.
280297
*/
298+
@Deprecated
281299
public <T> T getSimpleWorkflowResultWithWait(
282300
final Class<T> valueClass,
283301
final String workflowId) {
@@ -922,6 +940,64 @@ public void skipTimer(
922940
unregisteredClient.skipTimer(workflowId, workflowRunId, workflowStateId, stateExecutionNumber, timerCommandIndex);
923941
}
924942

943+
/**
944+
* A long poll API to wait for the completion of the state. This only waits for the first completion.
945+
* Note 1 The stateCompletion to wait for is needed to registered on starting workflow due to limitation in https://github.com/indeedeng/iwf/issues/349
946+
* Note 2 The max polling time is configured as clientOptions as the Feign client timeout(default to 10s)
947+
* If the state is not COMPLETED, throw the {@link ClientSideException} with the sub status of {@link ErrorSubStatus#LONG_POLL_TIME_OUT_SUB_STATUS}
948+
* @param workflowId the workflowId
949+
* @param stateClass the state class.
950+
*/
951+
public void waitForStateExecutionCompletion(
952+
final String workflowId,
953+
final Class<? extends WorkflowState> stateClass) {
954+
this.waitForStateExecutionCompletion(Void.class, workflowId, stateClass, 1);
955+
}
956+
957+
/**
958+
* A long poll API to wait for the completion of the state. This only waits for the first completion.
959+
* Note 1 The stateCompletion to wait for is needed to registered on starting workflow due to limitation in https://github.com/indeedeng/iwf/issues/349
960+
* Note 2 The max polling time is configured as clientOptions as the Feign client timeout(default to 10s)
961+
* If the state is not COMPLETED, throw the {@link ClientSideException} with the sub status of {@link ErrorSubStatus#LONG_POLL_TIME_OUT_SUB_STATUS}
962+
* @param valueClass the result of the state completion. Could be Void if not interested
963+
* @param workflowId the workflowId
964+
* @param stateClass the state class.
965+
* @return the result of the state completion
966+
* @param <T> the result type of the state completion
967+
*/
968+
public <T> T waitForStateExecutionCompletion(
969+
final Class<T> valueClass,
970+
final String workflowId,
971+
final Class<? extends WorkflowState> stateClass) {
972+
return this.waitForStateExecutionCompletion(valueClass, workflowId, stateClass, 1);
973+
}
974+
975+
/**
976+
* A long poll API to wait for the completion of the state. This only waits for the first completion.
977+
* Note 1 The stateCompletion and stateExecutionNumber to wait for must be registered on starting workflow due to limitation in https://github.com/indeedeng/iwf/issues/349
978+
* Note 2 The max polling time is configured as clientOptions as the Feign client timeout(default to 10s)
979+
* If the state is not COMPLETED, throw the {@link ClientSideException} with the sub status of {@link ErrorSubStatus#LONG_POLL_TIME_OUT_SUB_STATUS}
980+
* @param valueClass the result of the state completion. Could be Void if not interested
981+
* @param workflowId the workflowId
982+
* @param stateClass the state class
983+
* @param stateExecutionNumber the state execution number. E.g. if it's 2, it means the 2nd execution of the state
984+
* @return the result of the state completion
985+
* @param <T> the result type of the state completion
986+
*/
987+
public <T> T waitForStateExecutionCompletion(
988+
final Class<T> valueClass,
989+
final String workflowId,
990+
final Class<? extends WorkflowState> stateClass,
991+
final int stateExecutionNumber) {
992+
final String stateExecutionId= WorkflowState.getStateExecutionId(stateClass, stateExecutionNumber);
993+
return unregisteredClient.waitForStateExecutionCompletion(valueClass, workflowId, stateExecutionId);
994+
}
995+
996+
997+
/**
998+
* This method is deprecated, use the method with stateClass instead for strongly typing experience
999+
*/
1000+
@Deprecated
9251001
public <T> T waitForStateExecutionCompletion(
9261002
final Class<T> valueClass,
9271003
final String workflowId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.iworkflow.core;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* This class is for extending {@link ImmutableWorkflowOptions.Builder} to provide a
7+
* better experience with strongly typing.
8+
*/
9+
public class WorkflowOptionBuilderExtension {
10+
private ImmutableWorkflowOptions.Builder builder = ImmutableWorkflowOptions.builder();
11+
12+
/**
13+
* Add a state to wait for completion. This only waiting for the first completion of the state
14+
* @param states The states to wait for completion. O
15+
* @return The builder.
16+
*/
17+
public WorkflowOptionBuilderExtension WaitForCompletionStates(Class<? extends WorkflowState> ...states) {
18+
Arrays.stream(states).forEach(
19+
state -> builder.addWaitForCompletionStateExecutionIds(
20+
WorkflowState.getStateExecutionId(state,1)
21+
));
22+
return this;
23+
}
24+
25+
/**
26+
* Add a state to wait for completion. This can wait for any times completion of the state
27+
* @param state The state to wait for completion.
28+
* @param number The number of the state completion to wait for. E.g. when it's 2, it's waiting for the second completion of the state.
29+
* @return The builder.
30+
*/
31+
public WorkflowOptionBuilderExtension WaitForCompletionStateWithNumber(Class<? extends WorkflowState> state, int number) {
32+
builder.addWaitForCompletionStateExecutionIds(
33+
WorkflowState.getStateExecutionId(state, number)
34+
);
35+
return this;
36+
}
37+
38+
public ImmutableWorkflowOptions.Builder getBuilder() {
39+
return builder;
40+
}
41+
}

Diff for: src/main/java/io/iworkflow/core/WorkflowOptions.java

+8
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,12 @@ public abstract class WorkflowOptions {
2222
public abstract Optional<WorkflowConfig> getWorkflowConfigOverride();
2323

2424
public abstract List<String> getWaitForCompletionStateExecutionIds();
25+
26+
public static WorkflowOptionBuilderExtension extendedBuilder() {
27+
return new WorkflowOptionBuilderExtension();
28+
}
29+
30+
public static ImmutableWorkflowOptions.Builder basicBuilder() {
31+
return ImmutableWorkflowOptions.builder();
32+
}
2533
}

Diff for: src/main/java/io/iworkflow/core/WorkflowState.java

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ static boolean shouldSkipWaitUntil(final WorkflowState state) {
112112
}
113113
return false;
114114
}
115+
116+
static String getStateExecutionId(Class<? extends WorkflowState> state, int number) {
117+
return String.format("%s-%d", state.getSimpleName(), number);
118+
}
115119
}
116120

117121

Diff for: src/test/java/io/iworkflow/integ/TimerTest.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import io.iworkflow.core.Client;
44
import io.iworkflow.core.ClientOptions;
55
import io.iworkflow.core.ImmutableWorkflowOptions;
6+
import io.iworkflow.core.WorkflowOptionBuilderExtension;
7+
import io.iworkflow.core.WorkflowOptions;
68
import io.iworkflow.integ.timer.BasicTimerWorkflow;
9+
import io.iworkflow.integ.timer.BasicTimerWorkflowState1;
710
import io.iworkflow.spring.TestSingletonWorkerService;
811
import io.iworkflow.spring.controller.WorkflowRegistry;
912
import org.junit.jupiter.api.Assertions;
@@ -28,10 +31,12 @@ public void testBasicTimerWorkflow() throws InterruptedException {
2831

2932
client.startWorkflow(
3033
BasicTimerWorkflow.class, wfId, 10, input,
31-
ImmutableWorkflowOptions.builder().addWaitForCompletionStateExecutionIds("BasicTimerWorkflowState1-1").build());
34+
WorkflowOptions.extendedBuilder()
35+
.WaitForCompletionStates(BasicTimerWorkflowState1.class)
36+
.getBuilder().build());
3237

33-
client.waitForStateExecutionCompletion(Void.class, wfId, "BasicTimerWorkflowState1-1");
34-
client.getSimpleWorkflowResultWithWait(Integer.class, wfId);
38+
client.waitForStateExecutionCompletion(wfId, BasicTimerWorkflowState1.class);
39+
client.waitForWorkflowCompletion(wfId);
3540
final long elapsed = System.currentTimeMillis() - startTs;
3641
Assertions.assertTrue(elapsed >= 4000 && elapsed <= 7000, String.format("actual duration: %d", elapsed));
3742
}

0 commit comments

Comments
 (0)