All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.serverlessworkflow.utils.WorkflowUtils Maven / Gradle / Ivy

There is a newer version: 5.0.0.Final
Show newest version
/*
 * 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.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.serverlessworkflow.api.Workflow;
import io.serverlessworkflow.api.actions.Action;
import io.serverlessworkflow.api.branches.Branch;
import io.serverlessworkflow.api.defaultdef.DefaultConditionDefinition;
import io.serverlessworkflow.api.events.EventDefinition;
import io.serverlessworkflow.api.events.OnEvents;
import io.serverlessworkflow.api.functions.FunctionDefinition;
import io.serverlessworkflow.api.functions.FunctionRef;
import io.serverlessworkflow.api.interfaces.State;
import io.serverlessworkflow.api.start.Start;
import io.serverlessworkflow.api.states.*;
import io.serverlessworkflow.api.switchconditions.DataCondition;
import io.serverlessworkflow.api.switchconditions.EventCondition;
import java.util.*;
import java.util.stream.Collectors;

/** Provides common utility methods to provide most often needed answers from a workflow */
@SuppressWarnings("StreamToLoop")
public final class WorkflowUtils {
  private static final int DEFAULT_STARTING_STATE_POSITION = 0;
  private static final long DEFAULT_STATE_COUNT = 0;

  /**
   * Gets State matching Start state. If start is not present returns first state. If start is
   * present, returns the matching start State. If matching state is not present, returns null
   *
   * @param workflow workflow
   * @return {@code state} when present else returns {@code null}
   */
  public static State getStartingState(Workflow workflow) {
    if (!hasStates(workflow)) {
      return null;
    }

    Start start = workflow.getStart();
    if (start == null) {
      return workflow.getStates().get(DEFAULT_STARTING_STATE_POSITION);
    } else {
      Optional startingState =
          workflow.getStates().stream()
              .filter(state -> state.getName().equals(start.getStateName()))
              .findFirst();
      return startingState.orElse(null);
    }
  }

  /**
   * Gets List of States matching stateType
   *
   * @param workflow
   * @param stateType
   * @return {@code List}. Returns {@code null} when workflow is null.
   */
  public static List getStates(Workflow workflow, DefaultState.Type stateType) {
    if (!hasStates(workflow)) {
      return null;
    }

    return workflow.getStates().stream()
        .filter(state -> state.getType() == stateType)
        .collect(Collectors.toList());
  }

  /**
   * @return {@code List}. Returns {@code NULL}
   *     when workflow is null or when workflow does not contain events
   */
  public static List getDefinedConsumedEvents(Workflow workflow) {
    return getDefinedEvents(workflow, EventDefinition.Kind.CONSUMED);
  }

  /**
   * @return {@code List}. Returns {@code NULL}
   *     when workflow is null or when workflow does not contain events
   */
  public static List getDefinedProducedEvents(Workflow workflow) {
    return getDefinedEvents(workflow, EventDefinition.Kind.PRODUCED);
  }

  /**
   * Gets list of event definition matching eventKind
   *
   * @param workflow
   * @return {@code List}. Returns {@code NULL}
   *     when workflow is null or when workflow does not contain events
   */
  public static List getDefinedEvents(
      Workflow workflow, EventDefinition.Kind eventKind) {
    if (!hasEventDefs(workflow)) {
      return null;
    }

    List eventDefs = workflow.getEvents().getEventDefs();
    return eventDefs.stream()
        .filter(eventDef -> eventDef.getKind() == eventKind)
        .collect(Collectors.toList());
  }

  /** @return {@code int} Returns count of defined event count matching eventKind */
  public static int getDefinedEventsCount(Workflow workflow, EventDefinition.Kind eventKind) {
    List definedEvents = getDefinedEvents(workflow, eventKind);
    return definedEvents == null ? 0 : definedEvents.size();
  }

  /** @return {@code int} Returns count of Defined Consumed Event Count */
  public static int getDefinedConsumedEventsCount(Workflow workflow) {
    return getDefinedEventsCount(workflow, EventDefinition.Kind.CONSUMED);
  }

  /** @return {@code int} Returns count of Defined Produced Event Count */
  public static int getDefinedProducedEventsCount(Workflow workflow) {
    return getDefinedEventsCount(workflow, EventDefinition.Kind.PRODUCED);
  }

  /**
   * Gets Consumed Events of parent workflow Iterates through states in parent workflow and collects
   * all the ConsumedEvents. Sub Workflows of the Workflow are not considered for
   * getting Consumed Events
   *
   * @return Returns {@code List}
   */
  public static List getWorkflowConsumedEvents(Workflow workflow) {
    return getWorkflowEventDefinitions(workflow, EventDefinition.Kind.CONSUMED);
  }

  /**
   * Gets Produced Events of parent workflow Iterates through states in parent workflow and collects
   * all the ConsumedEvents. Sub Workflows of the Workflow are not considered for
   * getting Consumed Events
   *
   * @return Returns {@code List}
   */
  public static List getWorkflowProducedEvents(Workflow workflow) {
    return getWorkflowEventDefinitions(workflow, EventDefinition.Kind.PRODUCED);
  }

  /**
   * Gets Events of parent workflow matching {@code EventDefinition.Kind} Iterates through states in
   * parent workflow and collects all the events matching {@code EventDefinition.Kind} .
   *
   * @return Returns {@code List}
   */
  public static List getWorkflowEventDefinitions(
      Workflow workflow, EventDefinition.Kind eventKind) {
    if (!hasStates(workflow)) {
      return null;
    }

    List uniqueWorkflowEventsFromStates = getUniqueWorkflowEventsFromStates(workflow);
    List definedConsumedEvents = getDefinedEvents(workflow, eventKind);
    if (definedConsumedEvents == null) {
      return null;
    }
    return definedConsumedEvents.stream()
        .filter(definedEvent -> uniqueWorkflowEventsFromStates.contains(definedEvent.getName()))
        .collect(Collectors.toList());
  }

  /** Returns a list of unique event names from workflow states */
  public static List getUniqueWorkflowEventsFromStates(Workflow workflow) {
    List eventReferences = new ArrayList<>();

    for (State state : workflow.getStates()) {
      if (state instanceof SwitchState) {
        SwitchState switchState = (SwitchState) state;
        if (switchState.getEventConditions() != null) {
          switchState
              .getEventConditions()
              .forEach(eventCondition -> eventReferences.add(eventCondition.getEventRef()));
        }
      } else if (state instanceof CallbackState) {
        CallbackState callbackState = (CallbackState) state;
        if (callbackState.getEventRef() != null) eventReferences.add(callbackState.getEventRef());
        if (callbackState.getAction() != null && callbackState.getAction().getEventRef() != null) {
          eventReferences.addAll(getActionEvents(callbackState.getAction()));
        }
      } else if (state instanceof EventState) {
        EventState eventState = (EventState) state;
        if (eventState.getOnEvents() != null) {
          eventState
              .getOnEvents()
              .forEach(
                  onEvents -> {
                    eventReferences.addAll(onEvents.getEventRefs());
                    if (onEvents.getActions() != null) {
                      for (Action action : onEvents.getActions()) {
                        eventReferences.addAll(getActionEvents(action));
                      }
                    }
                  });
        }
      } else if (state instanceof OperationState) {
        OperationState operationState = (OperationState) state;
        if (operationState.getActions() != null) {
          for (Action action : operationState.getActions()) {
            eventReferences.addAll(getActionEvents(action));
          }
        }
      } else if (state instanceof ParallelState) {
        ParallelState parallelState = (ParallelState) state;
        if (parallelState.getBranches() != null) {
          for (Branch branch : parallelState.getBranches()) {
            if (branch.getActions() != null) {
              for (Action action : branch.getActions()) {
                eventReferences.addAll(getActionEvents(action));
              }
            }
          }
        }
      }
    }

    return eventReferences.stream().distinct().collect(Collectors.toList());
  }

  /**
   * @return Returns {@code int } Count of the workflow consumed events. Does not
   *     consider sub-workflows
   */
  public static int getWorkflowConsumedEventsCount(Workflow workflow) {
    List workflowConsumedEvents = getWorkflowConsumedEvents(workflow);
    return workflowConsumedEvents == null ? 0 : workflowConsumedEvents.size();
  }

  /**
   * @return Returns {@code int} Count of the workflow produced events. Does not
   *     consider sub-workflows in the count
   */
  public static int getWorkflowProducedEventsCount(Workflow workflow) {
    List workflowProducedEvents = getWorkflowProducedEvents(workflow);
    return workflowProducedEvents == null ? 0 : workflowProducedEvents.size();
  }

  /** @return Returns function definition for actions */
  public static FunctionDefinition getFunctionDefinitionsForAction(
      Workflow workflow, String action) {
    if (!hasFunctionDefs(workflow)) return null;
    FunctionRef functionRef = getFunctionRefFromAction(workflow, action);
    if (functionRef == null) return null;
    final Optional functionDefinition =
        workflow.getFunctions().getFunctionDefs().stream()
            .filter(functionDef -> functionDef.getName().equals(functionRef.getRefName()))
            .distinct()
            .findFirst();

    return functionDefinition.isPresent() ? functionDefinition.get() : null;
  }

  /** @return : Returns @{code List} which uses a function defintion */
  public static List getActionsForFunctionDefinition(
      Workflow workflow, String functionDefinitionName) {
    if (!hasFunctionDefs(workflow, functionDefinitionName)) return null;
    return getActionsWhichUsesFunctionDefinition(workflow, functionDefinitionName);
  }

  /**
   * Gets Num of State in the workflow does not consider child workflow
   *
   * @param workflow
   * @return
   */
  public static long getNumOfStates(Workflow workflow) {
    return hasStates(workflow) ? workflow.getStates().size() : DEFAULT_STATE_COUNT;
  }

  /**
   * Gets Num of States for State Type
   *
   * @param workflow
   * @param type
   * @return
   */
  public static long getNumOfStates(Workflow workflow, DefaultState.Type type) {
    return hasStates(workflow)
        ? workflow.getStates().stream().filter(state -> state.getType() == type).count()
        : DEFAULT_STATE_COUNT;
  }

  /**
   * Returns workflow state from provided name, or null if not found.
   *
   * @param workflow
   * @param name
   * @return
   */
  public static State getStateWithName(Workflow workflow, String name) {
    if (!hasStates(workflow)) {
      return null;
    }

    Optional state =
        workflow.getStates().stream().filter(s -> s.getName().equals(name)).findFirst();

    if (state.isPresent()) {
      return state.get();
    } else {
      return null;
    }
  }

  public static long getNumOfEndStates(Workflow workflow) {
    if (hasStates(workflow)) {
      long count = workflow.getStates().stream().filter(state -> state.getEnd() != null).count();
      List switchStates =
          workflow.getStates().stream()
              .filter(state -> state instanceof SwitchState)
              .collect(Collectors.toList());
      for (State state : switchStates) {
        SwitchState switchState = (SwitchState) state;
        List eventConditions = switchState.getEventConditions();
        if (eventConditions != null) {
          count =
              count
                  + eventConditions.stream()
                      .filter(eventCondition -> eventCondition.getEnd() != null)
                      .count();
        }
        List dataConditions = switchState.getDataConditions();
        if (dataConditions != null) {
          count =
              count
                  + dataConditions.stream()
                      .filter(dataCondition -> dataCondition.getEnd() != null)
                      .count();
        }
        DefaultConditionDefinition defaultCondition = switchState.getDefaultCondition();
        if (defaultCondition != null) {
          count = (defaultCondition.getEnd() != null) ? count + 1 : count;
        }
      }
      return count;
    } else {
      return DEFAULT_STATE_COUNT;
    }
  }

  public static List getActionsWhichUsesFunctionDefinition(
      Workflow workflow, String functionDefinitionName) {
    List actions = new ArrayList<>();
    for (State state : workflow.getStates()) {
      if (state instanceof EventState) {
        EventState eventState = (EventState) state;
        List onEvents = eventState.getOnEvents();
        if (onEvents != null) {
          for (OnEvents onEvent : onEvents) {
            if (onEvent != null) {
              List onEventActions = onEvent.getActions();
              if (onEventActions != null) {
                for (Action onEventAction : onEventActions) {
                  if (checkIfActionUsesFunctionDefinition(functionDefinitionName, onEventAction))
                    actions.add(onEventAction);
                }
              }
            }
          }
        }
      } else if (state instanceof CallbackState) {
        CallbackState callbackState = (CallbackState) state;
        final Action callbackStateAction = callbackState.getAction();
        if (checkIfActionUsesFunctionDefinition(functionDefinitionName, callbackStateAction)) {
          actions.add(callbackStateAction);
        }

      } else if (state instanceof OperationState) {
        OperationState operationState = (OperationState) state;
        final List operationStateActions = operationState.getActions();
        if (operationStateActions != null) {
          for (Action operationStateAction : operationStateActions) {
            if (checkIfActionUsesFunctionDefinition(functionDefinitionName, operationStateAction)) {
              actions.add(operationStateAction);
            }
          }
        }
      } else if (state instanceof ParallelState) {
        ParallelState parallelState = (ParallelState) state;
        List parallelStateBranches = parallelState.getBranches();
        if (parallelStateBranches != null) {
          for (Branch branch : parallelStateBranches) {
            List branchActions = branch.getActions();
            if (branchActions != null) {
              for (Action branchAction : branchActions) {
                if (checkIfActionUsesFunctionDefinition(functionDefinitionName, branchAction)) {
                  actions.add(branchAction);
                }
              }
            }
          }
        }
      } else if (state instanceof ForEachState) {
        ForEachState forEachState = (ForEachState) state;
        List forEachStateActions = forEachState.getActions();
        if (forEachStateActions != null) {
          for (Action forEachStateAction : forEachStateActions) {
            if (checkIfActionUsesFunctionDefinition(functionDefinitionName, forEachStateAction)) {
              actions.add(forEachStateAction);
            }
          }
        }
      }
    }

    return actions;
  }

  public static boolean checkIfActionUsesFunctionDefinition(
      String functionDefinitionName, Action action) {
    return action != null
        && action.getFunctionRef() != null
        && action.getFunctionRef().getRefName() != null
        && action.getFunctionRef().getRefName().equals(functionDefinitionName);
  }

  public static boolean hasFunctionDefs(Workflow workflow, String functionDefinitionName) {
    if (!hasFunctionDefs(workflow)) return false;
    List functionDefs = workflow.getFunctions().getFunctionDefs();
    return functionDefs.stream()
        .anyMatch(
            functionDefinition -> functionDefinition.getName().equals(functionDefinitionName));
  }

  public static FunctionRef getFunctionRefFromAction(Workflow workflow, String action) {
    if (!hasStates(workflow)) return null;

    for (State state : workflow.getStates()) {
      if (state instanceof EventState) {
        EventState eventState = (EventState) state;
        List onEvents = eventState.getOnEvents();
        if (onEvents != null) {
          for (OnEvents onEvent : onEvents) {
            if (onEvent != null) {
              List onEventActions = onEvent.getActions();
              if (onEventActions != null) {
                for (Action onEventAction : onEventActions) {
                  if (onEventAction != null
                      && onEventAction.getName() != null
                      && onEventAction.getName().equals(action))
                    return onEventAction.getFunctionRef();
                }
              }
            }
          }
        }
      } else if (state instanceof CallbackState) {
        CallbackState callbackState = (CallbackState) state;
        final Action callbackStateAction = callbackState.getAction();
        if (callbackStateAction != null
            && callbackStateAction.getName() != null
            && callbackStateAction.getName().equals(action)) {
          return callbackStateAction.getFunctionRef();
        }

      } else if (state instanceof OperationState) {
        OperationState operationState = (OperationState) state;
        final List operationStateActions = operationState.getActions();
        if (operationStateActions != null) {
          for (Action operationStateAction : operationStateActions) {
            if (operationStateAction != null
                && operationStateAction.getName() != null
                && operationStateAction.getName().equals(action)) {
              return operationStateAction.getFunctionRef();
            }
          }
        }
      } else if (state instanceof ParallelState) {
        ParallelState parallelState = (ParallelState) state;
        List parallelStateBranches = parallelState.getBranches();
        if (parallelStateBranches != null) {
          for (Branch branch : parallelStateBranches) {
            List branchActions = branch.getActions();
            if (branchActions != null) {
              for (Action branchAction : branchActions) {
                if (branchAction != null
                    && branchAction.getName() != null
                    && branchAction.getName().equals(action)) {
                  return branchAction.getFunctionRef();
                }
              }
            }
          }
        }
      } else if (state instanceof ForEachState) {
        ForEachState forEachState = (ForEachState) state;
        List forEachStateActions = forEachState.getActions();
        if (forEachStateActions != null) {
          for (Action forEachStateAction : forEachStateActions) {
            if (forEachStateAction != null
                && forEachStateAction.getName() != null
                && forEachStateAction.getName().equals(action)) {
              return forEachStateAction.getFunctionRef();
            }
          }
        }
      }
    }

    return null;
  }

  public static boolean hasFunctionDefs(Workflow workflow) {
    return workflow != null
        && workflow.getFunctions() != null
        && workflow.getFunctions().getFunctionDefs() != null
        && !workflow.getFunctions().getFunctionDefs().isEmpty();
  }

  /** Returns true if workflow has states, otherwise false */
  public static boolean hasStates(Workflow workflow) {
    return workflow != null && workflow.getStates() != null && !workflow.getStates().isEmpty();
  }

  /** Returns true if workflow has events definitions, otherwise false */
  public static boolean hasEventDefs(Workflow workflow) {
    return workflow != null
        && workflow.getEvents() != null
        && workflow.getEvents().getEventDefs() != null
        && !workflow.getEvents().getEventDefs().isEmpty();
  }

  /** Gets event refs of an action */
  public static List getActionEvents(Action action) {
    List actionEvents = new ArrayList<>();

    if (action != null && action.getEventRef() != null) {
      if (action.getEventRef().getTriggerEventRef() != null) {
        actionEvents.add(action.getEventRef().getTriggerEventRef());
      }
      if (action.getEventRef().getResultEventRef() != null) {
        actionEvents.add(action.getEventRef().getResultEventRef());
      }
    }

    return actionEvents;
  }

  /**
   * Merges two JsonNode
   *
   * @param mainNode
   * @param updateNode
   * @return merged JsonNode
   */
  public static JsonNode mergeNodes(JsonNode mainNode, JsonNode updateNode) {

    Iterator fieldNames = updateNode.fieldNames();
    while (fieldNames.hasNext()) {

      String fieldName = fieldNames.next();
      JsonNode jsonNode = mainNode.get(fieldName);
      // if field exists and is an embedded object
      if (jsonNode != null && jsonNode.isObject()) {
        mergeNodes(jsonNode, updateNode.get(fieldName));
      } else {
        if (mainNode instanceof ObjectNode) {
          // Overwrite field
          JsonNode value = updateNode.get(fieldName);
          ((ObjectNode) mainNode).put(fieldName, value);
        }
      }
    }

    return mainNode;
  }

  /**
   * Adds node as field
   *
   * @param mainNode
   * @param toAddNode
   * @param fieldName
   * @return original, main node with field added
   */
  public static JsonNode addNode(JsonNode mainNode, JsonNode toAddNode, String fieldName) {
    ((ObjectNode) mainNode).put(fieldName, toAddNode);
    return mainNode;
  }

  /**
   * Adds array with name
   *
   * @param mainNode
   * @param toAddArray
   * @param arrayName
   * @return original, main node with array added
   */
  public static JsonNode addArray(JsonNode mainNode, ArrayNode toAddArray, String arrayName) {
    ((ObjectNode) mainNode).put(arrayName, toAddArray);
    return mainNode;
  }

  /**
   * Adds a object field
   *
   * @param mainNode
   * @param toAddValue
   * @param fieldName
   * @return original, main node with field added
   */
  public static JsonNode addFieldValue(JsonNode mainNode, Object toAddValue, String fieldName) {
    ObjectMapper mapper = new ObjectMapper();
    ((ObjectNode) mainNode).put(fieldName, mapper.valueToTree(toAddValue));
    return mainNode;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy