Skip to content

Commit 2ada7d9

Browse files
CLOUDP-283086: PoC on validating spectral rule application
1 parent 85da0b0 commit 2ada7d9

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

tools/ipa/rulesets/IPA-104.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
functions:
55
- eachResourceHasGetMethod
6+
- withTracking
67

78
rules:
89
xgen-IPA-104-resource-has-GET:
@@ -12,4 +13,7 @@ rules:
1213
given: "$.paths"
1314
then:
1415
field: "@key"
15-
function: "eachResourceHasGetMethod"
16+
function:
17+
- withTracking
18+
- xgen-IPA-104-resource-has-GET
19+
- function: eachResourceHasGetMethod
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// rules-repo/functions/hitTracker.js
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
const RuleTracker = {
7+
hits: new Map(),
8+
noMatches: new Set(),
9+
10+
trackRule(ruleName, given, results) {
11+
if (!this.hits.has(ruleName)) {
12+
this.hits.set(ruleName, {
13+
matches: 0,
14+
violations: 0,
15+
elements: new Set(),
16+
paths: new Set()
17+
});
18+
}
19+
20+
if (!given || (Array.isArray(given) && given.length === 0)) {
21+
this.noMatches.add(ruleName);
22+
return;
23+
}
24+
25+
const stats = this.hits.get(ruleName);
26+
stats.matches++;
27+
if (results && results.length > 0) {
28+
stats.violations++;
29+
// Track paths where violations occurred
30+
results.forEach(result => {
31+
if (result.path) {
32+
stats.paths.add(result.path);
33+
}
34+
});
35+
}
36+
37+
if (typeof given === 'object') {
38+
Object.keys(given).forEach(path => {
39+
stats.elements.add(path);
40+
});
41+
} else {
42+
stats.elements.add(String(given));
43+
}
44+
45+
// Write report to CI_REPORT_PATH if environment variable is set
46+
if (process.env.CI_REPORT_PATH) {
47+
this.writeReport(process.env.CI_REPORT_PATH);
48+
}
49+
},
50+
51+
getReport() {
52+
return {
53+
ruleStats: Object.fromEntries([...this.hits.entries()].map(([rule, stats]) => [
54+
rule,
55+
{
56+
totalMatches: stats.matches,
57+
violations: stats.violations,
58+
elements: Array.from(stats.elements),
59+
violationPaths: Array.from(stats.paths)
60+
}
61+
])),
62+
noMatches: Array.from(this.noMatches),
63+
timestamp: new Date().toISOString(),
64+
summary: {
65+
totalRules: this.hits.size + this.noMatches.size,
66+
rulesWithMatches: this.hits.size,
67+
rulesWithNoMatches: this.noMatches.size,
68+
totalViolations: [...this.hits.values()].reduce((sum, stat) => sum + stat.violations, 0)
69+
}
70+
};
71+
},
72+
73+
writeReport(reportPath) {
74+
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
75+
fs.writeFileSync(reportPath, JSON.stringify(this.getReport(), null, 2));
76+
}
77+
};
78+
79+
const withTracking = (ruleName, originalRule) => (given, options, context) => {
80+
const results = originalRule(given, options, context);
81+
RuleTracker.trackRule(ruleName, given, results);
82+
return results;
83+
};
84+
85+
module.exports = { RuleTracker, withTracking };

0 commit comments

Comments
 (0)