Skip to content

Commit ce21754

Browse files
chazwatkinsRubenHalman
authored andcommitted
fix(MissingFaultPath): exclude WaitDuration and WaitDate elements
`WaitDuration` and `WaitDate` elements cannot have fault paths in Salesforce, only Wait For Conditions supports them. - Updated rule to filter out `WaitDuration` and `WaitDuration` wait subtypes to prevent false positives. Closes #272
1 parent 57a5a58 commit ce21754

File tree

5 files changed

+335
-2
lines changed

5 files changed

+335
-2
lines changed

src/main/rules/MissingFaultPath.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,28 @@ export class MissingFaultPath
3434
suppressionElement: "actionName",
3535
});
3636
}
37+
38+
private isValidSubtype(proxyNode: core.FlowNode): boolean {
39+
if (!this.applicableElements.includes(proxyNode.subtype)) {
40+
return false;
41+
}
42+
43+
if (proxyNode.subtype === "waits") {
44+
const elementSubtype: string = (proxyNode.element as Record<string, unknown>)?.["elementSubtype"] as string;
45+
const excludedSubtypes: string[] = ["WaitDuration", "WaitDate"];
46+
return !excludedSubtypes.includes(elementSubtype);
47+
}
48+
49+
return true;
50+
}
51+
3752
public execute(flow: core.Flow): core.RuleResult {
3853
const compiler = new core.Compiler();
3954
const results: core.ResultDetails[] = [];
4055
const elementsWhereFaultPathIsApplicable = (
4156
flow.elements?.filter((node) => {
4257
const proxyNode = node as unknown as core.FlowNode;
43-
const validSubtype = this.applicableElements.includes(proxyNode.subtype);
44-
return validSubtype;
58+
return this.isValidSubtype(proxyNode);
4559
}) as core.FlowNode[]
4660
).map((e) => e.name);
4761

tests/MissingFaultPath.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,31 @@ describe("MissingFaultPath", () => {
5858
const occurringResults = results[0].ruleResults.filter((rule) => rule.occurs);
5959
expect(occurringResults).toHaveLength(0);
6060
});
61+
62+
it("should not flag WaitDuration elements as missing fault path", async () => {
63+
const { default: rawFile } = await import("./jsonfiles/MissingFaultPath_WaitDuration.json");
64+
const parsedFile: ParsedFlow[] = rawFile as unknown as ParsedFlow[];
65+
const missingFaultPathRule = new MissingFaultPath();
66+
const flow: Flow = parsedFile.pop()?.flow as Flow;
67+
const scanResults: RuleResult = missingFaultPathRule.execute(flow);
68+
expect(scanResults.occurs).toBe(false);
69+
});
70+
71+
it("should not flag WaitDate elements as missing fault path", async () => {
72+
const { default: rawFile } = await import("./jsonfiles/MissingFaultPath_WaitDate.json");
73+
const parsedFile: ParsedFlow[] = rawFile as unknown as ParsedFlow[];
74+
const missingFaultPathRule = new MissingFaultPath();
75+
const flow: Flow = parsedFile.pop()?.flow as Flow;
76+
const scanResults: RuleResult = missingFaultPathRule.execute(flow);
77+
expect(scanResults.occurs).toBe(false);
78+
});
79+
80+
it("should flag Wait For Conditions elements as missing fault path", async () => {
81+
const { default: rawFile } = await import("./jsonfiles/MissingFaultPath_WaitConditions.json");
82+
const parsedFile: ParsedFlow[] = rawFile as unknown as ParsedFlow[];
83+
const missingFaultPathRule = new MissingFaultPath();
84+
const flow: Flow = parsedFile.pop()?.flow as Flow;
85+
const scanResults: RuleResult = missingFaultPathRule.execute(flow);
86+
expect(scanResults.occurs).toBe(true);
87+
});
6188
});
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
[
2+
{
3+
"flow": {
4+
"flowVariables": [],
5+
"flowResources": [],
6+
"flowMetadata": ["apiVersion", "processType", "label", "status"],
7+
"flowNodes": ["waits", "start"],
8+
"name": "Test_Flow_WaitConditions",
9+
"xmldata": {
10+
"@xmlns": "http://soap.sforce.com/2006/04/metadata",
11+
"apiVersion": "62.0",
12+
"label": "Test Flow Wait Conditions",
13+
"processType": "AutoLaunchedFlow",
14+
"waits": {
15+
"name": "Wait_For_Conditions",
16+
"label": "Wait For Conditions",
17+
"locationX": "176",
18+
"locationY": "287",
19+
"waitEvents": {
20+
"name": "myWaitEvent",
21+
"conditionLogic": "and",
22+
"conditions": {
23+
"leftValueReference": "myVariable_current.Status",
24+
"operator": "EqualTo",
25+
"rightValue": { "stringValue": "Completed" }
26+
}
27+
}
28+
},
29+
"start": {
30+
"locationX": "50",
31+
"locationY": "0",
32+
"connector": { "targetReference": "Wait_For_Conditions" },
33+
"triggerType": "RecordAfterSave",
34+
"object": "Account",
35+
"recordTriggerType": "Create"
36+
},
37+
"status": "Draft"
38+
},
39+
"label": "Test Flow Wait Conditions",
40+
"processType": "AutoLaunchedFlow",
41+
"start": {
42+
"locationX": "50",
43+
"locationY": "0",
44+
"connector": { "targetReference": "Wait_For_Conditions" },
45+
"triggerType": "RecordAfterSave",
46+
"object": "Account",
47+
"recordTriggerType": "Create"
48+
},
49+
"status": "Draft",
50+
"type": "AutoLaunchedFlow",
51+
"elements": [
52+
{ "element": "62.0", "subtype": "apiVersion", "metaType": "metadata" },
53+
{ "element": "Test Flow Wait Conditions", "subtype": "label", "metaType": "metadata" },
54+
{ "element": "AutoLaunchedFlow", "subtype": "processType", "metaType": "metadata" },
55+
{
56+
"element": {
57+
"name": "Wait_For_Conditions",
58+
"label": "Wait For Conditions",
59+
"locationX": "176",
60+
"locationY": "287",
61+
"waitEvents": {
62+
"name": "myWaitEvent",
63+
"conditionLogic": "and",
64+
"conditions": {
65+
"leftValueReference": "myVariable_current.Status",
66+
"operator": "EqualTo",
67+
"rightValue": { "stringValue": "Completed" }
68+
}
69+
}
70+
},
71+
"subtype": "waits",
72+
"metaType": "node",
73+
"connectors": [],
74+
"name": "Wait_For_Conditions",
75+
"locationX": "176",
76+
"locationY": "287"
77+
},
78+
{
79+
"element": {
80+
"locationX": "50",
81+
"locationY": "0",
82+
"connector": { "targetReference": "Wait_For_Conditions" },
83+
"triggerType": "RecordAfterSave",
84+
"object": "Account",
85+
"recordTriggerType": "Create"
86+
},
87+
"subtype": "start",
88+
"metaType": "node",
89+
"connectors": [
90+
{
91+
"element": { "targetReference": "Wait_For_Conditions" },
92+
"processed": false,
93+
"type": "connector",
94+
"reference": "Wait_For_Conditions"
95+
}
96+
],
97+
"name": "flowstart",
98+
"locationX": "50",
99+
"locationY": "0"
100+
},
101+
{ "element": "Draft", "subtype": "status", "metaType": "metadata" }
102+
],
103+
"startReference": "Wait_For_Conditions"
104+
}
105+
}
106+
]
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
[
2+
{
3+
"flow": {
4+
"flowVariables": [],
5+
"flowResources": [],
6+
"flowMetadata": ["apiVersion", "processType", "label", "status"],
7+
"flowNodes": ["waits", "start"],
8+
"name": "Test_Flow_WaitDate",
9+
"xmldata": {
10+
"@xmlns": "http://soap.sforce.com/2006/04/metadata",
11+
"apiVersion": "62.0",
12+
"label": "Test Flow WaitDate",
13+
"processType": "AutoLaunchedFlow",
14+
"waits": {
15+
"name": "Wait_Until_Date",
16+
"label": "Wait Until Date",
17+
"locationX": "176",
18+
"locationY": "287",
19+
"elementSubtype": "WaitDate",
20+
"dateTimeValue": "2025-12-31T00:00:00.000Z"
21+
},
22+
"start": {
23+
"locationX": "50",
24+
"locationY": "0",
25+
"connector": { "targetReference": "Wait_Until_Date" },
26+
"triggerType": "RecordAfterSave",
27+
"object": "Account",
28+
"recordTriggerType": "Create"
29+
},
30+
"status": "Draft"
31+
},
32+
"label": "Test Flow WaitDate",
33+
"processType": "AutoLaunchedFlow",
34+
"start": {
35+
"locationX": "50",
36+
"locationY": "0",
37+
"connector": { "targetReference": "Wait_Until_Date" },
38+
"triggerType": "RecordAfterSave",
39+
"object": "Account",
40+
"recordTriggerType": "Create"
41+
},
42+
"status": "Draft",
43+
"type": "AutoLaunchedFlow",
44+
"elements": [
45+
{ "element": "62.0", "subtype": "apiVersion", "metaType": "metadata" },
46+
{ "element": "Test Flow WaitDate", "subtype": "label", "metaType": "metadata" },
47+
{ "element": "AutoLaunchedFlow", "subtype": "processType", "metaType": "metadata" },
48+
{
49+
"element": {
50+
"name": "Wait_Until_Date",
51+
"label": "Wait Until Date",
52+
"locationX": "176",
53+
"locationY": "287",
54+
"elementSubtype": "WaitDate",
55+
"dateTimeValue": "2025-12-31T00:00:00.000Z"
56+
},
57+
"subtype": "waits",
58+
"metaType": "node",
59+
"connectors": [],
60+
"name": "Wait_Until_Date",
61+
"locationX": "176",
62+
"locationY": "287"
63+
},
64+
{
65+
"element": {
66+
"locationX": "50",
67+
"locationY": "0",
68+
"connector": { "targetReference": "Wait_Until_Date" },
69+
"triggerType": "RecordAfterSave",
70+
"object": "Account",
71+
"recordTriggerType": "Create"
72+
},
73+
"subtype": "start",
74+
"metaType": "node",
75+
"connectors": [
76+
{
77+
"element": { "targetReference": "Wait_Until_Date" },
78+
"processed": false,
79+
"type": "connector",
80+
"reference": "Wait_Until_Date"
81+
}
82+
],
83+
"name": "flowstart",
84+
"locationX": "50",
85+
"locationY": "0"
86+
},
87+
{ "element": "Draft", "subtype": "status", "metaType": "metadata" }
88+
],
89+
"startReference": "Wait_Until_Date"
90+
}
91+
}
92+
]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
[
2+
{
3+
"flow": {
4+
"flowVariables": [],
5+
"flowResources": [],
6+
"flowMetadata": ["apiVersion", "processType", "label", "status"],
7+
"flowNodes": ["waits", "start"],
8+
"name": "Test_Flow_WaitDuration",
9+
"xmldata": {
10+
"@xmlns": "http://soap.sforce.com/2006/04/metadata",
11+
"apiVersion": "62.0",
12+
"label": "Test Flow WaitDuration",
13+
"processType": "AutoLaunchedFlow",
14+
"waits": {
15+
"name": "Wait_5_Minutes",
16+
"label": "Wait 5 Minutes",
17+
"locationX": "176",
18+
"locationY": "287",
19+
"elementSubtype": "WaitDuration",
20+
"timeOffset": 5,
21+
"timeOffsetUnit": "Minutes"
22+
},
23+
"start": {
24+
"locationX": "50",
25+
"locationY": "0",
26+
"connector": { "targetReference": "Wait_5_Minutes" },
27+
"triggerType": "RecordAfterSave",
28+
"object": "Account",
29+
"recordTriggerType": "Create"
30+
},
31+
"status": "Draft"
32+
},
33+
"label": "Test Flow WaitDuration",
34+
"processType": "AutoLaunchedFlow",
35+
"start": {
36+
"locationX": "50",
37+
"locationY": "0",
38+
"connector": { "targetReference": "Wait_5_Minutes" },
39+
"triggerType": "RecordAfterSave",
40+
"object": "Account",
41+
"recordTriggerType": "Create"
42+
},
43+
"status": "Draft",
44+
"type": "AutoLaunchedFlow",
45+
"elements": [
46+
{ "element": "62.0", "subtype": "apiVersion", "metaType": "metadata" },
47+
{ "element": "Test Flow WaitDuration", "subtype": "label", "metaType": "metadata" },
48+
{ "element": "AutoLaunchedFlow", "subtype": "processType", "metaType": "metadata" },
49+
{
50+
"element": {
51+
"name": "Wait_5_Minutes",
52+
"label": "Wait 5 Minutes",
53+
"locationX": "176",
54+
"locationY": "287",
55+
"elementSubtype": "WaitDuration",
56+
"timeOffset": 5,
57+
"timeOffsetUnit": "Minutes"
58+
},
59+
"subtype": "waits",
60+
"metaType": "node",
61+
"connectors": [],
62+
"name": "Wait_5_Minutes",
63+
"locationX": "176",
64+
"locationY": "287"
65+
},
66+
{
67+
"element": {
68+
"locationX": "50",
69+
"locationY": "0",
70+
"connector": { "targetReference": "Wait_5_Minutes" },
71+
"triggerType": "RecordAfterSave",
72+
"object": "Account",
73+
"recordTriggerType": "Create"
74+
},
75+
"subtype": "start",
76+
"metaType": "node",
77+
"connectors": [
78+
{
79+
"element": { "targetReference": "Wait_5_Minutes" },
80+
"processed": false,
81+
"type": "connector",
82+
"reference": "Wait_5_Minutes"
83+
}
84+
],
85+
"name": "flowstart",
86+
"locationX": "50",
87+
"locationY": "0"
88+
},
89+
{ "element": "Draft", "subtype": "status", "metaType": "metadata" }
90+
],
91+
"startReference": "Wait_5_Minutes"
92+
}
93+
}
94+
]

0 commit comments

Comments
 (0)