Skip to content

Commit

Permalink
feat: implement max-flow-complexity rule
Browse files Browse the repository at this point in the history
  • Loading branch information
tomcarman committed Jul 23, 2024
1 parent a38b485 commit 14fc15e
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
7 changes: 7 additions & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ rules:
- name: max-flow-complexity
active: true
level: warning
options:
- name: maximumComplexity
value: 10
# 1 - 10: Simple procedure, little risk
# 11 - 20: More complex, moderate risk
# 21 - 50: Complex, high risk
# > 50: Untestable code, very high risk
50 changes: 45 additions & 5 deletions src/rules/max-flow-complexity.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@
import * as fs from 'node:fs';
import type { Flow } from '@salesforce/types/metadata';
import { RuleClass } from '../common/types.js';
import { RuleClass, SingleRuleResult } from '../common/types.js';
import { parseMetadataXml } from '../common/util.js';
import { FlowWrapper } from '../common/types/FlowWrapper.js';
import { RuleOption } from '../common/config-parser.js';

export default class MaxFlowComplexity extends RuleClass {
public ruleId: string = 'no-missing-description-on-fields';
public shortDescriptionText = 'Flow loops should not contain DML operations.';
public fullDescriptionText = 'Flow loops should not contain DML operations.';
public ruleId: string = 'max-flow-complexity';
public startLine = 1;
public endLine = 1;

public maximumComplexity = 10; // Default Value

public constructor(files: string[], level: string, options: RuleOption[]) {
super(files, level);
if (options) {
const maximumComplexity = options.find((ruleOption) => ruleOption.name === 'maximumComplexity');
if (maximumComplexity) {
this.maximumComplexity = maximumComplexity.value as number;
}
}
}

public get shortDescriptionText(): string {
return `Flow has a cyclomatic complexity higher than ${this.maximumComplexity}.`;
}
public get fullDescriptionText(): string {
return `Flow has a cyclomatic complexity higher than ${this.maximumComplexity}.`;
}

/*
Cyclomatic complexity is calculated by: M = E - N + 2P
Where: E = number of edges
N = number of nodes
P = number of connected components (typically 1 for a Flow)
https://en.wikipedia.org/wiki/Cyclomatic_complexity
*/
public execute(): void {
const flows = this.files.filter((file) => file.endsWith('.flow-meta.xml'));

for (const file of flows) {
const fileText = fs.readFileSync(file, 'utf-8');
const flow = parseMetadataXml<Flow>(fileText, 'Flow');
const flowWrapper = new FlowWrapper(flow);
console.log('Flow Name: ', flowWrapper.flowName);

const totalNodes = flowWrapper.nodes.length;
const totalConnectors = flowWrapper.nodes.reduce((cons, node) => cons + node.connectors.length, 0);
const complexity = totalConnectors - totalNodes + 2;

console.log('Flow Name: ', flowWrapper.flowName, ' Complexity: ', complexity);

/* 1 - 10: Simple procedure, little risk
11 - 20: More complex, moderate risk
21 - 50: Complex, high risk
> 50: Untestable code, very high risk
*/

if (complexity > this.maximumComplexity) {
this.results.push(new SingleRuleResult(file, this.startLine, this.endLine, 0, 0));
}
}
}
}

0 comments on commit 14fc15e

Please sign in to comment.