Skip to content

Commit 754d1bf

Browse files
committed
adds betamode
1 parent 82af4b6 commit 754d1bf

File tree

4 files changed

+188
-19
lines changed

4 files changed

+188
-19
lines changed

readme.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [Defining Severity Levels](#defining-severity-levels)
1212
- [Configuring Expressions](#configuring-expressions)
1313
- [Specifying Exceptions](#specifying-exceptions)
14+
- [Include Beta Rules](#include-beta-rules)
1415
- [Core Functions](#core-functions)
1516
- [Development](#development)
1617

@@ -128,15 +129,15 @@ It is recommended to set up configuration and define:
128129
```json
129130
{
130131
"rules": {
131-
...
132+
// Your rules here
132133
},
133134
"exceptions": {
134-
...
135+
// Your exceptions here
135136
}
136137
}
137138
```
138139

139-
Using the rules section of your configurations, you can specify the list of rules to be run. Furthermore, you can define the severity of violating specific rules and configure relevant expressions for some rules. Below is a breakdown of the available attributes of rule configuration:
140+
Using the rules section of your configurations, you can specify the list of rules to be run. Furthermore, you can define the severity and configure expressions of rules. To include rules currently that are currently in beta, set `betarules` to true. Below is a breakdown of the available attributes of rule configuration:
140141

141142
```json
142143
{
@@ -157,10 +158,10 @@ When the severity is not provided it will be `warning` by default. Other availab
157158
{
158159
"rules": {
159160
"FlowDescription": {
160-
"severity": "warning"
161+
"severity": "error"
161162
},
162163
"UnusedVariable": {
163-
"severity": "error"
164+
"severity": "note"
164165
}
165166
}
166167
}
@@ -178,7 +179,7 @@ Some rules have additional attributes to configure, such as the expression, that
178179
"expression": "===58"
179180
},
180181
"FlowName": {
181-
"severity": "error",
182+
"severity": "note",
182183
"expression": "[A-Za-z0-9]"
183184
}
184185
}
@@ -204,6 +205,23 @@ Specifying exceptions allows you to exclude specific scenarios from rule enforce
204205
}
205206
```
206207

208+
### Include Beta Rules
209+
210+
New rules are introduced in Beta mode before being added to the default ruleset.
211+
To include current Beta rules, enable the optional betamode parameter in your configuration:
212+
213+
```json
214+
{
215+
"rules": {
216+
...
217+
},
218+
"exceptions": {
219+
...
220+
},
221+
"betamode": true
222+
}
223+
```
224+
207225
## Core Functions
208226

209227
### [`getRules(ruleNames?: string[]): IRuleDefinition[]`](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tree/main/src/main/libs/GetRuleDefinitions.ts)

src/main/interfaces/IRulesConfig.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IExceptions } from "./IExceptions";
33
import { IRuleOptions } from "./IRuleOptions";
44

55
export interface IRulesConfig {
6+
betamode?: boolean;
67
exceptions?: IExceptions;
7-
rules?: AdvancedRuleConfig | IRuleOptions;
8-
}
8+
rules?: AdvancedRuleConfig | IRuleOptions;
9+
}
Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
2+
import { IRulesConfig } from "../interfaces/IRulesConfig";
23
import { BetaRuleStore, DefaultRuleStore } from "../store/DefaultRuleStore";
34
import { DynamicRule } from "./DynamicRule";
45

5-
export function GetRuleDefinitions(ruleConfig?: Map<string, unknown>): IRuleDefinition[] {
6+
export function GetRuleDefinitions(ruleConfig?: Map<string, unknown>, options?: IRulesConfig): IRuleDefinition[] {
67
const selectedRules: IRuleDefinition[] = [];
8+
const includeBeta = options?.betamode === true;
9+
710
if (ruleConfig && ruleConfig instanceof Map) {
811
for (const ruleName of ruleConfig.keys()) {
9-
let severity = "error";
12+
let severity = "warning";
1013
try {
1114
const configuredSeverity = ruleConfig.get(ruleName)?.["severity"];
1215
if (
1316
configuredSeverity &&
1417
(configuredSeverity === "error" ||
15-
configuredSeverity === "warning" ||
16-
configuredSeverity === "note")
18+
configuredSeverity === "warning" ||
19+
configuredSeverity === "note")
1720
) {
1821
severity = configuredSeverity;
1922
}
@@ -22,27 +25,37 @@ export function GetRuleDefinitions(ruleConfig?: Map<string, unknown>): IRuleDefi
2225
matchedRule.severity = severity;
2326
}
2427
selectedRules.push(matchedRule);
25-
2628
} catch (error) {
2729
console.log(error.message);
2830
}
2931
}
3032
} else {
33+
// Load all defaults
3134
for (const rule in DefaultRuleStore) {
3235
const matchedRule = new DynamicRule(rule) as IRuleDefinition;
3336
selectedRules.push(matchedRule);
3437
}
3538
}
3639

40+
// Append all beta rules if opted in (skip if already included via config/default)
41+
if (includeBeta) {
42+
for (const betaRuleName in BetaRuleStore) {
43+
if (!selectedRules.some((r) => r.name === betaRuleName)) { // Avoid duplicates
44+
const betaRule = new DynamicRule(betaRuleName) as IRuleDefinition;
45+
selectedRules.push(betaRule);
46+
}
47+
}
48+
}
49+
3750
return selectedRules;
3851
}
3952

40-
export function getRules(ruleNames?: string[]): IRuleDefinition[] {
53+
export function getRules(ruleNames?: string[], options?: IRulesConfig): IRuleDefinition[] {
4154
if (ruleNames && ruleNames.length > 0) {
42-
const ruleSeverityMap = new Map<string, string>(ruleNames.map((name) => [name, "error"]));
43-
return GetRuleDefinitions(ruleSeverityMap);
55+
const ruleSeverityMap = new Map<string, { severity: string }>(ruleNames.map((name) => [name, { severity: "error" }]));
56+
return GetRuleDefinitions(ruleSeverityMap, options);
4457
} else {
45-
return GetRuleDefinitions();
58+
return GetRuleDefinitions(undefined, options);
4659
}
4760
}
4861

@@ -52,5 +65,4 @@ export function getBetaRules(): IRuleDefinition[] {
5265

5366
function getBetaDefinition(): IRuleDefinition[] {
5467
return Object.values(BetaRuleStore).map((rule) => new rule() as IRuleDefinition);
55-
}
56-
68+
}

tests/ConfigBetaMode.test.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { describe, expect, it } from "@jest/globals";
2+
import * as path from "path";
3+
4+
import * as core from "../src";
5+
6+
describe("Rule Configurations ", () => {
7+
const example_uri1 = path.join(__dirname, "../assets/example-flows/force-app/main/default/flows/Unconnected_Element.flow-meta.xml");
8+
9+
// eslint-disable-next-line jest/no-disabled-tests
10+
it.skip("should use default when no configuration is provided", async () => {
11+
const flows = await core.parse([example_uri1]);
12+
const results: core.ScanResult[] = core.scan(flows, undefined);
13+
const rules = [...core.getRules(), ...core.getBetaRules()];
14+
const allRuleNames = rules.map((r) => r.name);
15+
const allRuleResults = results[0].ruleResults.map((r) => r.ruleName);
16+
expect(allRuleNames).toEqual(allRuleResults);
17+
expect(results[0].ruleResults).toHaveLength(rules.length);
18+
});
19+
20+
// eslint-disable-next-line jest/no-disabled-tests
21+
it.skip("should use default and include beta", async () => {
22+
const flows = await core.parse([example_uri1]);
23+
const ruleConfig = {
24+
betamode: true,
25+
exceptions: {
26+
CreateANewAccountWithChild: { DuplicateDMLOperation: ["ViewAccountId"] },
27+
},
28+
rules: {}
29+
};
30+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
31+
const rules = [...core.getRules(), ...core.getBetaRules()];
32+
const allRuleNames = rules.map((r) => r.name);
33+
const allRuleResults = results[0].ruleResults.map((r) => r.ruleName);
34+
expect(allRuleNames).toEqual(allRuleResults);
35+
expect(results[0].ruleResults).toHaveLength(rules.length);
36+
expect(results[0].ruleResults).toHaveLength(23);
37+
});
38+
39+
// eslint-disable-next-line jest/no-disabled-tests
40+
it.skip("should use default when no rules are specified", async () => {
41+
const flows = await core.parse([example_uri1]);
42+
const ruleConfig = {
43+
exceptions: {
44+
CreateANewAccountWithChild: { DuplicateDMLOperation: ["ViewAccountId"] },
45+
},
46+
rules: {}
47+
};
48+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
49+
const rules = [...core.getRules()];
50+
const allRuleNames = rules.map((r) => r.name);
51+
const allRuleResults = results[0].ruleResults.map((r) => r.ruleName);
52+
expect(allRuleNames).toEqual(allRuleResults);
53+
expect(results[0].ruleResults).toHaveLength(rules.length);
54+
});
55+
56+
it("incorrect rule severity configurations are defaulted", async () => {
57+
const flows = await core.parse([example_uri1]);
58+
const ruleConfig = {
59+
rules: {
60+
MissingNullHandler: {
61+
severity: "errorr",
62+
},
63+
},
64+
};
65+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
66+
expect(results[0].ruleResults).toHaveLength(1);
67+
});
68+
69+
it("incorrect rule configurations are skipped", async () => {
70+
const flows = await core.parse([example_uri1]);
71+
jest.spyOn(global.console, "error").mockImplementation(() => {});
72+
jest.spyOn(global.console, "log").mockImplementation(() => {});
73+
74+
const ruleConfig = {
75+
exceptions: {
76+
CreateANewAccountWithChild: { DuplicateDMLOperation: ["ViewAccountId"] },
77+
},
78+
rules: {
79+
MissingNullHandler: {
80+
severity: "error",
81+
},
82+
MissingNullHandler2: {
83+
severity: "error",
84+
},
85+
},
86+
};
87+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
88+
expect(results[0].ruleResults).toHaveLength(1);
89+
});
90+
91+
it("Multiple Expressions are individually checked", async () => {
92+
const flows = await core.parse([example_uri1]);
93+
const ruleConfig = {
94+
rules: {
95+
APIVersion: {
96+
expression: ">50",
97+
severity: "error",
98+
},
99+
CopyAPIName: {
100+
severity: "error",
101+
},
102+
DMLStatementInLoop: {
103+
severity: "error",
104+
},
105+
DuplicateDMLOperation: {
106+
severity: "error",
107+
},
108+
FlowDescription: {
109+
severity: "error",
110+
},
111+
FlowName: {
112+
expression: "[A-Za-z0-9]+_[A-Za-z0-9]+",
113+
severity: "error",
114+
},
115+
HardcodedId: {
116+
severity: "error",
117+
},
118+
MissingFaultPath: {
119+
severity: "error",
120+
},
121+
MissingNullHandler: {
122+
severity: "error",
123+
},
124+
SOQLQueryInLoop: {
125+
severity: "error",
126+
},
127+
UnconnectedElement: {
128+
severity: "error",
129+
},
130+
UnusedVariable: {
131+
severity: "error",
132+
},
133+
},
134+
};
135+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
136+
expect(results[0].ruleResults.find((r) => r.ruleName === "FlowName")?.occurs).toBe(false);
137+
});
138+
});

0 commit comments

Comments
 (0)