Skip to content

Instantly share code, notes, and snippets.

@DmitryevichD
Last active March 30, 2022 09:58
Show Gist options
  • Save DmitryevichD/9f0d26d84f1a1da809e1c35f04e2bc8c to your computer and use it in GitHub Desktop.
Save DmitryevichD/9f0d26d84f1a1da809e1c35f04e2bc8c to your computer and use it in GitHub Desktop.
Rule service. Add-ons for the rule expressions contract #rule-service

Abstract expression language v2.0.1

Innovations:

  • Expressions can return result as simple types (date, boolean, number, version, string)

  • Expressions can use names functions with predefined logic and signature such as (some, every, count, min, max, if etc)

  • Expression can use new type (dictionary)

  • Backward compatibility with contract v1.0.0

[2.0.1] - 2022-03-29

Added

  • type: dictionary

    • Group: Complex type
    • Description: Type is used for define variables that store key-value variables.
    • Available operations: **dictionary_1 ** (in | nin | eq | neq) dictionary_2
    • Attributes:
      • type - value type => dictionary
      • element_type - type of values in the dictionary => (string | number | version | date | boolean)
      • value - contains object with key-values. Required if user_property is not present
      • user_property - name of user property that contains object with key-values. Required if value is not present
    • Restrictions:
      • Keys in the dictionary must be present as string.
      • Values in the one dictionary must have the same type that defined in the attribute element_type
      • Values must be represented as a simple type (string | number | version | date | boolean)
    • Example:

      {
          "type": "dictionary",
          "element_type": "string",
          "value": {
              "map_key1": "mav_value_1",
              "map_key2": "mav_value_2"
          }
      },
      {
          "type": "dictionary",
          "element_type": "string",
          "user_property": "name_of_user_param_with_map_type"
      }
  • type: func

    • Group: Complex type

    • Available operations: operations depends on simple type (string | number | version | date | boolean) that return function

    • Description: The rule-engine has some named function with a specific signature. This type is used to define the name of this functions to be used in the expression

    • Attributes:

      • type - value type => func

      • name - function name => (some | every | count | min | max | if)

      • values - list of function arguments

    • Restrictions:

      • function result must be only simple type (string | number | version | date | boolean)
    • Example:

          {
            "type": "func",
            "name": "count",
            "values": [
              {
                "type": "inner_rule",
                "value": {
                  "operation": "eq",
                  "values": [
                    {
                      "type": "string",
                      "value": "key_value"
                    },
                    {
                      "type": "string",
                      "argument": "key_user_param"
                    }
                  ]
                }
              },
              {
                "type": "dictionary",
                "user_property": "experiment"
              }
            ]
          }
  • operation: call

    • Description: This operation execute function and return result

    • Example:

      {
        "operation": "call",
        "values": [
          {
            "type": "func",
            "name": "min",
            "values": [
              {
                "type": "number",
                "value": "20"
              },
              {
                "type": "number",
                "value": "100"
              },
              {
                "type": "number",
                "value": "10"
              }
            ]
          }
        ]
      }
  • function: count

    • Description: returns count of elements of the map match the provided predicate

    • Name: count

    • Signature: count(inner_rule as predicate, dictionary)

    • Return type: number

  • function: some

    • Description: returns true if some element of the map match the provided predicate

    • Name: some

    • Signature: some(inner_rule as predicate, dictionary)

    • Return type: boolean

  • function: every

    • Description: returns true if every element of the map match the provided predicate

    • Name: every

    • Signature: every(inner_rule as predicate, dictionary)

    • Return type: boolean

  • function: min

    • Description: returns the minimal number from list of numbers

    • Name: min

    • Signature: min(number, number, ...)

    • Return type: number

  • function: max

    • Description: returns the maximal number from list of numbers

    • Name: max

    • Signature: max(number, number, ...)

    • Return type: number

  • function: if

    • Description: if the predicate is true returns branch 1 else branch 2

    • Name: if

    • Signature: if(inner_rule as predicate, number as branch1, number as branch2)

    • Return type: number

Changed

  • inner_rule

    • can return simple types (string | number | version | date | boolean)
  • rule_expression

    • can return simple types (string | number | version | date | boolean)

Examples

Checking that the dictionary contains 1 or more key values corresponding to the predicate

RabbitMQ message that contains user param experiments with type dictionary and element type string

{
  "experiment_key1": {
    "value": "1",
    "startDate": "2022-01-12", 
    "endDate": "2022-02-12",
    "enabled": true
  },
  "experiment_key2": {
    "value": "3",
    "startDate": "2022-01-12",
    "endDate": "2022-04-12",
    "enabled": false
  },
    "experiment_key3": {
    "value": "1",
    "startDate": "2022-01-12",
    "endDate": "2022-09-12",
    "enabled": true
  },
  "experiment_key4": {
    "value": "4",
    "startDate": "2022-01-12",
    "endDate": "2022-04-12",
    "enabled": true
  }
}

Rule expression that counts the number of values in a dictionary using a predicate

{
  "operation": "gte",
  "values": [
    {
      "type": "func",
      "name": "count",
      "values": [
        {
          "type": "inner_rule",
          "value": {
            "operation": "eq",
            "values": [
              {
                "type": "string",
                "value": "1"
              },
              {
                "type": "string",
                "argument": "experement_key3"
              }
            ]
          }
        },
        {
          "type": "dictionary",
          "user_property": "experiment"
        }
      ]
    },
    {
      "type": "number",
      "value": "1"
    }
  ]
}

Execution steps (as of date 2022-03-22)

Step 0 - Receive from database user_param experiment
Step 1 - Extract valid key-values that will be used in expression

This step uses the message attributes such as startDate, endDate, enabled to define available attributes

  • experiment_key2 is skipped because it is disabled,
  • experement_key1 is skipped because it is deprecated

The user_property experiment that should use in the

        {
          "type": "dictionary",
          "user_property": "experiment"
        }

will have the following value

{
    "experiment_key3": "1",
    "experiment_key4": "4"
}
Step 2 - Execute count function
  • predicate attribute "argument": "experement_key3" will be changed to "argument": "1" because key with name experiment_key3 has value "1"
  • For each key-value will be execute next steps:
    • if the predicate doesn't contain the key name in any agrument attribute the key-value is skipped
    • else the predicate is executed and it result is true count is incremented
  • Returns the count
Step 3 - Execute expression in which the function count changes to number elements that to match to predicate
{
  "operation": "gte",
  "values": [
    {
      "type": "number",
      "value": "1"
    },
    {
      "type": "number",
      "value": "1"
    }
  ]
}
Result true

Checks that dictionary contains some key-values

RabbitMQ message that contains user param experiments with type dictionary and element type string

{
  "experiment_key1": {
    "value": "1",
    "startDate": "2022-01-12", 
    "endDate": "2022-02-12",
    "enabled": true
  },
  "experiment_key2": {
    "value": "3",
    "startDate": "2022-01-12",
    "endDate": "2022-04-12",
    "enabled": false
  },
    "experiment_key3": {
    "value": "1",
    "startDate": "2022-01-12",
    "endDate": "2022-09-12",
    "enabled": true
  },
  "experiment_key4": {
    "value": "4",
    "startDate": "2022-01-12",
    "endDate": "2022-04-12",
    "enabled": true
  }
}

Rule expression that detects one or more of values in a dictionary using a predicate

{
  "operation": "call",
  "values": [
    {
      "type": "func",
      "name": "some",
      "values": [
        {
          "type": "inner_rule",
          "value": {
            "operation": "eq",
            "values": [
              {
                "type": "string",
                "value": "1"
              },
              {
                "type": "string",
                "user_property": "experement_key3"
              }
            ]
          }
        },
        {
          "type": "dictionary",
          "user_property": "experiment"
        }
      ]
    }
  ]
}

Execution steps (as of date 2022-03-22)

Step 0 - Receive from database user_param experiment
Step 1 - Extract valid key-values that will be used in expression

This step uses the message attributes such as startDate, endDate, enabled to define available attributes

  • experiment_key2 is skipped because it is disabled,
  • experement_key1 is skipped because it is deprecated

The user_property experiment that should use in the

        {
          "type": "dictionary",
          "user_property": "experiment"
        }

will have the following value

{
    "experiment_key3": "1",
    "experiment_key4": "4"
}
Step 2 - Execute some function
  • predicate attribute "argument": "experement_key3" will be changed to "argument": "1" because key with name experiment_key3 has value "1"

  • For each key-value will be execute next steps:

    • if the predicate doesn't contain the key name in any agrument attribute the key-value is skipped
    • else the predicate is executed and it result is true the function returns true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment