Macros Use Case: Recursive Operations

Here’s an example of a macro that deletes a sfdcDigest node. It also deletes the digest node and all other nodes that depend on that node.

This macro deletes the specified node and dynamically queries the JSON document to delete each dependency of those nodes.

Consider the simplified JSON below. To delete node3 and its dependencies, the result should delete: node3, node4, node5 (depends on node4), node6 (depends on node5), node7, node8, and node9.

Example 

Example JSON

1{
2    "node1": { },
3    "node2": {
4        "dependsOn": "node1"
5    },
6    "node3": {
7        "dependsOn": "node2"
8    },
9    "node4": {
10        "dependsOn": "node3"
11    },
12    "node5": {
13        "dependsOn": "node4"
14    },
15    "node6": {
16        "dependsOn": "node5"
17    },
18    "node7": {
19        "dependsOn": "node3"
20    },
21    "node8": {
22        "dependsOn": "node3"
23    },
24    "node9": {
25        "dependsOn": "node3"
26    }
27}

Example Without Macro 

Without using a macro (and associated enhancements), nodes would have to be deleted individually.

1{
2    "rules": [
3        {
4            "name": "Delete node3 and dependencies",
5            "actions": [
6               {
7                   "action": "delete",
8                   "path": "$.node3"
9               },
10               {
11                   "action": "delete",
12                   "path": "$.node4"
13               },
14              {
15                   "action": "delete",
16                   "path": "$.node5"
17               },
18              {
19                   "action": "delete",
20                   "path": "$.node6"
21               },
22               {
23                   "action": "delete",
24                   "path": "$.node7"
25               },
26               {
27                   "action": "delete",
28                   "path": "$.node8"
29               },
30               {
31                   "action": "delete",
32                   "path": "$.node9"
33               }
34           ]
35      }
36  ]
37}

This code is rigid and difficult to maintain because the rules need to be modified as node dependencies are added and removed from the JSON payload.

Example with a Recursive Macro 

1{
2    "namespace": "macroRecursion",
3    "definitions": [
4        {
5            "name": "deleteNodeAndDependencies",
6            "description": "Delete a node and its dependencies.  Returns an array of json paths of all the nodes that were deleted.",
7            "parameters": [
8                "nodeName"
9            ],
10            "actions": [
11                {
12                  "action": "eval", 
13                  "key": "fullNodePath",
14                  "value": "$.${p.nodeName}" 
15                },
16                {
17                  "action": "eval", 
18                  "key": "dependencies",  
19                  "value": "${macroRecursion:getDependents(p.nodeName)}"
20                },
21                {
22                  "action": "eval",
23                  "value": "${array:forEach(Rules.Eval.dependencies, '${macroRecursion:deleteNodeAndDependencies(macroRecursion:deleteSingleNodeByFullJsonPath(var))}')}"
24                },
25                {
26                   "action": "eval",
27                   "value": "${macroRecursion:deleteSingleNodeByFullJsonPath(Rules.Eval.fullNodePath)}"
28                }
29            ]
30        },
31        {
32            "name": "deleteSingleNodeByFullJsonPath",
33            "description": "Deletes a node by full json path.",
34            "parameters": [
35                "fullJsonPath"
36            ],
37            "actions": [
38              {
39                "action": "eval",
40                "key": "pathSegments",
41                "value": "${string:match(p.fullJsonPath,'\\\\[\\'(.*?)\\'\\\\]')}"
42              },
43              { 
44                "action": "delete",  
45                "path": "${p.fullJsonPath}"
46              }
47            ],
48            "returns": "${array:last(Rules.Eval.pathSegments)}"
49        },
50        {
51            "name": "getDependents",
52            "description": "Returns the full json path to search results",
53            "parameters": [
54                "nodeName"
55            ],
56            "actions": [
57              { 
58                "action": "eval", 
59                "key": "searchString", 
60                "value": "$.*[?(@.dependsOn=='${p.nodeName}')]"
61              },
62              { 
63                "action": "eval", 
64                "key": "paths", 
65                "value": "${json:searchPaths(Rules.Eval.searchString)}"
66              }
67            ],
68            "returns": "${Rules.Eval.paths}"
69        }
70    ]
71 }

Then, use a rule to call the macro:

1{
2    "rules": [
3        {
4            "name": "Recursively delete node3 and dependencies",
5            "actions": [
6                {
7                  "action": "eval", 
8                  "value": "${macroRecursion:deleteNodeAndDependencies('node3')}"
9                }
10            ]
11        }
12    ]
13}

Back to Macros in Rules