SDK: Creating Custom Flow and Rule Behavior
  • 08 Jul 2024
  • 5 Minutes to read
  • Dark
    Light

SDK: Creating Custom Flow and Rule Behavior

  • Dark
    Light

Article summary

Overview

Flows in Decisions can be assigned a Flow Behavior which acts like an interface definition for a Flow. It can enforce expected inputs and outputs as well as create those expected inputs when the behavior is assigned.

Decisions comes with several prebuilt Flow behaviors, but developers may create and implement custom Flow behaviors to improve a process.


Custom Flow Behavior Methods

To create a new Flow behavior, create a public class that inherits from DefaultFlowBehavior. This provides a number of methods and properties available for configuration.

OnBehaviorAssigned

public virtual void OnBehaviorAssigned(Flow f)
public virtual DataDescription[] ProcessInputDeclaration(Flow flow, DataDescription[] inputData);
public virtual void ProcessInputValues(Flow flow, FlowStateData data);
public virtual DataDescription[] ProcessStepInputDeclaration(FlowStep step, DataDescription[] inputs);
public virtual FlowStepToolboxInformation[] ProcessSteps(Flow flow, string[] nodes, FlowStepToolboxInformation[] steps);
public virtual ValidationIssue[] RunFlowValidation(Flow flow);

This code runs when the behavior is assigned to a Flow. This method edits the Flow as desired. 

The below examples utilizes this method to add input data to a Flow and to add a tag to the Flow for quicker searching for the behavior.

Default Behavior: Nothing is done
Example Override of this Method:

 public override void OnBehaviorAssigned(Flow f)
{
   //Set inputs on this flow
   List<flowinputdatadescription> inputs = new List<flowinputdatadescription>();
   DecisionsType decTypeString = new DecisionsNativeType(typeof(string));
   inputs.Add(FlowInputDataDescription.Create(new DataDescription(decTypeString, INPUT_NAME_USER_ID)));
   f.Input = inputs.ToArray();

   //Set tag on this flow
   AbstractUserContext userContext = new SystemUserContext();
   tring flowBehaviorTag = "My Sample Behavior Flow";

   string[] existingTags = TaggingService.Instance.GetTags(userContext, f.Id, typeof(ElementRegistration).FullName);
   {
       if (ArrayUtilities.IsEmpty(existingTags) || !existingTags.Contains(flowBehaviorTag))
       {
           TaggingService.Instance.AddTag(userContext, f.Id, typeof(ElementRegistration).FullName, typeof(ElementRegistration).Name, flowBehaviorTag);
       }
   }
}</flowinputdatadescription></flowinputdatadescription>

ProcessInputDeclaration

This method is used in conjunction with the ProcessInputValues method to declare special inputs that are only visible inside your Flow. 

Whereas inputs normally get their value by mapping data into the Flow, these inputs retrieve their values from code ran through the ProcessInputValues method. These inputs are not visible  outside of the Flow, but the inputs' data is consumable inside the Flow. 

An example may include passing only a simple data such as an ID into a Flow while connecting complex data like the object associated with that ID available in the Flow. In this example, the built Flow behavior takes in only a user ID, but Flow contains the full user object available for use.

Default Behavior: No inputs are modified.
Example Override of this Method

 public override DataDescription[] ProcessInputDeclaration(Flow flow, DataDescription[] inputData)
{
    if (flow == null)
    {
        throw new Exception("flow not specified");
    }
 
    if (inputData == null)
    {
        inputData = new DataDescription[0];
    }

    List<datadescription> inputs = new List<datadescription>(inputData);

    DecisionsType decType = DataStructureService.Instance.GetDecisionsTypeByFullName(new SystemUserContext(), typeof(Account).FullName);
    inputs.Add(new DataDescription(decType, "User Account to Process"));

    return inputs.ToArray();
}</datadescription></datadescription>

ProcessInputValues

As mentioned above, this method works in conjunction with the ProcessInputsDelcaration method. This method executes at runtime to populate the values of the inputs declared by ProcessInputDeclaration. 

In the example below, the 'User Id to Process' input value allows users to search for the value for 'User Account to Process'.

Default Behavior: Do nothing
Example Override of this Method:

 public override void ProcessInputValues(Flow flow, FlowStateData data)
{
    Account accountToProcess = AccountService.Instance.GetByID(new SystemUserContext(), data["User Id to Process"].ToString());

    data["User Account to Process"] = accountToProcess;
}

RunFlowValidation

This method returns an array of validation issues to validate anything about the Flow. Validation issues are assigned to steps since it is a ValidationIssue data type.

An example use of this method may ensure that the Flow contains its expected inputs.

Default Behavior: No validation issues are checked for or returned.
Example Override of this Method:

 public override ValidationIssue[] RunFlowValidation(Flow flow)
{
    // check that the flow only has correct inputs
    List<validationissue> validationIssues = new List<validationissue>();

    if (flow.InputData != null && flow.InputData.Length == 1)
    {
        if (flow.InputData[0].Type is DecisionsNativeType && ((DecisionsNativeType)flow.Input[0].Type).ToNativeType() == typeof(string))
        {
            return validationIssues.ToArray();
        }
    }
    FlowStep startStep = flow.GetStepsOfType(typeof(StartStep)).FirstOrDefault();
    validationIssues.Add(new ValidationIssue(startStep, "This flow must have only one String input"));
    return validationIssues.ToArray();
}</validationissue></validationissue>

In addition to the other methods, DefaultFlowBehavrior also has properties that can be overridden.

public virtual bool ConstrainFlowOutputs { get; }
public virtual OutcomeScenarioData[] DefaultOutputs { get; }
public virtual bool IsUserSettable { get; }
public virtual string Name { get; }
public virtual bool OnlySyncSteps { get; }
public virtual string OverrideDebugUIClassName { get; }
public virtual bool ShowFlowInputs { get; }

DefaultOutputs

The DefaultOutputs method defines the expected output data and its outcome paths. This should be used in conjunction with ConstrainFlowOutputs to cause validation warnings when the defined defaults are not met.

Default Value: null
Example Override:

 public override OutcomeScenarioData[] DefaultOutputs
{
    get
    {
        return new OutcomeScenarioData[] { 
            new OutcomeScenarioData("Done",new DataDescription[] { new DataDescription(new DecisionsNativeType(typeof(string)), "My Result", false, true, false)}) };
    }
}

ConstrainFlowOutputs

Configuring this property to True causes validation warnings on the end steps in a Flow if the end steps' outputs don't match those as defined in the DefaultOutputs property.

Default Value: False
Example Override:

 public override bool ConstrainFlowOutputs
{
    get
    {
        return true;
    }
}

IsUserSettable

This property toggles whether or not this Flow behavior shows up in the Decisions Portal UI when a user sets the behavior of a Flow. 

When set to false, this behavior will not show up in the behavior list for users to select from; however, it can still be selected in any custom code to set Flow behaviors on Flows.

Default Value: True
Example Override:

 public override bool IsUserSettable
{
    get { return false; }
}

Name

Use this property to give the Flow behavior a name. This name displays in the portal UI once a user sets the Flow behavior for a Flow. 

If left blank, the name defaults to the full class.

Default Value: 'Default Flow Behavior'

Example Override:

 public override string Name
{
    get
    {
        return "My Behavior Flow";
    }
}

OnlySyncSteps

When set to true, this property forces the Flow to only allow Sync steps. This should be used on Flows with responses expected to return synchronously.

Default Value: False

Example Override:

 public override bool OnlySyncSteps
{
    get
    {
        return true;
    }
}

OverrideDebugUIClassName

This is an advanced property not normally required in a custom Flow behavior.
If needing to use this property, please contact Professional Services for implementation help and strategies.

The OverrideDebugUIClassName property allows developers to specify a different debugger UI when it runs.

Default Value: null

Since this is an advanced property that varies greatly for each specific use case, no example override is provided. 

ShowFlowInputs

When set to true, this property hides the Inputs section of the Flow's property editor. This can block Designers from editing the inputs already defined in the Flow behavior.

Default Value: True
Example Override:

 public override bool ShowFlowInputs
{
    get
    {
        return false;
    }
}



Custom Rule Behavior

IRuleBehavior, much like IFlowBehavior allows for the customization of the behavior of the Rule editor.

 public interface IRuleBehavior
{
    string Name { get; }

    bool IsUserSettable { get; }

    string RewriteSubject(Rule r, string subject);
    
    string RewriteVerb(Rule r, string verb);
    
    string RewritePredicate(Rule r, IRuleStep step, DataDescription anchorData, IInputMapping[] mappings, string predicate);
  
    DataDescription[] ProcessTreeInputDeclaration(Rule r, string path, DataDescription[] inputs);
    
    DataDescription[] ProcessStepInputDeclaration(RuleStep step, DataDescription[] inputs);
    
    DataDescription[] ProcessInputDeclaration(Rule r, DataDescription[] inputData);
    
    FlowStepToolboxInformation[] ProcessSteps(Rule r, DataDescription anchorData, string[] nodes, FlowStepToolboxInformation[] steps);
    
    ValidationIssue[] RunRuleValidation(Rule rule);
    
    void ProcessInputValues(Rule r, FlowStateData data);

    string OverrideDebugUIClassName { get; }
    
    string OverrideRunUIClassName { get; }

    AllowUserToEditRuleInputData { get; }

    void OnBehaviorAssigned(Rule r);

    bool CanChangeBehavior { get; }

    bool CanChangeRuleType { get; }

    bool AllowDirectServiceCall { get; }

}

For further information on SDK, visit the Decisions Forum.

Was this article helpful?