SDK: Rule Steps (Advanced)
  • 25 Jan 2022
  • 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 have complete control over all aspects of the rule being constructed. 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

To create an advanced rule, create a public class, and decorate with the AutoRegisterRuleStep attribute. This attribute has four properties.

  • string name -- This property sets the name of your rule which will show up in the rule designer.
  • System.Type anchorType -- This property sets the type your rule will mainly interact with. This property is used to limit the rules that are 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 your rule will not show up as a possible choice in the designer if you select a string as anchor data.
  • bool anchorTypeIsList -- Similar to the anchorType property, this property is used to limit 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 your 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, this is what 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 sentence form what your 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 is only evaluating whether or not 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, still also 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 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 hardcoded. It looks like this.


Design-time validation to the rule. This would prevent the user from entering a number value less than 1. To do this, configure the class to inherit from and implement the IValidationSource interface. Also, add the InvalidateVerbInfo(); Once the property is set so that the validations show up correctly in the designer when the value of the property 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?