Skip to content

Commit

Permalink
IWF-101: Use different structures for WorkflowStateOptions (#283)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevo89519 authored Dec 19, 2024
1 parent 055f283 commit 8d03c15
Show file tree
Hide file tree
Showing 20 changed files with 483 additions and 197 deletions.
7 changes: 4 additions & 3 deletions src/main/java/io/iworkflow/core/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.iworkflow.core.exceptions.NoRunningWorkflowException;
import io.iworkflow.core.exceptions.WorkflowAlreadyStartedException;
import io.iworkflow.core.exceptions.WorkflowNotExistsException;
import io.iworkflow.core.mapper.StateMovementMapper;
import io.iworkflow.core.persistence.PersistenceOptions;
import io.iworkflow.gen.models.ErrorSubStatus;
import io.iworkflow.gen.models.KeyValue;
Expand Down Expand Up @@ -32,6 +31,8 @@
import java.util.stream.Collectors;

import static io.iworkflow.core.WorkflowState.shouldSkipWaitUntil;
import static io.iworkflow.core.mapper.StateMovementMapper.autoFillFailureProceedingStateOptions;
import static io.iworkflow.core.mapper.StateMovementMapper.toIdlWorkflowStateOptionsWithValidation;

public class Client {
private final Registry registry;
Expand Down Expand Up @@ -172,7 +173,7 @@ public String startWorkflow(
throw new WorkflowDefinitionException(String.format("input cannot be assigned to the starting state, input type: %s, starting state input type: %s", input.getClass(), registeredInputType));
}

WorkflowStateOptions stateOptions = StateMovementMapper.validateAndGetStateOptionsCopy(stateDef);
WorkflowStateOptions stateOptions = toIdlWorkflowStateOptionsWithValidation(stateDef);
if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
if (stateOptions == null) {
stateOptions = new WorkflowStateOptions().skipWaitUntil(true);
Expand All @@ -181,7 +182,7 @@ public String startWorkflow(
}
}

StateMovementMapper.autoFillFailureProceedingStateOptions(stateOptions, wfType, registry);
autoFillFailureProceedingStateOptions(stateOptions, wfType, registry);

if (stateOptions != null) {
unregisterWorkflowOptions.startStateOptions(stateOptions);
Expand Down
1 change: 0 additions & 1 deletion src/main/java/io/iworkflow/core/StateDecision.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.iworkflow.core;

import io.iworkflow.gen.models.WorkflowConditionalCloseType;
import io.iworkflow.gen.models.WorkflowStateOptions;
import org.immutables.value.Value;

import java.util.ArrayList;
Expand Down
1 change: 0 additions & 1 deletion src/main/java/io/iworkflow/core/StateMovement.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.iworkflow.core;

import io.iworkflow.gen.models.WorkflowStateOptions;
import org.immutables.value.Value;

import java.util.Optional;
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/io/iworkflow/core/WorkflowState.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.iworkflow.core.command.CommandResults;
import io.iworkflow.core.communication.Communication;
import io.iworkflow.core.persistence.Persistence;
import io.iworkflow.gen.models.WorkflowStateOptions;

import java.lang.reflect.Method;

Expand Down Expand Up @@ -92,7 +91,6 @@ default String getStateId() {
* - MaxInternalSeconds:100
* - MaximumAttempts: 0
* - BackoffCoefficient: 2
* See {@link WorkflowStateOptionsExtension} for some helpers to build the options.
* @return the optional options
*/
default WorkflowStateOptions getStateOptions() {
Expand Down
351 changes: 351 additions & 0 deletions src/main/java/io/iworkflow/core/WorkflowStateOptions.java

Large diffs are not rendered by default.

121 changes: 0 additions & 121 deletions src/main/java/io/iworkflow/core/WorkflowStateOptionsExtension.java

This file was deleted.

110 changes: 87 additions & 23 deletions src/main/java/io/iworkflow/core/mapper/StateMovementMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import static io.iworkflow.core.StateMovement.RESERVED_STATE_ID_PREFIX;
import static io.iworkflow.core.WorkflowState.shouldSkipWaitUntil;
import static io.iworkflow.core.WorkflowStateOptionsExtension.deepCopyStateOptions;

public class StateMovementMapper {

Expand All @@ -31,10 +30,10 @@ public static StateMovement toGenerated(final io.iworkflow.core.StateMovement st
// Try to get the overrode stateOptions, if it's null, get the stateOptions from stateDef
WorkflowStateOptions stateOptions;
if (stateMovement.getStateOptionsOverride().isPresent()) {
// Always deep copy the state options so we don't modify the original
stateOptions = deepCopyStateOptions(stateMovement.getStateOptionsOverride().get());
stateOptions =
toIdlWorkflowStateOptionsWithValidation(stateMovement.getStateOptionsOverride().get(), stateMovement.getStateId());
} else {
stateOptions = StateMovementMapper.validateAndGetStateOptionsCopy(stateDef);
stateOptions = toIdlWorkflowStateOptionsWithValidation(stateDef);
}

if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
Expand Down Expand Up @@ -66,7 +65,7 @@ public static void autoFillFailureProceedingStateOptions(WorkflowStateOptions st
// fill the state options for the proceeding state
String proceedStateId = stateOptions.getExecuteApiFailureProceedStateId();
final StateDef proceedStatDef = registry.getWorkflowState(workflowType, proceedStateId);
WorkflowStateOptions proceedStateOptions = StateMovementMapper.validateAndGetStateOptionsCopy(proceedStatDef);
WorkflowStateOptions proceedStateOptions = toIdlWorkflowStateOptionsWithValidation(proceedStatDef);
if (proceedStateOptions != null &&
proceedStateOptions.getExecuteApiFailurePolicy() == ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE) {
throw new WorkflowDefinitionException("nested failure handling is not supported. You cannot set a failure proceeding state on top of another failure proceeding state.");
Expand All @@ -84,35 +83,100 @@ public static void autoFillFailureProceedingStateOptions(WorkflowStateOptions st
}
}

public static WorkflowStateOptions validateAndGetStateOptionsCopy(final StateDef stateDef){
final WorkflowState state = stateDef.getWorkflowState();
// Always deep copy the state options so we don't modify the original
WorkflowStateOptions stateOptions = deepCopyStateOptions(state.getStateOptions());
if (stateOptions == null){
return null;
}
if(stateOptions.getExecuteApiFailurePolicy() == ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE){
/**
* Validates if the required fields are present when the {@link WorkflowStateOptions} is configured to proceed on Execute
* API Failure.
* @param stateOptions the state options to validate.
* @param stateId the unique ID of the state
*/
private static void validateExecuteApiFailurePolicy(WorkflowStateOptions stateOptions, String stateId) {
// Validate required fields if Execute failure policy is configured to proceed
if (stateOptions.getExecuteApiFailurePolicy() == ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE) {
// retry policy must be set
if(stateOptions.getExecuteApiRetryPolicy() == null){
throw new WorkflowDefinitionException("RetryPolicy must be set for the execute "+state.getStateId());
if (stateOptions.getExecuteApiRetryPolicy() == null) {
throw new WorkflowDefinitionException("RetryPolicy must be set for the execute " + stateId);
}
final RetryPolicy policy = stateOptions.getExecuteApiRetryPolicy();
// either maximumAttempts or maximumAttemptsDurationSeconds must be set and greater than zero
if(policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null){
throw new WorkflowDefinitionException("Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the execute "+state.getStateId());
if (policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null) {
throw new WorkflowDefinitionException(
"Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the execute " + stateId);
}
}
if(stateOptions.getWaitUntilApiFailurePolicy() == WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE){
}

/**
* Validates if the required fields are present when the {@link WorkflowStateOptions} is configured to proceed on WaitUntil
* API Failure.
* @param stateOptions the state options to validate.
* @param stateId the unique ID of the state
*/
private static void validateWaitUntilApiFailurePolicy(WorkflowStateOptions stateOptions, String stateId) {
// Validate required fields if Wait Until failure policy is configured to proceed
if (stateOptions.getWaitUntilApiFailurePolicy() == WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE) {
// retry policy must be set
if(stateOptions.getWaitUntilApiRetryPolicy() == null){
throw new WorkflowDefinitionException("RetryPolicy must be set for the waitUntil "+state.getStateId());
if (stateOptions.getWaitUntilApiRetryPolicy() == null) {
throw new WorkflowDefinitionException("RetryPolicy must be set for the waitUntil " + stateId);
}
final RetryPolicy policy = stateOptions.getWaitUntilApiRetryPolicy();
// either maximumAttempts or maximumAttemptsDurationSeconds must be set and greater than zero
if(policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null){
throw new WorkflowDefinitionException("Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the waitUntil "+state.getStateId());
if (policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null) {
throw new WorkflowDefinitionException(
"Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the waitUntil " + stateId);
}
}
return stateOptions;
}

public static WorkflowStateOptions toIdlWorkflowStateOptionsWithValidation(final StateDef stateDef) {
final WorkflowState state = stateDef.getWorkflowState();
if (state.getStateOptions() == null) {
return null;
}

return toIdlWorkflowStateOptionsWithValidation(state.getStateOptions(), state.getStateId());
}

public static WorkflowStateOptions toIdlWorkflowStateOptionsWithValidation(
io.iworkflow.core.WorkflowStateOptions stateOptions,
String stateId) {
if (stateOptions == null) {
return null;
}

// Guarantee workflow state options copy is not holding references to the original by cloning object
stateOptions = stateOptions.clone();

final WorkflowStateOptions idlWorkflowStateOptions = new WorkflowStateOptions();

idlWorkflowStateOptions.setSearchAttributesLoadingPolicy(stateOptions.getSearchAttributesLoadingPolicy());
idlWorkflowStateOptions.setWaitUntilApiSearchAttributesLoadingPolicy(stateOptions.getWaitUntilApiSearchAttributesLoadingPolicy());
idlWorkflowStateOptions.setExecuteApiSearchAttributesLoadingPolicy(stateOptions.getExecuteApiSearchAttributesLoadingPolicy());
idlWorkflowStateOptions.setDataAttributesLoadingPolicy(stateOptions.getDataAttributesLoadingPolicy());
idlWorkflowStateOptions.setWaitUntilApiDataAttributesLoadingPolicy(stateOptions.getWaitUntilApiDataAttributesLoadingPolicy());
idlWorkflowStateOptions.setExecuteApiDataAttributesLoadingPolicy(stateOptions.getExecuteApiDataAttributesLoadingPolicy());
idlWorkflowStateOptions.setWaitUntilApiTimeoutSeconds(stateOptions.getWaitUntilApiTimeoutSeconds());
idlWorkflowStateOptions.setExecuteApiTimeoutSeconds(stateOptions.getExecuteApiTimeoutSeconds());
idlWorkflowStateOptions.setWaitUntilApiRetryPolicy(stateOptions.getWaitUntilApiRetryPolicy());
idlWorkflowStateOptions.setExecuteApiRetryPolicy(stateOptions.getExecuteApiRetryPolicy());
if (stateOptions.getProceedToExecuteWhenWaitUntilRetryExhausted() != null) {
idlWorkflowStateOptions.setWaitUntilApiFailurePolicy(Boolean.TRUE.equals(stateOptions.getProceedToExecuteWhenWaitUntilRetryExhausted())
? WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE
: WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE);
}
if (stateOptions.getProceedToStateWhenExecuteRetryExhausted() != null) {
idlWorkflowStateOptions.setExecuteApiFailurePolicy(ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE);
idlWorkflowStateOptions.setExecuteApiFailureProceedStateId(stateOptions.getProceedToStateWhenExecuteRetryExhausted()
.getSimpleName());
}
if (stateOptions.getProceedToStateWhenExecuteRetryExhaustedStateOptions() != null) {
idlWorkflowStateOptions.setExecuteApiFailureProceedStateOptions(toIdlWorkflowStateOptionsWithValidation(
stateOptions.getProceedToStateWhenExecuteRetryExhaustedStateOptions(),
stateId));
}

validateExecuteApiFailurePolicy(idlWorkflowStateOptions, stateId);
validateWaitUntilApiFailurePolicy(idlWorkflowStateOptions, stateId);

return idlWorkflowStateOptions;
}
}
Loading

0 comments on commit 8d03c15

Please sign in to comment.