SDK: Rule Steps (Advanced)
  • 15 Apr 2024
  • 6 Minutes to read
  • Dark
    Light

SDK: Rule Steps (Advanced)

  • Dark
    Light

Article Summary

Version 7.x .NET Architecture Change

Overview

This document describes how to construct advanced rules; see SDK: Rule Steps (Basic) for how to create simple rules. Creating an advanced Rule allows developers to control all constructed Rule aspects. For example, create custom design-time validation messages to enhance the designer's ability to appropriately configure a Rule.

Decisions has a Public GitHub Repository with various SDK examples available.

Example

Create an advanced Rule, create a public class, and decorate it with the AutoRegisterRuleStep attribute. This attribute has four properties.

  • String Name: This property sets the name of the Rule, which will show up in the Rule designer.
  • System.Type anchorType: This property sets the type the Rule will mainly interact with. This property limits the rules shown in the Rule designer based on which anchor data is selected for a Rule statement. For example, if you set this value to int32, the Rule will not appear as a possible choice in the designer if you select a string as anchor data.
  • bool anchorTypeIsList: Similar to the anchorType property, this property limits which rules show in the Rule designer based on the anchor data selected. However, this property limits the rules shown based on whether or not the anchor data is or isn't an array.
  • params string[] categories: This property sets a category in which the Rule will be displayed in the Rule designer

Decorating the class with the AutoRegisterRuleStep attribute, configure the class to inherit from AbstractRuleStep. This interface provides a default implementation for all the methods of the IRuleStep interface. Customize these default implementations by overriding them. The following example shows how to override the most common methods.

The first method to override is the Run method. The default implementation of this will always evaluate false. The following example snippet shows how to override the run method to evaluate if the anchor data is not null or empty

The following code block contains the code used in this example:

using System;
using System.Collections.Generic;
using System.Linq;
using DecisionsFramework;
using DecisionsFramework.Design.ConfigurationStorage.Attributes;
using DecisionsFramework.Design.Flow;
using DecisionsFramework.Design.Flow.CoreRuleSteps;
using DecisionsFramework.Design.Flow.Mapping;
using DecisionsFramework.Design.Properties;

namespace CustomRuleAdvanced
{
    // [AutoRegisterRuleStep(name, anchorType, isList, category)]
    //  Name: Name of the rule. This value is shown in the Verbs dialog when selecting your comparison operator
    //  anchorType: The primary DataType you intend to interact with. If an Int is selected as InputData in the Rule Designer, this rule will not be shown as a verb
    //  isList: Is anchorType a List?
    //  category: Specifies the rule category for the verb dialog
    [AutoRegisterRuleStep("String Shorter Than", typeof(string), false, "Sample SDK Rules")]
    public class CustomRuleStep : 
        AbstractRuleStep,        // Defines base Rule behavior, you will override methods from this class to create your Rule
        IValidationSource        // Provides interface for exposing ValidationIssues to the Rule Designer
    {
        

            /*
             *
             *    Required Overrides
             * 
             */
            
            // Your logic goes inside of the Run method
            public override bool Run(RuleStepExecutionData data)
            {
                return (((string)data.Data[this.AnchorData.Name]).Length < number);
            }
        
            // This value is shown in the Rule Designer, after selecting "String Shorter Than" from the Verbs dialog
            // if [someVariable 'Shorter Than' myInputData] then...
            public override string GetVerbInfo(IInputMapping[] mappings)
            {
                return "Shorter Than";
            }
        
            
            
            /*
             *
             *    Inputs
             * 
             */
            
            // This method allows you to specify the InputData fields that are required by your Rule
            public override DataDescription[] InputData
            {
                get
                {
                    return new DataDescription[]
                    {
                        new DataDescription(new DecisionsNativeType(typeof(string)), "value"),
                    };
                }
            }
            
            // This method allows you to specify the format for how the mapped value will be displayed in the Rule Designer
            public override string GetValueInfo(IInputMapping[] mappings)
            {
                string value = "";
                IInputMapping valueMapping = (mappings ?? new IInputMapping[0]).FirstOrDefault(m => m != null && "value" == m.InputDataName);
                if (valueMapping != null)
                    value = base.GetValueInfo(valueMapping);
                return value;
            }
            
            
            
            /*
             *
             *    Options
             * 
             */
            
            [WritableValue]
            private int number;
            [PropertyClassification(0, "Number", "Settings")]
            public virtual int Number
            {
                get { return number; }
                set
                {
                    number = value;
                    OnPropertyChanged("Number");
                    InvalidateVerbInfo();             // Updates VerbInfo display in the Rule Designer
                }
            }
            
            
            
            /*
             *
             *    Validations
             * 
             */
            
            // This method allows you to return a list of ValidationIssue objects representing errors and warnings.
            public ValidationIssue[] GetValidationIssues()
            {
                List<ValidationIssue> issues = new List<ValidationIssue>();

                if (Number < 1) // Add a ValidationIssue if the number is < 1
                    issues.Add(new ValidationIssue(this, "Number must be greater than 0", null, BreakLevel.Fatal, "Number"));
                
                return issues.ToArray();
            }



    }
}

When used in the Rule designer, the Rule will look like when configured to evaluate Flow Data.InitiatingUserEmail as the anchor data. Notice how the Rule has no description of what it is doing. The description, or Rule verb, will be demonstrated next.

Next, give the Rule a verb. This verb is used by the designer to describe in a sentence what the Rule is doing. To give your Rule a verb, override the GetVerbInfo method as follows.

public override string GetVerbInfo(IInputMapping[] mappings)
{
    return "Has Value";
}

This configuration will look as follows:

The above Rule has no input data because it only evaluates whether the anchor data has value. However, there are many times when a Rule needs to have input data. In cases where the Rule has input data, override the GetValueInfo method to format how this input data appears in the text of the Rule. The below sample code shows an example of input data and how to format this data to show up clean in the Rule designer.

 public override DataDescription[] InputData
{
get
{
return new DataDescription[]
{
new DataDescription(new DecisionsNativeType(typeof(string)), "value"),
};
}
}
public override string GetValueInfo(IInputMapping[] mappings)
{
string value = "[Not Specified]";
IInputMapping valueMapping = (mappings ?? new IInputMapping[0]).FirstOrDefault(m => m != null && "value" == m.InputDataName);
if (valueMapping != null)
value = base.GetValueInfo(valueMapping);
return value;
}

The above configuration would render like this before selecting input data:

The above configuration would render like this if the value was configured to "tom@decisions.com "

In addition to input data, the Rule may need a property.


Example with a Property Value

The following example shows how to construct a Rule with a property value. This Rule evaluates the length of a string to see if its length is less than a number property.
The property of the Rule would be written like this:

 [WritableValue]
private int number;
[PropertyClassification(0, "Number", "Settings")]
public virtual int Number
{
get { return number; }
set
{
number = value;
OnPropertyChanged("Number");
}
}

The Run method would use the property like this:

 public override bool Run(RuleStepExecutionData data)
{
   return (((string)data.Data[this.AnchorData.Name]).Length < number);
}

This property would show up in the Rule designer as a value that can be hard coded. It looks like this.


Design-time validation of the Rule. This would prevent the user from entering a number value less than 1. Configure the class to inherit from and implement the IValidationSource interface to do this. Also, add the InvalidateVerbInfo(); Once the property is set, the validations show up correctly in the designer when the property's value is changed. The property and implemented IValidationSource would look like the following.

 [WritableValue]
private int number;
[PropertyClassification(0, "Number", "Settings")]
public virtual int Number
{
    get { return number; }
    set
    {
        number = value;
        OnPropertyChanged("Number");
        InvalidateVerbInfo();
    }
}
public ValidationIssue[] GetValidationIssues()
{
    List issues = new List();
    if (number < 1)
        issues.Add(new ValidationIssue(this, "Number must be greater than 0", null, BreakLevel.Fatal, "Number"));
    return issues.ToArray();
}



Was this article helpful?