Skip to content

Commit f644389

Browse files
committed
adds detailLevel config
1 parent ab74777 commit f644389

File tree

7 files changed

+82
-37
lines changed

7 files changed

+82
-37
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [Defining Severity Levels](#defining-severity-levels)
1616
- [Configuring Expressions](#configuring-expressions)
1717
- [Specifying Exceptions](#specifying-exceptions)
18+
- [Report Detail Level](#report-detail-level)
1819
- [Include Beta Rules](#include-beta-rules)
1920
- **[Usage](#Usage)**
2021
- [Examples](#examples)
@@ -125,7 +126,7 @@ _[UnusedVariable](https://github.com/Flow-Scanner/lightning-flow-scanner-core/tr
125126

126127
## Configuration
127128

128-
It is recommended to set up configuration and define:
129+
Lightning Flow Scanner is plug-and-play by default, but we recommend configuring and defining:
129130

130131
- The rules to be executed.
131132
- The severity of violating any specific rule.
@@ -226,6 +227,22 @@ _Example_
226227
}
227228
```
228229

230+
### Report Detail Level
231+
232+
Control the verbosity of violation reports via detailLevel. By default (`enriched`), outputs include element or flow-level details like variable data types, node connectors/locations, or attribute expressions for comprehensive reports. Set to `simple` for lighter output with only line and column numbers.
233+
234+
```json
235+
{
236+
"rules": {
237+
...
238+
},
239+
"exceptions": {
240+
...
241+
},
242+
"detailLevel": "simple"
243+
}
244+
```
245+
229246
### Include Beta Rules
230247

231248
New rules are introduced in Beta mode before being added to the default ruleset. To include current Beta rules, enable the optional betamode parameter in your configuration:
@@ -238,15 +255,16 @@ New rules are introduced in Beta mode before being added to the default ruleset.
238255
"exceptions": {
239256
...
240257
},
241-
"betamode": true
258+
"betaMode": true
242259
}
260+
243261
```
244262

245263
---
246264

247265
## Usage
248266

249-
`lightning-flow-scanner-core` can be used as a dependency in Node.js and browser environments, or as a standalone UMD module.
267+
Use `lightning-flow-scanner-core` as a Node.js/browser dependency or standalone UMD module.
250268

251269
### Examples
252270

src/main/interfaces/AutoFixable.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { IExceptions } from "./IExceptions";
22
import { IRuleOptions } from "./IRuleOptions";
33

4+
export enum DetailLevel {
5+
ENRICHED = 'enriched',
6+
SIMPLE = 'simple'
7+
}
8+
49
export interface IRulesConfig {
5-
betamode?: boolean;
10+
betaMode?: boolean; // Toggles beta rules; defaults to false
11+
betamode?: boolean; // Use betaMode instead; to be removed
12+
detailLevel?: 'enriched' | 'simple' | DetailLevel;
613
exceptions?: IExceptions;
714
rules?: IRuleOptions;
815
}

src/main/libs/GetRuleDefinitions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
23
import { IRulesConfig } from "../interfaces/IRulesConfig";
34
import { BetaRuleStore, DefaultRuleStore } from "../store/DefaultRuleStore";
@@ -9,7 +10,7 @@ export function getBetaRules(): IRuleDefinition[] {
910

1011
export function GetRuleDefinitions(ruleConfig?: Map<string, unknown>, options?: IRulesConfig): IRuleDefinition[] {
1112
const selectedRules: IRuleDefinition[] = [];
12-
const includeBeta = options?.betamode === true;
13+
const includeBeta = options?.betaMode === true || options?.betamode === true;
1314

1415
if (ruleConfig && ruleConfig instanceof Map) {
1516
for (const ruleName of ruleConfig.keys()) {

src/main/libs/ScanFlows.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
RuleResult,
66
ScanResult,
77
} from "../../main/internals/internals";
8+
import { DetailLevel } from "../interfaces/IRulesConfig";
89
import { ParsedFlow } from "../models/ParsedFlow";
910
import { enrichViolationsWithLineNumbers } from "../models/Violation";
1011
import { GetRuleDefinitions } from "./GetRuleDefinitions";
@@ -23,7 +24,12 @@ export function scan(parsedFlows: ParsedFlow[], ruleOptions?: IRulesConfig): Sca
2324
export function ScanFlows(flows: Flow[], ruleOptions?: IRulesConfig): ScanResult[] {
2425
const flowResults: ScanResult[] = [];
2526
let selectedRules: IRuleDefinition[] = [];
26-
27+
const rawMode = ruleOptions?.detailLevel;
28+
const detailLevel =
29+
typeof rawMode === 'string' && rawMode.toLowerCase() === 'simple'
30+
? DetailLevel.SIMPLE
31+
: DetailLevel.ENRICHED;
32+
2733
if (ruleOptions?.rules && Object.keys(ruleOptions.rules).length > 0) {
2834
const ruleMap = new Map<string, object>();
2935
for (const [ruleName, config] of Object.entries(ruleOptions.rules)) {
@@ -33,38 +39,40 @@ export function ScanFlows(flows: Flow[], ruleOptions?: IRulesConfig): ScanResult
3339
} else {
3440
selectedRules = GetRuleDefinitions();
3541
}
36-
37-
// Simple cache for flowXml (generated only when needed)
42+
3843
const flowXmlCache = new Map<string, string>();
39-
44+
4045
for (const flowInput of flows) {
41-
// Ensure it's a Flow instance (for tests with mocked JSONs); no-op if already Flow
4246
const flow = flowInput instanceof Flow ? flowInput : Flow.from(flowInput);
43-
4447
const ruleResults: RuleResult[] = [];
48+
4549
for (const rule of selectedRules) {
4650
try {
4751
if (!rule.supportedTypes.includes(flow.type)) {
4852
ruleResults.push(new RuleResult(rule, []));
4953
continue;
5054
}
55+
5156
let config: object | undefined = undefined;
5257
if (ruleOptions?.rules?.[rule.name]) {
5358
config = ruleOptions.rules[rule.name];
5459
}
60+
5561
const rawSuppressions: string[] | undefined =
5662
ruleOptions?.exceptions?.[flow.name]?.[rule.name];
63+
5764
const suppressions: string[] =
5865
rawSuppressions?.includes("*") ? ["*"] : (rawSuppressions ?? []);
66+
5967
const result =
6068
config && Object.keys(config).length > 0
6169
? rule.execute(flow, config, suppressions)
6270
: rule.execute(flow, undefined, suppressions);
71+
6372
if (result.severity !== rule.severity) {
6473
result.severity = rule.severity as string;
6574
}
66-
67-
// Enrich only if violations exist (and cache XML if needed)
75+
6876
if (result.details.length > 0) {
6977
let flowXml = flowXmlCache.get(flow.name);
7078
if (!flowXml) {
@@ -75,20 +83,29 @@ export function ScanFlows(flows: Flow[], ruleOptions?: IRulesConfig): ScanResult
7583
enrichViolationsWithLineNumbers(result.details, flowXml);
7684
}
7785
}
78-
86+
7987
ruleResults.push(result);
8088
} catch (error) {
8189
const message = `Something went wrong while executing ${rule.name} in the Flow: ${flow.name} with error ${error}`;
8290
ruleResults.push(new RuleResult(rule, [], message));
8391
}
8492
}
93+
8594
flowResults.push(new ScanResult(flow, ruleResults));
86-
87-
// Clear per-flow cache to free memory
8895
flowXmlCache.delete(flow.name);
8996
}
90-
91-
// Clear global cache
97+
9298
flowXmlCache.clear();
99+
100+
if (detailLevel === DetailLevel.SIMPLE) {
101+
flowResults.forEach(scanResult => {
102+
scanResult.ruleResults.forEach(ruleResult => {
103+
ruleResult.details.forEach(violation => {
104+
delete violation.details;
105+
});
106+
});
107+
});
108+
}
109+
93110
return flowResults;
94111
}

tests/ConfigBetaMode.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe("Rule Configurations ", () => {
2121
it.skip("should use default and include beta", async () => {
2222
const flows = await core.parse([example_uri1]);
2323
const ruleConfig = {
24-
betamode: true,
24+
betaMode: true,
2525
exceptions: {
2626
CreateANewAccountWithChild: { DuplicateDMLOperation: ["ViewAccountId"] },
2727
},

tests/UnconnectedElement.test.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import * as core from "../src";
22
import * as path from "path";
3-
43
import { parse } from "../src/main/libs/ParseFlows";
54
import { ParsedFlow } from "../src/main/models/ParsedFlow";
6-
75
import { UnconnectedElement } from "../src/main/rules/UnconnectedElement";
8-
96
import { describe, it, expect } from "@jest/globals";
107

118
describe("UnconnectedElement", () => {
129
const unconnectedElementRule: UnconnectedElement = new UnconnectedElement();
13-
1410
it("there should be checks for unconnected element", async () => {
1511
const connectedElementTestFile = path.join(
1612
__dirname,
@@ -24,7 +20,6 @@ describe("UnconnectedElement", () => {
2420
expect(detail.name).toBe("unused_assignment");
2521
});
2622
});
27-
2823
it("async path there should be checks for unconnected element", async () => {
2924
const connectedElementTestFile = path.join(
3025
__dirname,
@@ -37,7 +32,6 @@ describe("UnconnectedElement", () => {
3732
expect(ruleDetail.name).toBe("UnconnectedElementTestOnAsync");
3833
});
3934
});
40-
4135
it("should fix the unconnected element error", async () => {
4236
const connectedElementTestFile = path.join(
4337
__dirname,
@@ -58,4 +52,23 @@ describe("UnconnectedElement", () => {
5852
const fixedResultsOccurring = newResults[0].ruleResults.filter((rule) => rule.occurs);
5953
expect(fixedResultsOccurring).toHaveLength(0);
6054
});
61-
});
55+
it("should not include enriched details with detailLevel simple", async () => {
56+
const testFile = path.join(
57+
__dirname,
58+
"../assets/example-flows/force-app/main/default/flows/Unconnected_Element.flow-meta.xml"
59+
);
60+
const flows = await core.parse([testFile]);
61+
const ruleConfig = {
62+
detailLevel: "simple",
63+
};
64+
const results: core.ScanResult[] = core.scan(flows, ruleConfig);
65+
const ruleResult = results[0].ruleResults.find((r) => r.ruleName === "UnconnectedElement");
66+
expect(ruleResult).toBeDefined();
67+
expect(ruleResult.occurs).toBe(true);
68+
expect(ruleResult.details).not.toHaveLength(0);
69+
ruleResult.details.forEach((detail) => {
70+
expect(detail.name).toBe("unused_assignment");
71+
expect(detail.details).toBeUndefined();
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)