Skip to content

Commit 85a797e

Browse files
authored
feat: adds check for case-insensitive property name collisions (#162)
1 parent 9329353 commit 85a797e

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ The supported rules are described below:
265265
| array_of_arrays | Flag any schema with a 'property' of type `array` with items of type `array`. | shared |
266266
| inconsistent_property_type | Flag any properties that have the same name but an inconsistent type. | shared |
267267
| property_case_convention | Flag any property with a `name` that does not follow a given case convention. snake_case_only must be 'off' to use. | shared |
268+
| property_case_collision | Flag any property with a `name` that is identical to another property's `name` except for the naming convention | shared |
268269
| enum_case_convention | Flag any enum with a `value` that does not follow a given case convention. snake_case_only must be 'off' to use. | shared |
269270
| json_or_param_binary_string | Flag parameters or application/json request/response bodies with schema type: string, format: binary. | oas3 |
270271

@@ -445,6 +446,7 @@ The default values for each rule are described below.
445446
| array_of_arrays | warning |
446447
| inconsistent_property_type | warning |
447448
| property_case_convention | error, lower_snake_case |
449+
| property_case_collision | error |
448450
| enum_case_convention | error, lower_snake_case |
449451

450452
###### walker

src/.defaultsForValidator.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const defaults = {
6666
'array_of_arrays': 'warning',
6767
'inconsistent_property_type': 'warning',
6868
'property_case_convention': [ 'error', 'lower_snake_case'],
69+
'property_case_collision': 'error',
6970
'enum_case_convention': [ 'error', 'lower_snake_case']
7071
},
7172
'walker': {

src/plugins/validation/2and3/semantic-validators/schema-ibm.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ module.exports.validate = function({ jsSpec, isOAS3 }, config) {
115115

116116
checkEnumValues(schema, path, config, messages);
117117
} else {
118-
// optional support for property_case_convention and enum_case_convention
118+
// optional support for property_case_convention, property_case_collision, and enum_case_convention
119119
// in config. In the else block because support should be mutually exclusive
120120
// with config.snake_case_only since it is overlapping functionality
121121
if (config.property_case_convention) {
@@ -129,6 +129,17 @@ module.exports.validate = function({ jsSpec, isOAS3 }, config) {
129129
);
130130
}
131131
}
132+
if (config.property_case_collision) {
133+
const checkCaseStatus = config.property_case_collision;
134+
if (checkCaseStatus !== 'off') {
135+
checkPropNamesCaseCollision(
136+
schema,
137+
path,
138+
config.property_case_collision,
139+
messages
140+
);
141+
}
142+
}
132143
if (config.enum_case_convention) {
133144
const checkCaseStatus = config.enum_case_convention[0];
134145
if (checkCaseStatus !== 'off') {
@@ -415,11 +426,50 @@ function checkPropNames(schema, contextPath, config, messages) {
415426
});
416427
}
417428

429+
/**
430+
* Check that property names follow the specified case convention
431+
* @param schema
432+
* @param contextPath
433+
* @param checkStatus a string, 'off' | 'warning' | 'error'
434+
* @param messages
435+
*/
436+
function checkPropNamesCaseCollision(
437+
schema,
438+
contextPath,
439+
checkStatus,
440+
messages
441+
) {
442+
if (!schema.properties) {
443+
return;
444+
}
445+
446+
// flag any property whose name is identical to another property in a different case
447+
const prevProps = [];
448+
forIn(schema.properties, (property, propName) => {
449+
if (propName.slice(0, 2) === 'x-') return;
450+
451+
// Skip case_convention checks for deprecated properties
452+
if (propName.deprecated === true) return;
453+
454+
const caselessPropName = propName.replace(/[_-]/g, '').toLowerCase();
455+
if (prevProps.includes(caselessPropName)) {
456+
messages.addMessage(
457+
contextPath.concat(['properties', propName]),
458+
`Property name is identical to another property except for the naming convention: ${propName}`,
459+
checkStatus
460+
);
461+
} else {
462+
prevProps.push(caselessPropName);
463+
}
464+
});
465+
}
466+
418467
/**
419468
* Check that property names follow the specified case convention
420469
* @param schema
421470
* @param contextPath
422471
* @param caseConvention an array, [0]='off' | 'warning' | 'error'. [1]='lower_snake_case' etc.
472+
* @param messages
423473
*/
424474
function checkPropNamesCaseConvention(
425475
schema,
@@ -481,6 +531,7 @@ function checkEnumValues(schema, contextPath, config, messages) {
481531
* @param schema
482532
* @param contextPath
483533
* @param caseConvention an array, [0]='off' | 'warning' | 'error'. [1]='lower_snake_case' etc.
534+
* @param messages
484535
*/
485536
function checkEnumCaseConvention(
486537
schema,

test/plugins/validation/2and3/schema-ibm.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,48 @@ describe('validation plugin - semantic - schema-ibm - Swagger 2', () => {
537537
expect(res.warnings.length).toEqual(0);
538538
});
539539

540+
// tests for explicit name collisions
541+
it('should return a warning when two property names of different case conventions are identical if converted to a single case', () => {
542+
const customConfig = {
543+
schemas: {
544+
snake_case_only: 'off',
545+
property_case_convention: 'warning'
546+
}
547+
};
548+
549+
const spec = {
550+
definitions: {
551+
Thing: {
552+
type: 'object',
553+
description: 'thing',
554+
properties: {
555+
thingString: {
556+
type: 'string',
557+
description: 'thing string'
558+
},
559+
thing_string: {
560+
type: 'string',
561+
description: 'thing string'
562+
}
563+
}
564+
}
565+
}
566+
};
567+
568+
const res = validate({ jsSpec: spec }, customConfig);
569+
expect(res.errors.length).toEqual(0);
570+
expect(res.warnings.length).toEqual(1);
571+
expect(res.warnings[0].path).toEqual([
572+
'definitions',
573+
'Thing',
574+
'properties',
575+
'thingString'
576+
]);
577+
expect(res.warnings[0].message).toEqual(
578+
'Property name is identical to another property except for the naming convention: thingString'
579+
);
580+
});
581+
540582
it('should not return a case_convention error when property is deprecated', () => {
541583
const spec = {
542584
definitions: {

0 commit comments

Comments
 (0)