@sfdxy/mule-lint 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +1 -1
- package/dist/src/mcp/prompts/index.d.ts +1 -1
- package/dist/src/mcp/prompts/index.d.ts.map +1 -1
- package/dist/src/mcp/prompts/index.js +62 -1
- package/dist/src/mcp/prompts/index.js.map +1 -1
- package/dist/src/mcp/resources/index.js +114 -16
- package/dist/src/mcp/resources/index.js.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.d.ts.map +1 -1
- package/dist/src/mcp/tools/getRuleDetails.js +30 -1
- package/dist/src/mcp/tools/getRuleDetails.js.map +1 -1
- package/docs/README.md +87 -27
- package/docs/best-practices/ci-cd.md +135 -0
- package/docs/best-practices/connector-patterns.md +253 -0
- package/docs/best-practices/dataweave-patterns.md +370 -0
- package/docs/best-practices/deployment-2026.md +171 -0
- package/docs/best-practices/error-handling.md +277 -0
- package/docs/best-practices/event-driven-patterns.md +424 -0
- package/docs/best-practices/logging.md +163 -0
- package/docs/best-practices/mulesoft-best-practices.md +72 -865
- package/docs/best-practices/performance.md +273 -0
- package/docs/best-practices/security.md +181 -0
- package/docs/best-practices/testing.md +190 -0
- package/docs/best-practices/variable-contracts.md +191 -0
- package/package.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# CI/CD Integration
|
|
2
|
+
|
|
3
|
+
> **Applies to:** All
|
|
4
|
+
> **Related Rules:** `PROJ-001` · `PROJ-002`
|
|
5
|
+
> **Last Updated:** April 2026
|
|
6
|
+
|
|
7
|
+
## When to Read This
|
|
8
|
+
|
|
9
|
+
Read this when setting up CI/CD pipelines, integrating mule-lint into builds, or configuring quality gates.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Pipeline Stages
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
17
|
+
│ Build │ → │ Lint │ → │ Test │ → │ Package │ → │ Deploy │
|
|
18
|
+
│ │ │ │ │ │ │ │ │ │
|
|
19
|
+
│ mvn │ │ mule- │ │ mvn test │ │ mvn │ │ anypoint │
|
|
20
|
+
│ compile │ │ lint │ │ │ │ package │ │ deploy │
|
|
21
|
+
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## mule-lint Integration
|
|
27
|
+
|
|
28
|
+
### Quality Gate Configuration
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
// .mulelintrc.json
|
|
32
|
+
{
|
|
33
|
+
"include": ["src/main/mule/**/*.xml"],
|
|
34
|
+
"exclude": ["src/test/munit/**/*.xml"],
|
|
35
|
+
"qualityGate": {
|
|
36
|
+
"name": "Project Quality Gate",
|
|
37
|
+
"conditions": [
|
|
38
|
+
{ "metric": "errors", "operator": ">", "threshold": 0, "status": "fail" },
|
|
39
|
+
{ "metric": "warnings", "operator": ">", "threshold": 10, "status": "warn" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"rules": {
|
|
43
|
+
"MULE-001": {
|
|
44
|
+
"enabled": false,
|
|
45
|
+
"reason": "Global error handler in non-standard location — documented exception"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Rule suppression:** When disabling a rule, always include a `reason` or `comment` field explaining why. This documents the engineering decision for future reviewers.
|
|
52
|
+
|
|
53
|
+
### Output Formats
|
|
54
|
+
|
|
55
|
+
| Format | Use Case | Command |
|
|
56
|
+
| -------- | ------------------------------- | ------------------------------------ |
|
|
57
|
+
| `pretty` | Local development | `mule-lint . -f pretty` |
|
|
58
|
+
| `json` | CI/CD parsing | `mule-lint . -f json` |
|
|
59
|
+
| `sarif` | GitHub Code Scanning, AI agents | `mule-lint . -f sarif -o lint.sarif` |
|
|
60
|
+
| `html` | Human review reports | `mule-lint . -f html -o report.html` |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## GitHub Actions Example
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
name: Mule CI/CD
|
|
68
|
+
|
|
69
|
+
on:
|
|
70
|
+
push:
|
|
71
|
+
branches: [main, develop]
|
|
72
|
+
pull_request:
|
|
73
|
+
branches: [main]
|
|
74
|
+
|
|
75
|
+
jobs:
|
|
76
|
+
build-and-test:
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
|
|
81
|
+
- name: Set up JDK 17
|
|
82
|
+
uses: actions/setup-java@v4
|
|
83
|
+
with:
|
|
84
|
+
java-version: '17'
|
|
85
|
+
distribution: 'temurin'
|
|
86
|
+
|
|
87
|
+
- name: Cache Maven packages
|
|
88
|
+
uses: actions/cache@v4
|
|
89
|
+
with:
|
|
90
|
+
path: ~/.m2
|
|
91
|
+
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
|
92
|
+
|
|
93
|
+
- name: Build
|
|
94
|
+
run: mvn -B clean compile
|
|
95
|
+
|
|
96
|
+
- name: Run mule-lint
|
|
97
|
+
run: npx @sfdxy/mule-lint . -c .mulelintrc.json -f sarif -o lint.sarif
|
|
98
|
+
|
|
99
|
+
- name: Upload SARIF results
|
|
100
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
101
|
+
with:
|
|
102
|
+
sarif_file: lint.sarif
|
|
103
|
+
|
|
104
|
+
- name: Run MUnit tests
|
|
105
|
+
run: mvn -B test -Dmule.env=dev -Dsecure.key=test
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Git Branch Strategy
|
|
111
|
+
|
|
112
|
+
| Branch | Purpose | Deployment Target |
|
|
113
|
+
| ----------- | --------------------- | ----------------- |
|
|
114
|
+
| `main` | Production-ready code | Production |
|
|
115
|
+
| `develop` | Integration branch | QA/Staging |
|
|
116
|
+
| `feature/*` | New features | Development |
|
|
117
|
+
| `hotfix/*` | Production fixes | Production |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Commit Messages
|
|
122
|
+
|
|
123
|
+
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
feat: add new order processing logic
|
|
127
|
+
fix: resolve null pointer in mapping
|
|
128
|
+
docs: update README with deployment steps
|
|
129
|
+
chore: upgrade mule maven plugin
|
|
130
|
+
refactor: extract common DWL functions to module
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**See also:** [Testing](testing.md) · [Deployment & Modernization](deployment-2026.md) · [Folder Structure](folder-structure.md)
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Connector Configuration Patterns
|
|
2
|
+
|
|
3
|
+
> **Applies to:** All (System APIs, Process APIs)
|
|
4
|
+
> **Related Rules:** `SEC-007` · `SEC-008` · `PERF-002` · `RES-001` · `RES-002` · `SF-001` · `SF-002`
|
|
5
|
+
> **Last Updated:** April 2026
|
|
6
|
+
|
|
7
|
+
## When to Read This
|
|
8
|
+
|
|
9
|
+
Read this when configuring Salesforce, NetSuite, HTTP, or Database connectors in Mule 4. Covers connector attribute gotchas, the entity-config pattern, and protocol negotiation.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Patterns
|
|
14
|
+
|
|
15
|
+
### Pattern 1: Entity Configuration (YAML-Driven)
|
|
16
|
+
|
|
17
|
+
**Use when:** building a System API that supports multiple entity types (Account, Contact, Order, etc.) with consistent CRUD operations.
|
|
18
|
+
|
|
19
|
+
Instead of hardcoding entity-specific details in flow XML, externalize them to per-entity YAML files:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/main/resources/entity-config/
|
|
23
|
+
├── account.yaml
|
|
24
|
+
├── contact.yaml
|
|
25
|
+
├── opportunity.yaml
|
|
26
|
+
└── order.yaml
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Entity config structure:**
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
# entity-config/account.yaml
|
|
33
|
+
entity:
|
|
34
|
+
account:
|
|
35
|
+
sObjectType: 'Account'
|
|
36
|
+
enabled: true
|
|
37
|
+
externalIdField: 'NetSuite_ID__c'
|
|
38
|
+
queryFields: 'Id, Name, BillingStreet, BillingCity, Phone, Website'
|
|
39
|
+
queryTemplate: 'SELECT {fields} FROM Account WHERE {filter} LIMIT {limit}'
|
|
40
|
+
writeback:
|
|
41
|
+
netSuiteIdField: 'NetSuite_ID__c'
|
|
42
|
+
errorField: 'NetSuite_Error__c'
|
|
43
|
+
lastSyncField: 'Last_NetSuite_Sync__c'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Load in global-config.xml:**
|
|
47
|
+
|
|
48
|
+
```xml
|
|
49
|
+
<configuration-properties doc:name="Account Config"
|
|
50
|
+
file="entity-config/account.yaml"/>
|
|
51
|
+
<configuration-properties doc:name="Contact Config"
|
|
52
|
+
file="entity-config/contact.yaml"/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Benefits:**
|
|
56
|
+
|
|
57
|
+
- Add a new entity by creating a YAML file — no flow XML changes
|
|
58
|
+
- Entity behavior is visible, auditable, and environment-independent
|
|
59
|
+
- LLMs can read entity configs to understand available operations
|
|
60
|
+
|
|
61
|
+
### Pattern 2: Salesforce JWT Connector
|
|
62
|
+
|
|
63
|
+
**Use when:** authenticating to Salesforce via OAuth 2.0 JWT Bearer flow.
|
|
64
|
+
|
|
65
|
+
```xml
|
|
66
|
+
<salesforce:sfdc-config name="Salesforce_Config">
|
|
67
|
+
<salesforce:jwt-connection
|
|
68
|
+
consumerKey="${secure::salesforce.jwt.consumerKey}"
|
|
69
|
+
keyStore="${salesforce.jwt.keystorePath}"
|
|
70
|
+
storePassword="${secure::salesforce.jwt.storePassword}"
|
|
71
|
+
principal="${salesforce.jwt.principal}"
|
|
72
|
+
tokenEndpoint="${salesforce.jwt.tokenEndpoint}"
|
|
73
|
+
audienceUrl="${salesforce.jwt.audienceUrl}">
|
|
74
|
+
<reconnection>
|
|
75
|
+
<reconnect count="3" frequency="5000"/>
|
|
76
|
+
</reconnection>
|
|
77
|
+
</salesforce:jwt-connection>
|
|
78
|
+
</salesforce:sfdc-config>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> ⚠️ **Gotchas** (common mistakes that cause runtime failures):
|
|
82
|
+
>
|
|
83
|
+
> | Incorrect | Correct | Notes |
|
|
84
|
+
> | ----------------------------------- | ------------------------ | ---------------------------------- |
|
|
85
|
+
> | `salesforce:jwt-connection-config` | `salesforce:sfdc-config` | Outer config element name |
|
|
86
|
+
> | `keyStorePath="..."` | `keyStore="..."` | JWT connection attribute |
|
|
87
|
+
> | `type="Account"` (on upsert) | `objectType="Account"` | Upsert operation attribute |
|
|
88
|
+
> | `type="Account"` (on create/update) | `type="Account"` | Create/Update use `type` (correct) |
|
|
89
|
+
|
|
90
|
+
### Pattern 3: Protocol Negotiation (Dual-Protocol SAPI)
|
|
91
|
+
|
|
92
|
+
**Use when:** a System API must support both SOAP and REST protocols to the same backend (e.g., NetSuite).
|
|
93
|
+
|
|
94
|
+
The calling PAPI sends an `x-integration-protocol` header. The SAPI routes internally:
|
|
95
|
+
|
|
96
|
+
```xml
|
|
97
|
+
<!-- common/netsuite-process-subflow.xml -->
|
|
98
|
+
<sub-flow name="netsuite-process-subflow">
|
|
99
|
+
<choice>
|
|
100
|
+
<when expression="#[vars.protocol == 'SOAP']">
|
|
101
|
+
<flow-ref name="netsuite-soap-upsert-subflow"/>
|
|
102
|
+
</when>
|
|
103
|
+
<when expression="#[vars.protocol == 'REST']">
|
|
104
|
+
<flow-ref name="netsuite-rest-upsert-subflow"/>
|
|
105
|
+
</when>
|
|
106
|
+
</choice>
|
|
107
|
+
</sub-flow>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Protocol support matrix:**
|
|
111
|
+
|
|
112
|
+
| Record Type | SOAP | REST |
|
|
113
|
+
| ----------- | ---- | -------------- |
|
|
114
|
+
| Customer | ✅ | ✅ |
|
|
115
|
+
| Contact | ✅ | ✅ |
|
|
116
|
+
| Sales Order | ✅ | ❌ (SOAP-only) |
|
|
117
|
+
| Credit Memo | ✅ | ❌ (SOAP-only) |
|
|
118
|
+
|
|
119
|
+
### Pattern 4: HTTP Request Connector (with Pooling)
|
|
120
|
+
|
|
121
|
+
**Use when:** making outbound HTTP calls to downstream APIs.
|
|
122
|
+
|
|
123
|
+
```xml
|
|
124
|
+
<http:request-config name="SAPI_HTTP_Config"
|
|
125
|
+
responseTimeout="${https.request.responseTimeout}">
|
|
126
|
+
<http:request-connection host="${https.request.host}"
|
|
127
|
+
port="${https.request.port}"
|
|
128
|
+
connectionIdleTimeout="${https.connection.idleTimeout}"
|
|
129
|
+
maxConnections="${https.connection.maxConnections}">
|
|
130
|
+
<reconnection>
|
|
131
|
+
<reconnect frequency="${reconnection.frequency}"
|
|
132
|
+
count="${reconnection.attempts}"/>
|
|
133
|
+
</reconnection>
|
|
134
|
+
</http:request-connection>
|
|
135
|
+
<!-- Set correlation ID once — applied to every request automatically -->
|
|
136
|
+
<http:default-headers>
|
|
137
|
+
<http:default-header key="X-Correlation-Id" value="#[correlationId]"/>
|
|
138
|
+
</http:default-headers>
|
|
139
|
+
</http:request-config>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Rules:**
|
|
143
|
+
|
|
144
|
+
- Always set `responseTimeout` (avoid hanging connections)
|
|
145
|
+
- Set `connectionIdleTimeout >= responseTimeout` (Grizzly kills connections if idle fires first)
|
|
146
|
+
- Configure connection pooling for production (`maxConnections`)
|
|
147
|
+
|
|
148
|
+
### Pattern 5: Reconnection Strategies
|
|
149
|
+
|
|
150
|
+
**Use when:** configuring any connector that connects to an external system.
|
|
151
|
+
|
|
152
|
+
| Connector Type | Strategy | Example |
|
|
153
|
+
| -------------------------------------- | --------------------- | ---------------------------------- |
|
|
154
|
+
| **Event listeners** (SF, MQ) | `reconnect-forever` | Must auto-recover from disconnects |
|
|
155
|
+
| **Outbound connectors** (HTTP, SF, DB) | `reconnect count="3"` | Bounded retries with frequency |
|
|
156
|
+
| **HTTP Listener** | `reconnect-forever` | Server must always be up |
|
|
157
|
+
|
|
158
|
+
```xml
|
|
159
|
+
<!-- Listener — always reconnect -->
|
|
160
|
+
<http:listener-config name="httpListenerConfig">
|
|
161
|
+
<http:listener-connection host="0.0.0.0" port="${http.port}">
|
|
162
|
+
<reconnection>
|
|
163
|
+
<reconnect-forever frequency="5000"/>
|
|
164
|
+
</reconnection>
|
|
165
|
+
</http:listener-connection>
|
|
166
|
+
</http:listener-config>
|
|
167
|
+
|
|
168
|
+
<!-- Outbound — bounded retries -->
|
|
169
|
+
<salesforce:sfdc-config name="Salesforce_Config">
|
|
170
|
+
<salesforce:jwt-connection ...>
|
|
171
|
+
<reconnection>
|
|
172
|
+
<reconnect count="3" frequency="5000"/>
|
|
173
|
+
</reconnection>
|
|
174
|
+
</salesforce:jwt-connection>
|
|
175
|
+
</salesforce:sfdc-config>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Pattern 6: ObjectStore for Reference Data Caching
|
|
179
|
+
|
|
180
|
+
**Use when:** frequently-accessed reference data (customer records, config lookups) is fetched repeatedly during batch processing. Cache in ObjectStore with TTL to reduce API calls.
|
|
181
|
+
|
|
182
|
+
```xml
|
|
183
|
+
<!-- global.xml — configure the cache store -->
|
|
184
|
+
<os:object-store name="customer-cache-store"
|
|
185
|
+
entryTtl="${customer.cache.ttl}"
|
|
186
|
+
expirationInterval="${customer.cache.expirationInterval}"
|
|
187
|
+
maxEntries="200"/>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Key rules:**
|
|
191
|
+
|
|
192
|
+
- Set `maxEntries` to prevent unbounded memory growth
|
|
193
|
+
- Set `entryTtl` appropriate to data volatility (e.g., 1 hour for customer data, 24 hours for country codes)
|
|
194
|
+
- Use `os:contains` + `os:retrieve` to check-then-get, not just `os:retrieve` with default (avoids computing defaults unnecessarily)
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## DWL Utility Module: `parseAndConvert`
|
|
199
|
+
|
|
200
|
+
The Salesforce connector requires Java-typed values (DateTime, Date, Boolean) — not strings. Use a shared DWL module for type coercion:
|
|
201
|
+
|
|
202
|
+
```dataweave
|
|
203
|
+
%dw 2.0
|
|
204
|
+
// dwl/modules/salesforceUtility.dwl
|
|
205
|
+
|
|
206
|
+
var dateTimePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})?$/
|
|
207
|
+
var dateOnlyPattern = /^\d{4}-\d{2}-\d{2}$/
|
|
208
|
+
|
|
209
|
+
fun parseAndConvert(obj: Object) =
|
|
210
|
+
obj mapObject ((value, key) ->
|
|
211
|
+
if ((value is String) and ((value as String) matches dateTimePattern))
|
|
212
|
+
(key): (value as String) as DateTime
|
|
213
|
+
else if ((value is String) and ((value as String) matches dateOnlyPattern))
|
|
214
|
+
(key): (value as String) as Date
|
|
215
|
+
else if ((value is String) and ((value as String) == ""))
|
|
216
|
+
(key): null
|
|
217
|
+
else if (value is Object)
|
|
218
|
+
(key): parseAndConvert(value)
|
|
219
|
+
else
|
|
220
|
+
(key): value
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Usage in flow XML:**
|
|
225
|
+
|
|
226
|
+
```xml
|
|
227
|
+
<ee:transform>
|
|
228
|
+
<ee:set-payload><![CDATA[%dw 2.0
|
|
229
|
+
import dwl::modules::salesforceUtility
|
|
230
|
+
output application/java
|
|
231
|
+
---
|
|
232
|
+
salesforceUtility::parseAndConvert(payload)
|
|
233
|
+
]]></ee:set-payload>
|
|
234
|
+
</ee:transform>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
> ⚠️ **DWL import path rule:** Always use the full path prefix `dwl::modules::moduleName`. Short paths like `modules::moduleName` will fail at runtime.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Checklist
|
|
242
|
+
|
|
243
|
+
- [ ] Entity configs externalized to `entity-config/{entity}.yaml`
|
|
244
|
+
- [ ] All connector credentials use `${secure::...}` property placeholders
|
|
245
|
+
- [ ] Reconnection strategies configured on all connectors
|
|
246
|
+
- [ ] `responseTimeout` explicitly set on HTTP request configs
|
|
247
|
+
- [ ] No hardcoded connector attribute values (hosts, ports, keys)
|
|
248
|
+
- [ ] DWL modules imported with full `dwl::modules::` path prefix
|
|
249
|
+
- [ ] Salesforce payloads pass through `parseAndConvert()` before connector call
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
**See also:** [Security](security.md) · [Performance](performance.md) · [DataWeave Patterns](dataweave-patterns.md)
|