Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added linter rule to check maximum allowed component in flow and sub-flow #156

Merged
merged 1 commit into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/available_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,21 @@ EnumMap<LoggerComponent.LogLevel, Integer> excessiveLoggers =
(LoggerComponent.LogLevel.WARN): 2,
(LoggerComponent.LogLevel.ERROR): 2]
```
### FLOW_SUBFLOW_COMPONENT_COUNT

This rule ensures that number of components in a `flow` and `sub-flow` does not exceed maximum allowed component count.
It is recommended that when creating flows and sub-flows in Mulesoft, Single Responsibility Principle is followed to observe good performance, reliability practices, and readability.
Defining maximum allowed component in _flow_ and _sub-flow_ will make the flows more readable.

The constructor for the rule is:

```groovy
FlowSubflowComponentCountRule()
FlowSubflowComponentCountRule(Integer maxCount)
```

*maxCount* is maximum allowed components in a flow or sub-flow.
The default maxCount is set to `20`.

### FLOW_SUBFLOW_NAMING

Expand Down
3 changes: 3 additions & 0 deletions mule-linter-core/AVIOGDSLRuleConfiguration.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ mule_linter {
EXCESSIVE_LOGGERS {
excessiveLoggers = 2
}
FLOW_SUBFLOW_COMPONENT_COUNT{
maxCount = 20
}
FLOW_SUBFLOW_NAMING {
format = 'KEBAB_CASE'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.avioconsulting.mule.linter.rule.configuration

import com.avioconsulting.mule.linter.model.Application
import com.avioconsulting.mule.linter.model.rule.*

/**
* This rule locates flow/sub-flow elements from inside a mule configuration xml files and validates
* that number of components in the flow/sub-flow is less than the maxCount property defined in the linter rule.
*/
class FlowSubflowComponentCountRule extends Rule {
static final String RULE_ID = 'FLOW_SUBFLOW_COMPONENT_COUNT'
static final String RULE_NAME = 'Components in the Flow and subflow should not exceed allowed component count.'
static final String RULE_VIOLATION_MESSAGE = ' has more than the defined components count. Components in the flow/sub-flow: '
static final Integer DEFAULT_MAX_COUNT = 20

/**
* maxCount: maximum allowed components in a flow or sub-flow.
* The default maxCount is `set to 20.
*/
@Param("maxCount") Integer maxCount

FlowSubflowComponentCountRule() {
super(RULE_ID, RULE_NAME, RuleSeverity.MINOR, RuleType.CODE_SMELL)
this.maxCount = DEFAULT_MAX_COUNT
}

FlowSubflowComponentCountRule(String ruleId, String ruleName, Integer maxCount) {
super()
this.maxCount = maxCount
}

@Override
List<RuleViolation> execute(Application application) {
List<RuleViolation> violations = []
application.allFlows.each {flow ->
if(flow.children.size() > maxCount){
violations.add(new RuleViolation(this, flow.file.path,
flow.lineNumber,
flow.name + RULE_VIOLATION_MESSAGE + flow.children.size()))
}
}
return violations
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.avioconsulting.mule.linter.rule.configuration

import com.avioconsulting.mule.linter.TestApplication
import com.avioconsulting.mule.linter.model.MuleApplication
import spock.lang.Specification

class FlowSubflowComponentCountTest extends Specification {

private final TestApplication testApp = new TestApplication()
private MuleApplication app

def setup() {
testApp.initialize()
testApp.addPom()
testApp.addFile('src/main/mule/main.xml', API)
}

def cleanup() {
testApp.remove()
}

def 'Flow Subflow count Rule Success'() {
given:
testApp.buildConfigContent('first-implementation.xml', FIRST_IMPLEMENTATION)
app = new MuleApplication(testApp.appDir)
FlowSubflowComponentCountRule rule = new FlowSubflowComponentCountRule()
// rule.setProperty('maxCount',20)
rule.init()

when:
def violations = rule.execute(app)

then:
violations.size() == 0
}

def 'Flow Subflow count Rule Failure'() {
given:
testApp.buildConfigContent('second-implementation.xml', SECOND_IMPLEMENTATION)
app = new MuleApplication(testApp.appDir)
FlowSubflowComponentCountRule rule = new FlowSubflowComponentCountRule()
// rule.setProperty('maxCount',20)
rule.init()

when:
def violations = rule.execute(app)

then:
violations.size() == 1
violations[0].message == 'business-subflow-four has more than the defined components count. Components in the flow/sub-flow: 22'
}

private static final String API = '''
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:apikit="http://www.mulesoft.org/schema/mule/mule-apikit"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/mule-apikit http://www.mulesoft.org/schema/mule/mule-apikit/current/mule-apikit.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">
\t<http:listener-config name="httpListenerConfig">
\t\t<http:listener-connection host="0.0.0.0" port="8081" />
\t</http:listener-config>
\t<apikit:config name="api-config" api="api.raml" raml="api.raml" outboundHeadersMapName="outboundHeaders" httpStatusVarName="httpStatus" />

\t<flow name="api-main">
\t\t<http:listener config-ref="httpListenerConfig" path="/api/*">
\t\t\t<http:response statusCode="#[vars.httpStatus default 200]">
\t\t\t\t<http:headers><![CDATA[#[output application/java
---
{
\t"x-correlation-id" : vars.correlationId
}]]]></http:headers>
\t\t\t</http:response>
\t\t\t<http:error-response statusCode="#[vars.httpStatus default 500]">
\t\t\t\t<http:body><![CDATA[#[payload]]]></http:body>
\t\t\t\t<http:headers><![CDATA[#[output application/java
---
{
\t"x-correlation-id" : vars.correlationId
}]]]></http:headers>
\t\t\t</http:error-response>
\t\t</http:listener>
\t\t<apikit:router config-ref="api-config" />
\t</flow>

\t<flow name="get:\\v1\\widgets:api-config">
\t\t<flow-ref doc:name="get-widgets" doc:id="450de59d-d74f-4df2-aa99-a7a3a3199ead" name="get-widgets" />
\t</flow>
</mule>'''

private static final String FIRST_IMPLEMENTATION = '''
\t<sub-flow name="business-subflow-one" doc:id="3ce52020-8918-442f-82f5-de9b0d2b87cd" >
\t\t<logger level="INFO" doc:name="Logger" doc:id="97a8bf4d-167b-4d3b-886b-d75db4b74992" />
\t\t<logger level="INFO" doc:name="Logger" doc:id="97a8bf4d-167b-4d3b-886b-d75db4b74993" />
\t\t<logger level="INFO" doc:name="Logger" doc:id="97a8bf4d-167b-4d3b-886b-d75db4b74994" />
\t</sub-flow>
\t<sub-flow name="business-subflow-two" doc:id="576e233d-92f8-4743-bce2-0d2b7eca1591" >
\t\t<logger level="INFO" doc:name="Logger" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb443" />
\t\t<logger level="INFO" doc:name="Logger" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb444" />
\t\t<logger level="INFO" doc:name="Logger" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t</sub-flow>'''

private static final String SECOND_IMPLEMENTATION = '''
\t<sub-flow name="business-subflow-three" doc:id="3ce52020-8918-442f-82f5-de9b0d2b87ce" >
\t\t<logger level="INFO" doc:name="Logger" doc:id="97a8bf4d-167b-4d3b-886b-d75db4b749a2" />
\t\t<logger level="INFO" doc:name="Logger" doc:id="97a8bf4d-167b-4d3b-886b-d75db4b749a3" />
\t</sub-flow>
\t<sub-flow name="business-subflow-four" doc:id="576e233d-92f8-4743-bce2-0d2b7eca1592" >
\t\t<logger level="INFO" doc:name="Logger 1" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 2" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 3" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 4" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 5" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 6" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 7" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 8" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 9" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 10" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 11" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 12" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 13" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 14" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 15" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 16" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 17" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 18" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 19" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 20" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t\t<logger level="INFO" doc:name="Logger 21" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb445" />
\t\t<logger level="INFO" doc:name="Logger 22" doc:id="fdbbf5d7-31cc-411d-a603-14354f4bb446" />
\t</sub-flow>'''
}