Skip to content

Commit fedcb0b

Browse files
fix(ipa): exception reason formatting (#918)
1 parent 4a10b79 commit fedcb0b

File tree

5 files changed

+134
-19
lines changed

5 files changed

+134
-19
lines changed

tools/spectral/ipa/__tests__/IPA005ExceptionExtensionFormat.test.js

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@ testRule('xgen-IPA-005-exception-extension-format', [
88
paths: {
99
'/path': {
1010
'x-xgen-IPA-exception': {
11-
'xgen-IPA-100-rule-name': 'Exception',
11+
'xgen-IPA-100-rule-name': 'Exception.',
12+
},
13+
},
14+
'/path-short': {
15+
'x-xgen-IPA-exception': {
16+
'xgen-IPA-100': 'Exception.',
1217
},
1318
},
1419
'/nested': {
1520
post: {
1621
'x-xgen-IPA-exception': {
17-
'xgen-IPA-100-rule-name': 'Exception',
22+
'xgen-IPA-100-rule-name': 'Exception.',
23+
'xgen-IPA-005': 'Short format exception.',
1824
},
1925
},
2026
},
@@ -46,31 +52,108 @@ testRule('xgen-IPA-005-exception-extension-format', [
4652
'xgen-IPA-100-rule-name': {},
4753
},
4854
},
55+
'/path5': {
56+
'x-xgen-IPA-exception': {
57+
'xgen-IPA-1001-rule-name': {},
58+
},
59+
},
60+
'/path6': {
61+
'x-xgen-IPA-exception': {
62+
'xgen-IPA-1001-rule_name': {},
63+
},
64+
},
65+
'/path7': {
66+
'x-xgen-IPA-exception': {
67+
'xgen-IPA_100_rule-name': {},
68+
},
69+
},
70+
'/path8': {
71+
'x-xgen-IPA-exception': {
72+
'xgen-IPA-100-': 'Exception.',
73+
},
74+
},
75+
'/path9': {
76+
'x-xgen-IPA-exception': {
77+
'xgen-IPA-5': 'Exception.',
78+
},
79+
},
80+
'/path10': {
81+
'x-xgen-IPA-exception': {
82+
'xgen-IPA-100': 'exception without uppercase.',
83+
},
84+
},
85+
'/path11': {
86+
'x-xgen-IPA-exception': {
87+
'xgen-IPA-100': 'Exception without period',
88+
},
89+
},
4990
},
5091
},
5192
errors: [
5293
{
5394
code: 'xgen-IPA-005-exception-extension-format',
54-
message: 'IPA exceptions must have a valid rule name and a reason.',
95+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
5596
path: ['paths', '/path1', 'x-xgen-IPA-exception'],
5697
severity: DiagnosticSeverity.Error,
5798
},
5899
{
59100
code: 'xgen-IPA-005-exception-extension-format',
60-
message: 'IPA exceptions must have a valid rule name and a reason.',
61-
path: ['paths', '/path2', 'x-xgen-IPA-exception', 'xgen-IPA-100-rule-name'],
101+
message: 'IPA exceptions must have a non-empty reason that starts with uppercase and ends with a full stop.',
102+
path: ['paths', '/path2', 'x-xgen-IPA-exception'],
103+
severity: DiagnosticSeverity.Error,
104+
},
105+
{
106+
code: 'xgen-IPA-005-exception-extension-format',
107+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
108+
path: ['paths', '/path3', 'x-xgen-IPA-exception'],
109+
severity: DiagnosticSeverity.Error,
110+
},
111+
{
112+
code: 'xgen-IPA-005-exception-extension-format',
113+
message: 'IPA exceptions must have a non-empty reason that starts with uppercase and ends with a full stop.',
114+
path: ['paths', '/path4', 'x-xgen-IPA-exception'],
115+
severity: DiagnosticSeverity.Error,
116+
},
117+
{
118+
code: 'xgen-IPA-005-exception-extension-format',
119+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
120+
path: ['paths', '/path5', 'x-xgen-IPA-exception'],
121+
severity: DiagnosticSeverity.Error,
122+
},
123+
{
124+
code: 'xgen-IPA-005-exception-extension-format',
125+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
126+
path: ['paths', '/path6', 'x-xgen-IPA-exception'],
127+
severity: DiagnosticSeverity.Error,
128+
},
129+
{
130+
code: 'xgen-IPA-005-exception-extension-format',
131+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
132+
path: ['paths', '/path7', 'x-xgen-IPA-exception'],
133+
severity: DiagnosticSeverity.Error,
134+
},
135+
{
136+
code: 'xgen-IPA-005-exception-extension-format',
137+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
138+
path: ['paths', '/path8', 'x-xgen-IPA-exception'],
139+
severity: DiagnosticSeverity.Error,
140+
},
141+
{
142+
code: 'xgen-IPA-005-exception-extension-format',
143+
message: 'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.',
144+
path: ['paths', '/path9', 'x-xgen-IPA-exception'],
62145
severity: DiagnosticSeverity.Error,
63146
},
64147
{
65148
code: 'xgen-IPA-005-exception-extension-format',
66-
message: 'IPA exceptions must have a valid rule name and a reason.',
67-
path: ['paths', '/path3', 'x-xgen-IPA-exception', 'invalid-rule-name'],
149+
message: 'IPA exceptions must have a non-empty reason that starts with uppercase and ends with a full stop.',
150+
path: ['paths', '/path10', 'x-xgen-IPA-exception'],
68151
severity: DiagnosticSeverity.Error,
69152
},
70153
{
71154
code: 'xgen-IPA-005-exception-extension-format',
72-
message: 'IPA exceptions must have a valid rule name and a reason.',
73-
path: ['paths', '/path4', 'x-xgen-IPA-exception', 'xgen-IPA-100-rule-name'],
155+
message: 'IPA exceptions must have a non-empty reason that starts with uppercase and ends with a full stop.',
156+
path: ['paths', '/path11', 'x-xgen-IPA-exception'],
74157
severity: DiagnosticSeverity.Error,
75158
},
76159
],

tools/spectral/ipa/ipa-spectral.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,7 @@ overrides:
214214
- '**#/paths/~1api~1atlas~1v2~1groups~1%7BgroupId%7D~1customDBRoles~1roles~1%7BroleName%7D'
215215
rules:
216216
xgen-IPA-102-collection-identifier-camelCase: 'off'
217+
- files:
218+
- '**' # Matches all files
219+
rules:
220+
xgen-IPA-005-exception-extension-format: 'off'

tools/spectral/ipa/rulesets/IPA-005.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ rules:
1212
##### Implementation details
1313
Rule checks for the following conditions:
1414
- Exception rule names must start with 'xgen-IPA-' prefix
15-
- Each exception must include a non-empty reason as a string
15+
- Exception rule names can be either short format (xgen-IPA-XXX) or full format (xgen-IPA-XXX-rule-name)
16+
- Each exception must include a non-empty reason as a string that starts with uppercase and ends with a full stop
1617
- This rule itself does not allow exceptions
1718
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-005-exception-extension-format'
1819
severity: error

tools/spectral/ipa/rulesets/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ IPA exception extensions must follow the correct format.
2020
##### Implementation details
2121
Rule checks for the following conditions:
2222
- Exception rule names must start with 'xgen-IPA-' prefix
23-
- Each exception must include a non-empty reason as a string
23+
- Exception rule names can be either short format (xgen-IPA-XXX) or full format (xgen-IPA-XXX-rule-name)
24+
- Each exception must include a non-empty reason as a string that starts with uppercase and ends with a full stop
2425
- This rule itself does not allow exceptions
2526

2627

tools/spectral/ipa/rulesets/functions/IPA005ExceptionExtensionFormat.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { evaluateAndCollectAdoptionStatusWithoutExceptions, handleInternalError } from './utils/collectionUtils.js';
22

3-
const ERROR_MESSAGE = 'IPA exceptions must have a valid rule name and a reason.';
4-
const RULE_NAME_PREFIX = 'xgen-IPA-';
3+
const ERROR_MESSAGE_RULENAME_FORMAT =
4+
'IPA exceptions must have a valid key following xgen-IPA-XXX or xgen-IPA-XXX-{rule-name} format.';
5+
const ERROR_MESSAGE_REASON_FORMAT =
6+
'IPA exceptions must have a non-empty reason that starts with uppercase and ends with a full stop.';
7+
const RULE_NAME_PATTERN = /^xgen-IPA-\d{3}(?:-[a-z-]+)?$/;
58

69
// Note: This rule does not allow exceptions
710
export default (input, _, { path, rule }) => {
@@ -10,21 +13,44 @@ export default (input, _, { path, rule }) => {
1013
return evaluateAndCollectAdoptionStatusWithoutExceptions(errors, ruleName, path);
1114
};
1215

13-
function isValidException(ruleName, reason) {
14-
return ruleName.startsWith(RULE_NAME_PREFIX) && typeof reason === 'string' && reason !== '';
16+
function isRuleNameValid(ruleName) {
17+
return RULE_NAME_PATTERN.test(ruleName);
18+
}
19+
20+
function isReasonFormatValid(reason) {
21+
if (reason === null || reason === undefined || typeof reason !== 'string' || reason === '') {
22+
return false;
23+
}
24+
// Check if reason starts with uppercase letter
25+
if (!/^[A-Z]/.test(reason)) {
26+
return false;
27+
}
28+
29+
// Check if reason ends with a full stop
30+
if (!reason.endsWith('.')) {
31+
return false;
32+
}
33+
return true;
1534
}
1635

1736
function checkViolationsAndReturnErrors(input, path, ruleName) {
1837
const errors = [];
1938
try {
2039
const exemptedRules = Object.keys(input);
21-
exemptedRules.forEach((ruleName) => {
22-
const reason = input[ruleName];
23-
if (!isValidException(ruleName, reason)) {
40+
exemptedRules.forEach((key) => {
41+
const reason = input[key];
42+
if (!isRuleNameValid(key)) {
2443
errors.push({
2544
path: path.concat([ruleName]),
26-
message: ERROR_MESSAGE,
45+
message: ERROR_MESSAGE_RULENAME_FORMAT,
2746
});
47+
} else {
48+
if (!isReasonFormatValid(reason)) {
49+
errors.push({
50+
path: path.concat([ruleName]),
51+
message: ERROR_MESSAGE_REASON_FORMAT,
52+
});
53+
}
2854
}
2955
});
3056
return errors;

0 commit comments

Comments
 (0)