Skip to content

Commit 2045620

Browse files
authored
Add configurations tab (#10)
* update static resource * update banner text * implements rule configs
1 parent 786cbca commit 2045620

File tree

16 files changed

+308
-89
lines changed

16 files changed

+308
-89
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ sf project:deploy:start
5353
sf project sync
5454
```
5555

56-
Want to help improve Lightning Flow Scanner? See our [Contributing Guidelines](https://github.com/Flow-Scanner/lightning-flow-scanner-core/blob/main/CONTRIBUTING.md).
56+
Want to help improve [Lightning Flow Scanner](https://flow-scanner.github.io/lightning-flow-scanner-core/)? See our [Contributing Guidelines](https://github.com/Flow-Scanner/lightning-flow-scanner-core/blob/main/CONTRIBUTING.md).

force-app/main/default/lwc/lightningFlowScanner/lightningFlowScanner.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
<div class="header">
99
<div class="flow-container">
1010
<div class="flow-column">
11-
<div class="info-item"><strong>Name:</strong> {flow.name}</div>
12-
<div class="info-item"><strong>Label:</strong> {flow.label}</div>
13-
<div class="info-item"><strong>Status:</strong> {flow.status}</div>
11+
<div class="info-item"><strong>Name:</strong> {name}</div>
12+
<div class="info-item"><strong>Label:</strong> {metadata.label}</div>
13+
<div class="info-item"><strong>Status:</strong> {metadata.status}</div>
1414
</div>
1515
<div class="flow-column">
16-
<div class="info-item"><strong>Type:</strong> {flow.processType}</div>
17-
<div class="info-item"><strong>API Version:</strong> {flow.apiVersion}</div>
16+
<div class="info-item"><strong>Type:</strong> {metadata.processType}</div>
17+
<div class="info-item"><strong>API Version:</strong> {metadata.apiVersion}</div>
1818
<div class="info-item">
1919
<strong># Rules Run:</strong> {numberOfRules}
2020
</div>
2121
</div>
2222
</div>
23-
<div class="description"><strong>Description:</strong> {flow.description}</div>
23+
<div class="description"><strong>Description:</strong> {metadata.description}</div>
2424
</div>
2525
</template>
2626

@@ -63,7 +63,7 @@
6363
</div>
6464
</template>
6565
<div class="github-star">
66-
<p>Want to help improve Lightning Flow Scanner? See our <a href="https://github.com/Flow-Scanner/lightning-flow-scanner-core/blob/main/CONTRIBUTING.md" target="_blank">Contributing Guidelines</a>.</p>
66+
<p>Want to help improve <a href="https://flow-scanner.github.io/lightning-flow-scanner-core/">Lightning Flow Scanner</a>? See our <a href="https://github.com/Flow-Scanner/lightning-flow-scanner-core/blob/main/CONTRIBUTING.md" target="_blank">Contributing Guidelines</a>.</p>
6767
</div>
6868
</div>
6969
</template>
Lines changed: 5 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,17 @@
1-
import { LightningElement, track, api, wire } from 'lwc';
2-
import { loadScript } from 'lightning/platformResourceLoader';
3-
import lfs from '@salesforce/resourceUrl/LFS';
1+
import { LightningElement, api, track } from 'lwc';
42

53
export default class LightningFlowScanner extends LightningElement {
64
@api name;
75
@api metadata;
8-
9-
@track numberOfRules;
10-
@track flow;
11-
@track scanResult;
12-
@track error;
13-
14-
scriptLoaded = false;
15-
16-
connectedCallback() {
17-
loadScript(this, lfs)
18-
.then(() => {
19-
try {
20-
this.numberOfRules = lightningflowscanner.getRules().length;
21-
this.flow = new lightningflowscanner.Flow(this.name, this.metadata);
22-
23-
let uri = '/services/data/v60.0/tooling/sobjects/Flow/' + this.id;
24-
let parsedFlow = { uri, flow: this.flow };
25-
26-
try {
27-
let scanResults = lightningflowscanner.scan([parsedFlow]);
28-
this.scanResult = scanResults[0];
29-
30-
// Add unique keys to each rule result and its details
31-
this.scanResult.ruleResults = this.scanResult.ruleResults.map((ruleResult, ruleIndex) => {
32-
return {
33-
...ruleResult,
34-
id: `rule-${ruleIndex}`,
35-
details: ruleResult.details.map((detail, detailIndex) => {
36-
return { ...detail, id: `rule-${ruleIndex}-detail-${detailIndex}` };
37-
})
38-
};
39-
});
40-
} catch (e) {
41-
this.error = e;
42-
console.error('Error scanning flow:', e);
43-
}
44-
} catch (error) {
45-
this.error = error;
46-
console.error('Error parsing flow:', error);
47-
}
48-
})
49-
.catch(error => {
50-
this.error = error;
51-
console.error('Error loading JavaScript file:', error);
52-
});
53-
}
6+
@api scanResult;
7+
@api numberOfRules;
8+
@api error;
549

5510
get hasScanResults() {
5611
return this.scanResult && this.scanResult.ruleResults && this.scanResult.ruleResults.length > 0;
5712
}
5813

5914
get flowName() {
60-
return this.flow ? this.flow.name : '';
15+
return this.name || '';
6116
}
62-
6317
}

force-app/main/default/lwc/lightningFlowScannerApp/lightningFlowScannerApp.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ footer {
4949
bottom: 0;
5050
width: 100%;
5151
}
52+
lightning-spinner {
53+
display: block;
54+
margin: 2rem auto;
55+
}

force-app/main/default/lwc/lightningFlowScannerApp/lightningFlowScannerApp.html

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,27 @@
33
<main>
44
<ul class="tabs">
55
<li class={FlowsClass} data-tab="1" onclick={handleTabClick}>Flows</li>
6-
<li class={AnalysisClass} data-tab="2" onclick={handleTabClick}>Analysis</li>
6+
<li class={AnalysisClass} data-tab="2" onclick={handleTabClick}>Results</li>
7+
<li class={ConfigClass} data-tab="3" onclick={handleTabClick}>Configuration</li>
78
</ul>
89
<div class="tab-content">
10+
<template if:true={isLoading}>
11+
<lightning-spinner alternative-text="Loading Flow Metadata..." size="medium"></lightning-spinner>
12+
</template>
13+
914
<template if:true={err}>
1015
<p>Error fetching Flows: {err}</p>
1116
</template>
1217
<template if:true={isTab1Active}>
1318
<c-flow-overview records={records} err={err} onscanflow={handleScanFlow}></c-flow-overview>
1419
</template>
1520
<template if:true={isTab2Active}>
16-
<c-lightning-flow-scanner name={flowName} metadata={flowMetadata}></c-lightning-flow-scanner>
21+
<c-lightning-flow-scanner name={flowName} metadata={flowMetadata} scan-result={scanResult} number-of-rules={numberOfRules} error={err}></c-lightning-flow-scanner>
22+
</template>
23+
<template if:true={isTab3Active}>
24+
<c-scan-configurator rules={rules} onrulechange={handleRuleChange}></c-scan-configurator>
1725
</template>
1826
</div>
1927
</main>
2028
</div>
21-
</template>
29+
</template>

force-app/main/default/lwc/lightningFlowScannerApp/lightningFlowScannerApp.js

Lines changed: 139 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ export default class lightningFlowScannerApp extends LightningElement {
1111
@track selectedFlowRecord = null;
1212
@track flowMetadata = null;
1313
@track flowName;
14+
@track scanResult;
15+
@track numberOfRules;
16+
@track rules = [];
17+
@track rulesConfig = null;
18+
@track isLoading = false;
1419
conn;
20+
scriptLoaded = false;
1521

1622
get isTab1Active() {
1723
return this.activeTab === 1;
@@ -21,6 +27,10 @@ export default class lightningFlowScannerApp extends LightningElement {
2127
return this.activeTab === 2;
2228
}
2329

30+
get isTab3Active() {
31+
return this.activeTab === 3;
32+
}
33+
2434
get FlowsClass() {
2535
return this.activeTab === 1 ? 'active' : '';
2636
}
@@ -29,9 +39,37 @@ export default class lightningFlowScannerApp extends LightningElement {
2939
return this.activeTab === 2 ? 'active' : '';
3040
}
3141

42+
get ConfigClass() {
43+
return this.activeTab === 3 ? 'active' : '';
44+
}
45+
3246
async connectedCallback() {
3347
try {
34-
await loadScript(this, LFSStaticRessource + '/js/jsforce.js');
48+
await Promise.all([
49+
loadScript(this, LFSStaticRessource + '/jsforce.js'),
50+
loadScript(this, LFSStaticRessource + '/LFS.js')
51+
]);
52+
this.scriptLoaded = true;
53+
54+
// Fetch rules for Configuration tab, default all to active
55+
this.rules = lightningflowscanner.getRules().map((rule, index) => ({
56+
id: `rule-${index}`,
57+
name: rule.name,
58+
description: rule.description,
59+
severity: rule.severity,
60+
category: rule.category,
61+
isActive: true // Default all rules to active
62+
}));
63+
64+
// Initialize rulesConfig with all rules (correct format: { rules: { [name]: { severity } } })
65+
this.rulesConfig = {
66+
rules: this.rules.reduce((acc, rule) => {
67+
acc[rule.name] = { severity: rule.severity }; // Include default severity
68+
return acc;
69+
}, {})
70+
};
71+
console.log('Initial rulesConfig:', JSON.stringify(this.rulesConfig));
72+
3573
let SF_API_VERSION = '60.0';
3674
this.conn = new jsforce.Connection({
3775
accessToken: this.accessToken,
@@ -48,7 +86,7 @@ export default class lightningFlowScannerApp extends LightningElement {
4886
isActive: !!record.ActiveVersionId,
4987
masterLabel: record.ActiveVersionId ? record.ActiveVersion.MasterLabel : record.LatestVersion.MasterLabel,
5088
processType: record.ActiveVersionId ? record.ActiveVersion.ProcessType : record.LatestVersion.ProcessType,
51-
versionId: record.ActiveVersionId ? record.ActiveVersionId : record.LatestVersionId
89+
versionId: record.ActiveVersionId ? record.ActiveVersionId : record.LatestVersionId // Fixed typo
5290
}));
5391

5492
if (this.records.length > 0) {
@@ -58,42 +96,123 @@ export default class lightningFlowScannerApp extends LightningElement {
5896
}
5997
} catch (error) {
6098
this.err = error.message;
61-
console.error(error.message);
99+
console.error('Error in connectedCallback:', error);
62100
}
63101
}
64102

65103
async loadFlowMetadata(record) {
66104
try {
67-
let id = record.versionId;
68-
const metadataRes = await this.conn.tooling.query(`SELECT Id, Fullname, Metadata FROM Flow WHERE Id = '${id}' LIMIT 1`);
69-
let fullname = metadataRes.records[0].FullName;
70-
console.log('fn', fullname);
71-
let fmd = metadataRes.records[0].Metadata;
72-
if (metadataRes && metadataRes.records) {
73-
this.flowName = fullname;
74-
this.flowMetadata = fmd;
105+
this.isLoading = true;
106+
const id = record.versionId;
107+
const metadataRes = await this.conn.tooling.query(
108+
`SELECT Id, FullName, Metadata FROM Flow WHERE Id = '${id}' LIMIT 1`
109+
);
110+
111+
if (metadataRes && metadataRes.records.length) {
112+
const flow = metadataRes.records[0];
113+
this.flowName = flow.FullName;
114+
this.flowMetadata = flow.Metadata;
115+
await this.scanFlow(this.rulesConfig); // Pass rulesConfig to scan
75116
}
76117
} catch (error) {
77118
this.err = error.message;
78-
console.error(error.message);
119+
console.error('Error in loadFlowMetadata:', error);
120+
} finally {
121+
this.isLoading = false;
79122
}
80123
}
81124

82-
handleTabClick(event) {
83-
this.activeTab = parseInt(event.currentTarget.dataset.tab, 10);
125+
async scanFlow(ruleOptions) {
126+
if (!this.scriptLoaded || !this.flowName || !this.flowMetadata) {
127+
return;
128+
}
129+
try {
130+
this.isLoading = true;
131+
// Log ruleOptions for debugging
132+
console.log('Scanning with ruleOptions:', JSON.stringify(ruleOptions));
133+
// Use only active rules for numberOfRules
134+
this.numberOfRules = ruleOptions && ruleOptions.rules ? Object.keys(ruleOptions.rules).length : lightningflowscanner.getRules().length;
135+
const flow = new lightningflowscanner.Flow(this.flowName, this.flowMetadata);
136+
137+
let uri = '/services/data/v60.0/tooling/sobjects/Flow/' + this.selectedFlowRecord.versionId;
138+
let parsedFlow = { uri, flow };
139+
140+
try {
141+
let scanResults = lightningflowscanner.scan([parsedFlow], ruleOptions);
142+
this.scanResult = scanResults[0];
143+
console.log('Raw scan results ruleResults count:', this.scanResult.ruleResults.length);
144+
console.log('Sample raw ruleResult structure:', JSON.stringify(this.scanResult.ruleResults[0] || {}));
84145

146+
// Fallback: Filter scan results to include only active rules
147+
const activeRuleNames = ruleOptions && ruleOptions.rules ? Object.keys(ruleOptions.rules) : [];
148+
if (this.scanResult && this.scanResult.ruleResults && activeRuleNames.length > 0) {
149+
this.scanResult.ruleResults = this.scanResult.ruleResults.filter(ruleResult => {
150+
if (!ruleResult.ruleName) {
151+
console.warn('Skipping ruleResult due to missing ruleName:', JSON.stringify(ruleResult));
152+
return false;
153+
}
154+
return activeRuleNames.includes(ruleResult.ruleName);
155+
});
156+
console.log('Filtered scan results ruleResults count:', this.scanResult.ruleResults.length);
157+
}
158+
159+
// Add unique keys to each rule result and its details
160+
this.scanResult.ruleResults = this.scanResult.ruleResults.map((ruleResult, ruleIndex) => {
161+
return {
162+
...ruleResult,
163+
id: `rule-${ruleIndex}`,
164+
details: ruleResult.details.map((detail, detailIndex) => {
165+
return { ...detail, id: `rule-${ruleIndex}-detail-${detailIndex}` };
166+
})
167+
};
168+
});
169+
} catch (e) {
170+
this.err = e.message;
171+
console.error('Error scanning flow:', e);
172+
}
173+
} catch (error) {
174+
this.err = error.message;
175+
console.error('Error parsing flow:', error);
176+
} finally {
177+
this.isLoading = false;
178+
}
179+
}
85180

181+
handleTabClick(event) {
182+
this.activeTab = parseInt(event.currentTarget.dataset.tab, 10);
86183
}
87184

88-
handleScanFlow(event) {
89-
console.log('scan');
185+
async handleScanFlow(event) {
90186
const flowId = event.detail.flowId;
91187
const record = this.records.find(rec => rec.id === flowId);
92-
188+
93189
if (record) {
94-
this.loadFlowMetadata(record, this.conn);
190+
this.isLoading = true;
95191
this.selectedFlowRecord = record;
192+
try {
193+
await this.loadFlowMetadata(record);
194+
this.activeTab = 2;
195+
} catch (error) {
196+
this.err = error.message;
197+
console.error('Error in handleScanFlow:', error);
198+
}
199+
}
200+
}
201+
202+
async handleRuleChange(event) {
203+
const updatedRules = event.detail.rules;
204+
this.rules = updatedRules;
205+
this.rulesConfig = {
206+
rules: updatedRules.filter(rule => rule.isActive).reduce((acc, rule) => {
207+
acc[rule.name] = { severity: rule.severity }; // Include user-selected severity
208+
return acc;
209+
}, {})
210+
};
211+
console.log('Updated rulesConfig:', JSON.stringify(this.rulesConfig));
212+
213+
// Re-run scan if a flow is already selected
214+
if (this.flowName && this.flowMetadata && this.selectedFlowRecord) {
215+
await this.scanFlow(this.rulesConfig);
96216
}
97-
this.activeTab = 2;
98217
}
99-
}
218+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { createElement } from '@lwc/engine-dom';
2+
import ScanConfigurator from 'c/scanConfigurator';
3+
4+
describe('c-scan-configurator', () => {
5+
afterEach(() => {
6+
// The jsdom instance is shared across test cases in a single file so reset the DOM
7+
while (document.body.firstChild) {
8+
document.body.removeChild(document.body.firstChild);
9+
}
10+
});
11+
12+
it('TODO: test case generated by CLI command, please fill in test logic', () => {
13+
// Arrange
14+
const element = createElement('c-scan-configurator', {
15+
is: ScanConfigurator
16+
});
17+
18+
// Act
19+
document.body.appendChild(element);
20+
21+
// Assert
22+
// const div = element.shadowRoot.querySelector('div');
23+
expect(1).toBe(1);
24+
});
25+
});

0 commit comments

Comments
 (0)