diff --git a/readme.md b/readme.md index 99366e2..ff8c0d0 100644 --- a/readme.md +++ b/readme.md @@ -27,6 +27,7 @@ _An Extensible Rule Engine for Salesforce Flows used by the Lightning Flow Scann | **Unconnected Element** ([`UnconnectedElement`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/UnconnectedElement.ts)) | Unconnected elements which are not being used by the Flow should be avoided to keep Flows efficient and maintainable. | | **Unused Variable** ([`UnusedVariable`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/UnusedVariable.ts)) | To maintain the efficiency and manageability of your Flow, it's advisable to avoid including unconnected variables that are not in use. | | **Process Builder** ([`ProcessBuilder`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/ProcessBuilder.ts)) | Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Ensure you're prepared for this transition by migrating your organization's automation to Flow. Refer to official documentation for more information on the transition process and tools available. | +| **Inactive Flow** ([`InactiveFlow`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/InactiveFlow.ts)) | It's better to delete flows that are no longer used rather than have them be inactive. Inactive flows can still delete records when testing them, and parent flows will run an inactive subflow if no active version is found. | ## Configurations diff --git a/src/main/rules/InactiveFlow.ts b/src/main/rules/InactiveFlow.ts new file mode 100644 index 0000000..9078b2f --- /dev/null +++ b/src/main/rules/InactiveFlow.ts @@ -0,0 +1,33 @@ +import { RuleCommon } from '../models/RuleCommon'; +import * as core from '../internals/internals'; + +export class InactiveFlow extends RuleCommon implements core.IRuleDefinition { + + constructor() { + super({ + name: 'InactiveFlow', + label: 'Inactive Flow', + description: 'It\'s better to delete flows that are no longer used rather than have them be inactive. Inactive flows can still delete records when testing them, and parent flows will run an inactive subflow if no active version is found.', + supportedTypes: core.FlowType.allTypes(), + docRefs: [], + isConfigurable: true, + autoFixable: false + }, + ); + } + + public execute(flow: core.Flow): core.RuleResult { + const inactiveFlows = []; + for (const node of flow.elements) { + const nodeElementString = JSON.stringify(node.element); + if (node.subtype == "status" && nodeElementString != '\"Active\"') { + inactiveFlows.push(node); + } + } + let results = []; + for (const det of inactiveFlows) { + results.push(new core.ResultDetails(det)); + } + return new core.RuleResult(this, results); + } +} \ No newline at end of file diff --git a/src/main/store/DefaultRuleStore.ts b/src/main/store/DefaultRuleStore.ts index 690c961..3f7eec2 100644 --- a/src/main/store/DefaultRuleStore.ts +++ b/src/main/store/DefaultRuleStore.ts @@ -6,6 +6,7 @@ import { DuplicateDMLOperation } from '../rules/DuplicateDMLOperation'; import { FlowDescription } from '../rules/FlowDescription'; import { FlowName } from '../rules/FlowName'; import { HardcodedId } from '../rules/HardcodedId'; +import { InactiveFlow } from '../rules/InactiveFlow'; import { MissingFaultPath } from '../rules/MissingFaultPath'; import { MissingNullHandler } from '../rules/MissingNullHandler'; import { ProcessBuilder } from '../rules/ProcessBuilder'; @@ -27,5 +28,6 @@ export const DefaultRuleStore: {} = { ProcessBuilder, SOQLQueryInLoop, UnconnectedElement, - UnusedVariable + UnusedVariable, + InactiveFlow, }; \ No newline at end of file diff --git a/tests/InactiveFlow.test.ts b/tests/InactiveFlow.test.ts new file mode 100644 index 0000000..9020e0e --- /dev/null +++ b/tests/InactiveFlow.test.ts @@ -0,0 +1,36 @@ +import { assert, expect } from 'chai'; +import 'mocha'; +import * as core from '../src'; +import obsolete from './testfiles/ObsoleteFlow_Demo.json'; +import active from './testfiles/ActiveFlow_Demo.json'; + +describe('In the ObsoleteFlow flow', () => { + let obsoleteflow, activeflow: core.Flow; + + before('arrange', () => { + // ARRANGE + obsoleteflow = new core.Flow({ + path: './testfiles/ObsoleteFlow_Demo.flow', + xmldata: obsolete, + }); + activeflow = new core.Flow({ + path: './testfiles/ActiveFlow_Demo.flow', + xmldata: active, + }); + }); + + it('there should be one result for the rule InactiveFlow', () => { + + const results: core.ScanResult[] = core.scan([obsoleteflow]); + const occurringResults = results[0].ruleResults.filter((rule) => rule.occurs); + expect(occurringResults.length).to.equal(1); + expect(occurringResults[0].ruleName).to.equal("InactiveFlow"); + }); + + it('there should be no results for an active flow', () => { + + const results: core.ScanResult[] = core.scan([activeflow]); + const occurringResults = results[0].ruleResults.filter((rule) => rule.occurs); + expect(occurringResults.length).to.equal(0); + }); +}); \ No newline at end of file diff --git a/tests/testfiles/ActiveFlow_Demo.json b/tests/testfiles/ActiveFlow_Demo.json new file mode 100644 index 0000000..f44a038 --- /dev/null +++ b/tests/testfiles/ActiveFlow_Demo.json @@ -0,0 +1,66 @@ +{ + "Flow": { + "$": { + "xmlns": "http://soap.sforce.com/2006/04/metadata" + }, + "apiVersion": [ + "58.0" + ], + "description": [ + "This flow demonstrates an active flow that does not violate the rule \"Inactive Flow\"." + ], + "environments": [ + "Default" + ], + "interviewLabel": [ + "Active Flow {!$Flow.CurrentDateTime}" + ], + "label": [ + "Active Flow" + ], + "processMetadataValues": [ + { + "name": [ + "BuilderType" + ], + "value": [ + { + "stringValue": [ + "LightningFlowBuilder" + ] + } + ] + }, + { + "name": [ + "CanvasMode" + ], + "value": [ + { + "stringValue": [ + "AUTO_LAYOUT_CANVAS" + ] + } + ] + }, + { + "name": [ + "OriginBuilderType" + ], + "value": [ + { + "stringValue": [ + "LightningFlowBuilder" + ] + } + ] + } + ], + "processType": [ + "AutoLaunchedFlow" + ], + "status": [ + "Active" + ] + } +} \ No newline at end of file diff --git a/tests/testfiles/ObsoleteFlow_Demo.json b/tests/testfiles/ObsoleteFlow_Demo.json new file mode 100644 index 0000000..e386c75 --- /dev/null +++ b/tests/testfiles/ObsoleteFlow_Demo.json @@ -0,0 +1,66 @@ +{ + "Flow": { + "$": { + "xmlns": "http://soap.sforce.com/2006/04/metadata" + }, + "apiVersion": [ + "58.0" + ], + "description": [ + "This flow demonstrates a violation of the rule \"Inactive Flow\"." + ], + "environments": [ + "Default" + ], + "interviewLabel": [ + "Obsolete Flow {!$Flow.CurrentDateTime}" + ], + "label": [ + "Obsolete Flow" + ], + "processMetadataValues": [ + { + "name": [ + "BuilderType" + ], + "value": [ + { + "stringValue": [ + "LightningFlowBuilder" + ] + } + ] + }, + { + "name": [ + "CanvasMode" + ], + "value": [ + { + "stringValue": [ + "AUTO_LAYOUT_CANVAS" + ] + } + ] + }, + { + "name": [ + "OriginBuilderType" + ], + "value": [ + { + "stringValue": [ + "LightningFlowBuilder" + ] + } + ] + } + ], + "processType": [ + "AutoLaunchedFlow" + ], + "status": [ + "Obsolete" + ] + } +} \ No newline at end of file