Skip to content

Commit e050b2d

Browse files
Fix on IPA-112 rule implementation (#594)
1 parent f491bba commit e050b2d

File tree

6 files changed

+366
-25
lines changed

6 files changed

+366
-25
lines changed

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

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,147 @@ testRule('xgen-IPA-112-boolean-field-names-avoid-is-prefix', [
177177
},
178178
errors: [],
179179
},
180+
{
181+
name: 'schema with referenced property types',
182+
document: {
183+
components: {
184+
schemas: {
185+
BooleanProperties: {
186+
type: 'object',
187+
properties: {
188+
active: { type: 'boolean' },
189+
isEnabled: { type: 'boolean' },
190+
disabled: { type: 'boolean' },
191+
},
192+
},
193+
User: {
194+
type: 'object',
195+
properties: {
196+
userId: { type: 'string' },
197+
name: { type: 'string' },
198+
status: { $ref: '#/components/schemas/BooleanProperties' },
199+
isAdmin: { type: 'boolean' },
200+
preferences: {
201+
type: 'object',
202+
properties: {
203+
isEmailNotificationsEnabled: { type: 'boolean' },
204+
darkMode: { type: 'boolean' },
205+
},
206+
},
207+
},
208+
},
209+
},
210+
},
211+
paths: {
212+
'/users/{userId}': {
213+
get: {
214+
responses: {
215+
200: {
216+
content: {
217+
'application/vnd.atlas.2024-01-01+json': {
218+
schema: {
219+
type: 'object',
220+
properties: {
221+
user: { $ref: '#/components/schemas/User' },
222+
isCached: { type: 'boolean' },
223+
},
224+
},
225+
},
226+
},
227+
},
228+
},
229+
},
230+
},
231+
},
232+
},
233+
errors: [
234+
{
235+
code: 'xgen-IPA-112-boolean-field-names-avoid-is-prefix',
236+
message: 'Boolean field "isEnabled" should not use the "is" prefix. Use "enabled" instead.',
237+
path: ['components', 'schemas', 'BooleanProperties', 'properties', 'isEnabled'],
238+
severity: DiagnosticSeverity.Warning,
239+
},
240+
{
241+
code: 'xgen-IPA-112-boolean-field-names-avoid-is-prefix',
242+
message: 'Boolean field "isAdmin" should not use the "is" prefix. Use "admin" instead.',
243+
path: ['components', 'schemas', 'User', 'properties', 'isAdmin'],
244+
severity: DiagnosticSeverity.Warning,
245+
},
246+
{
247+
code: 'xgen-IPA-112-boolean-field-names-avoid-is-prefix',
248+
message:
249+
'Boolean field "isEmailNotificationsEnabled" should not use the "is" prefix. Use "emailNotificationsEnabled" instead.',
250+
path: [
251+
'components',
252+
'schemas',
253+
'User',
254+
'properties',
255+
'preferences',
256+
'properties',
257+
'isEmailNotificationsEnabled',
258+
],
259+
severity: DiagnosticSeverity.Warning,
260+
},
261+
{
262+
code: 'xgen-IPA-112-boolean-field-names-avoid-is-prefix',
263+
message: 'Boolean field "isCached" should not use the "is" prefix. Use "cached" instead.',
264+
path: [
265+
'paths',
266+
'/users/{userId}',
267+
'get',
268+
'responses',
269+
'200',
270+
'content',
271+
'application/vnd.atlas.2024-01-01+json',
272+
'schema',
273+
'properties',
274+
'isCached',
275+
],
276+
severity: DiagnosticSeverity.Warning,
277+
},
278+
],
279+
},
280+
{
281+
name: 'schema with referenced property and exceptions',
282+
document: {
283+
components: {
284+
schemas: {
285+
UserSettings: {
286+
type: 'object',
287+
properties: {
288+
isVerified: {
289+
type: 'boolean',
290+
'x-xgen-IPA-exception': {
291+
'xgen-IPA-112-boolean-field-names-avoid-is-prefix': 'Reason',
292+
},
293+
},
294+
isMfaEnabled: { type: 'boolean' },
295+
},
296+
},
297+
UserProfile: {
298+
type: 'object',
299+
properties: {
300+
name: { type: 'string' },
301+
email: { type: 'string' },
302+
settings: { $ref: '#/components/schemas/UserSettings' },
303+
isPremiumUser: {
304+
type: 'boolean',
305+
'x-xgen-IPA-exception': {
306+
'xgen-IPA-112-boolean-field-names-avoid-is-prefix': 'Reason',
307+
},
308+
},
309+
},
310+
},
311+
},
312+
},
313+
},
314+
errors: [
315+
{
316+
code: 'xgen-IPA-112-boolean-field-names-avoid-is-prefix',
317+
message: 'Boolean field "isMfaEnabled" should not use the "is" prefix. Use "mfaEnabled" instead.',
318+
path: ['components', 'schemas', 'UserSettings', 'properties', 'isMfaEnabled'],
319+
severity: DiagnosticSeverity.Warning,
320+
},
321+
],
322+
},
180323
]);

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

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,4 +461,152 @@ testRule('xgen-IPA-112-field-names-are-camel-case', [
461461
},
462462
errors: [],
463463
},
464+
{
465+
name: 'schema with referenced property and nested objects',
466+
document: {
467+
components: {
468+
schemas: {
469+
Address: {
470+
type: 'object',
471+
properties: {
472+
State_Name: { type: 'string' },
473+
zipCode: { type: 'string' },
474+
},
475+
},
476+
User: {
477+
type: 'object',
478+
properties: {
479+
userId: { type: 'string' },
480+
firstName: { type: 'string' },
481+
Last_Name: { type: 'string' },
482+
primary_address: { $ref: '#/components/schemas/Address' },
483+
contactInfo: {
484+
type: 'object',
485+
properties: {
486+
email_address: { type: 'string' },
487+
phoneNumber: { type: 'string' },
488+
},
489+
},
490+
},
491+
},
492+
},
493+
},
494+
paths: {
495+
'/users/{userId}': {
496+
get: {
497+
responses: {
498+
200: {
499+
content: {
500+
'application/vnd.atlas.2024-01-01+json': {
501+
schema: {
502+
type: 'object',
503+
properties: {
504+
primary_user: { $ref: '#/components/schemas/User' },
505+
REQUEST_ID: { type: 'string' },
506+
},
507+
},
508+
},
509+
},
510+
},
511+
},
512+
},
513+
},
514+
},
515+
},
516+
errors: [
517+
{
518+
code: 'xgen-IPA-112-field-names-are-camel-case',
519+
message: 'Property "State_Name" must use camelCase format.',
520+
path: ['components', 'schemas', 'Address', 'properties', 'State_Name'],
521+
severity: DiagnosticSeverity.Warning,
522+
},
523+
{
524+
code: 'xgen-IPA-112-field-names-are-camel-case',
525+
message: 'Property "Last_Name" must use camelCase format.',
526+
path: ['components', 'schemas', 'User', 'properties', 'Last_Name'],
527+
severity: DiagnosticSeverity.Warning,
528+
},
529+
{
530+
code: 'xgen-IPA-112-field-names-are-camel-case',
531+
message: 'Property "primary_address" must use camelCase format.',
532+
path: ['components', 'schemas', 'User', 'properties', 'primary_address'],
533+
severity: DiagnosticSeverity.Warning,
534+
},
535+
{
536+
code: 'xgen-IPA-112-field-names-are-camel-case',
537+
message: 'Property "email_address" must use camelCase format.',
538+
path: ['components', 'schemas', 'User', 'properties', 'contactInfo', 'properties', 'email_address'],
539+
severity: DiagnosticSeverity.Warning,
540+
},
541+
{
542+
code: 'xgen-IPA-112-field-names-are-camel-case',
543+
message: 'Property "primary_user" must use camelCase format.',
544+
path: [
545+
'paths',
546+
'/users/{userId}',
547+
'get',
548+
'responses',
549+
'200',
550+
'content',
551+
'application/vnd.atlas.2024-01-01+json',
552+
'schema',
553+
'properties',
554+
'primary_user',
555+
],
556+
severity: DiagnosticSeverity.Warning,
557+
},
558+
{
559+
code: 'xgen-IPA-112-field-names-are-camel-case',
560+
message: 'Property "REQUEST_ID" must use camelCase format.',
561+
path: [
562+
'paths',
563+
'/users/{userId}',
564+
'get',
565+
'responses',
566+
'200',
567+
'content',
568+
'application/vnd.atlas.2024-01-01+json',
569+
'schema',
570+
'properties',
571+
'REQUEST_ID',
572+
],
573+
severity: DiagnosticSeverity.Warning,
574+
},
575+
],
576+
},
577+
{
578+
name: 'schema with referenced property and exceptions',
579+
document: {
580+
components: {
581+
schemas: {
582+
ApiSettings: {
583+
type: 'object',
584+
properties: {
585+
API_KEY: {
586+
type: 'string',
587+
'x-xgen-IPA-exception': {
588+
'xgen-IPA-112-field-names-are-camel-case': 'Reason',
589+
},
590+
},
591+
},
592+
},
593+
UserProfile: {
594+
type: 'object',
595+
properties: {
596+
name: { type: 'string' },
597+
email: { type: 'string' },
598+
apiSettings: { $ref: '#/components/schemas/ApiSettings' },
599+
User_Status: {
600+
type: 'string',
601+
'x-xgen-IPA-exception': {
602+
'xgen-IPA-112-field-names-are-camel-case': 'Reason',
603+
},
604+
},
605+
},
606+
},
607+
},
608+
},
609+
},
610+
errors: [],
611+
},
464612
]);

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ rules:
2121
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-112-avoid-project-field-names'
2222
severity: warn
2323
given:
24-
- '$.components.schemas..properties[*]~'
25-
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties[*]~'
26-
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties[*]~'
24+
- '$.components.schemas..properties'
25+
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties'
26+
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties'
2727
then:
28+
field: '@key'
2829
function: 'IPA112AvoidProjectFieldNames'
2930
functionOptions:
3031
prohibitedFieldNames:
@@ -46,10 +47,11 @@ rules:
4647
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-112-field-names-are-camel-case'
4748
severity: warn
4849
given:
49-
- '$.components.schemas..properties[*]~'
50-
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties[*]~'
51-
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties[*]~'
50+
- '$.components.schemas..properties'
51+
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties'
52+
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties'
5253
then:
54+
field: '@key'
5355
function: 'IPA112FieldNamesAreCamelCase'
5456
xgen-IPA-112-boolean-field-names-avoid-is-prefix:
5557
description: |
@@ -63,8 +65,9 @@ rules:
6365
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-112-boolean-field-names-avoid-is-prefix'
6466
severity: warn
6567
given:
66-
- '$.components.schemas..properties[*]~'
67-
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties[*]~'
68-
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties[*]~'
68+
- '$.components.schemas..properties'
69+
- '$.paths..requestBody.content[?(@property.match(/json$/i))].schema..properties'
70+
- '$.paths..responses..content[?(@property.match(/json$/i))].schema..properties'
6971
then:
72+
field: '@key'
7073
function: 'IPA112BooleanFieldNamesAvoidIsPrefix'

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@ import { splitCamelCase } from './utils/schemaUtils.js';
1111
const RULE_NAME = 'xgen-IPA-112-avoid-project-field-names';
1212

1313
export default (input, options, { path, documentInventory }) => {
14-
const oas = documentInventory.resolved;
14+
const oas = documentInventory.unresolved;
1515
const property = resolveObject(oas, path);
1616

17+
// Skip schema references ($ref):
18+
// Referenced schemas are validated separately to prevent duplicate violations
19+
if (!property) {
20+
return;
21+
}
22+
1723
const ignoreList = options?.ignore || [];
1824
if (ignoreList.some((ignoreTerm) => input.toLowerCase().includes(ignoreTerm))) {
1925
return;

0 commit comments

Comments
 (0)