diff --git a/docs/reference/openapi-rules.md b/docs/reference/openapi-rules.md index d55e43772..0ea70cded 100644 --- a/docs/reference/openapi-rules.md +++ b/docs/reference/openapi-rules.md @@ -115,7 +115,7 @@ It can be hard to pick a license, so if you don't have a lawyer around you can u How useful this is in court is not entirely known, but having a license is better than not having a license. -**Recommended:** Yes +**Recommended:** No **Good Example** @@ -130,7 +130,7 @@ info: Mentioning a license is only useful if people know what the license means, so add a link to the full text for those who need it. -**Recommended:** Yes +**Recommended:** No **Good Example** @@ -244,8 +244,6 @@ tags: - name: "Badger" ``` -**Recommended:** No - ### operation-description **Recommended:** Yes diff --git a/packages/functions/src/__tests__/xor.test.ts b/packages/functions/src/__tests__/xor.test.ts index 6ea29e233..49d328f32 100644 --- a/packages/functions/src/__tests__/xor.test.ts +++ b/packages/functions/src/__tests__/xor.test.ts @@ -26,6 +26,24 @@ describe('Core Functions / Xor', () => { ]); }); + it('given multiple properties that do not match, should return an error message', async () => { + expect( + await runXor( + { + version: '1.0.0', + title: 'Swagger Petstore', + termsOfService: 'http://swagger.io/terms/', + }, + { properties: ['yada-yada', 'whatever', 'foo'] }, + ), + ).toEqual([ + { + message: '"yada-yada", "whatever" and "foo" must not be both defined or both undefined', + path: [], + }, + ]); + }); + it('given both properties, should return an error message', async () => { expect( await runXor( @@ -99,22 +117,12 @@ describe('Core Functions / Xor', () => { ]), ], ], - [ - { properties: ['foo', 'bar', 'baz'] }, - [ - new RulesetValidationError( - 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', - ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], - ), - ], - ], [ { properties: ['foo', {}] }, [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -124,7 +132,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -134,7 +142,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]', + '"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], diff --git a/packages/functions/src/optionSchemas.ts b/packages/functions/src/optionSchemas.ts index f9a93c15e..7a3a2741d 100644 --- a/packages/functions/src/optionSchemas.ts +++ b/packages/functions/src/optionSchemas.ts @@ -207,8 +207,7 @@ export const optionSchemas: Record = { type: 'string', }, minItems: 2, - maxItems: 2, - errorMessage: `"xor" and its "properties" option support 2-item tuples, i.e. ["id", "name"]`, + errorMessage: `"xor" and its "properties" option require at least 2-item tuples, i.e. ["id", "name"]`, description: 'The properties to check.', }, }, diff --git a/packages/functions/src/xor.ts b/packages/functions/src/xor.ts index e9a122251..5d5d68b78 100644 --- a/packages/functions/src/xor.ts +++ b/packages/functions/src/xor.ts @@ -16,16 +16,20 @@ export default createRulesetFunction, Options>( options: optionSchemas.xor, }, function xor(targetVal, { properties }) { - if (properties.length !== 2) return; - const results: IFunctionResult[] = []; - const intersection = Object.keys(targetVal).filter(value => -1 !== properties.indexOf(value)); + const intersection = Object.keys(targetVal).filter(key => properties.includes(key)); + if (intersection.length !== 1) { + const formattedProperties = properties.map(prop => printValue(prop)); + + const lastProperty = formattedProperties.pop(); + let message = formattedProperties.join(', ') + (lastProperty != undefined ? ` and ${lastProperty}` : ''); + + message += ' must not be both defined or both undefined'; + results.push({ - message: `${printValue(properties[0])} and ${printValue( - properties[1], - )} must not be both defined or both undefined`, + message, }); } diff --git a/yarn.lock b/yarn.lock index 60467760e..941db348b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5249,6 +5249,16 @@ __metadata: languageName: node linkType: hard +"d@npm:^1.0.2": + version: 1.0.2 + resolution: "d@npm:1.0.2" + dependencies: + es5-ext: ^0.10.64 + type: ^2.7.2 + checksum: 775db1e8ced6707cddf64a5840522fcf5475d38ef49a5d615be0ac47f86ef64d15f5a73de1522b09327cc466d4dc35ea83dbfeed456f7a0fdcab138deb800355 + languageName: node + linkType: hard + "dargs@npm:^7.0.0": version: 7.0.0 resolution: "dargs@npm:7.0.0" @@ -5850,18 +5860,19 @@ __metadata: languageName: node linkType: hard -"es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.46, es5-ext@npm:^0.10.50, es5-ext@npm:^0.10.51, es5-ext@npm:^0.10.53, es5-ext@npm:~0.10.14, es5-ext@npm:~0.10.2, es5-ext@npm:~0.10.46": - version: 0.10.53 - resolution: "es5-ext@npm:0.10.53" +"es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.46, es5-ext@npm:^0.10.50, es5-ext@npm:^0.10.51, es5-ext@npm:^0.10.53, es5-ext@npm:^0.10.62, es5-ext@npm:^0.10.64, es5-ext@npm:~0.10.14, es5-ext@npm:~0.10.2, es5-ext@npm:~0.10.46": + version: 0.10.64 + resolution: "es5-ext@npm:0.10.64" dependencies: - es6-iterator: ~2.0.3 - es6-symbol: ~3.1.3 - next-tick: ~1.0.0 - checksum: 24ec22369260cf98605cb2f51eae9d7df5dc621bc5d3b311f6f5c3d0fcdb7bafae888270f3083ee6e9af27350a5ea49f1fe2dd6406a9017247ca40f091f529b2 + es6-iterator: ^2.0.3 + es6-symbol: ^3.1.3 + esniff: ^2.0.1 + next-tick: ^1.1.0 + checksum: 01179fab0769fdbef213062222f99d0346724dbaccf04b87c0e6ee7f0c97edabf14be647ca1321f0497425ea7145de0fd278d1b3f3478864b8933e7136a5c645 languageName: node linkType: hard -"es6-iterator@npm:^2.0.3, es6-iterator@npm:~2.0.3": +"es6-iterator@npm:^2.0.3": version: 2.0.3 resolution: "es6-iterator@npm:2.0.3" dependencies: @@ -5879,7 +5890,7 @@ __metadata: languageName: node linkType: hard -"es6-symbol@npm:^3.1.1, es6-symbol@npm:~3.1.3": +"es6-symbol@npm:^3.1.1": version: 3.1.3 resolution: "es6-symbol@npm:3.1.3" dependencies: @@ -5889,6 +5900,16 @@ __metadata: languageName: node linkType: hard +"es6-symbol@npm:^3.1.3": + version: 3.1.4 + resolution: "es6-symbol@npm:3.1.4" + dependencies: + d: ^1.0.2 + ext: ^1.7.0 + checksum: 52125ec4b5d1b6b93b8d3d42830bb19f8da21080ffcf45253b614bc6ff3e31349be202fb745d4d1af6778cdf5e38fea30e0c7e7dc37e2aecd44acc43502055f9 + languageName: node + linkType: hard + "es6-weak-map@npm:^2.0.3": version: 2.0.3 resolution: "es6-weak-map@npm:2.0.3" @@ -6099,6 +6120,18 @@ __metadata: languageName: node linkType: hard +"esniff@npm:^2.0.1": + version: 2.0.1 + resolution: "esniff@npm:2.0.1" + dependencies: + d: ^1.0.1 + es5-ext: ^0.10.62 + event-emitter: ^0.3.5 + type: ^2.7.2 + checksum: d814c0e5c39bce9925b2e65b6d8767af72c9b54f35a65f9f3d6e8c606dce9aebe35a9599d30f15b0807743f88689f445163cfb577a425de4fb8c3c5bc16710cc + languageName: node + linkType: hard + "espree@npm:^9.3.2, espree@npm:^9.3.3": version: 9.3.3 resolution: "espree@npm:9.3.3" @@ -6297,6 +6330,15 @@ __metadata: languageName: node linkType: hard +"ext@npm:^1.7.0": + version: 1.7.0 + resolution: "ext@npm:1.7.0" + dependencies: + type: ^2.7.2 + checksum: ef481f9ef45434d8c867cfd09d0393b60945b7c8a1798bedc4514cb35aac342ccb8d8ecb66a513e6a2b4ec1e294a338e3124c49b29736f8e7c735721af352c31 + languageName: node + linkType: hard + "extend@npm:^3.0.0": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -9744,13 +9786,6 @@ __metadata: languageName: node linkType: hard -"next-tick@npm:~1.0.0": - version: 1.0.0 - resolution: "next-tick@npm:1.0.0" - checksum: 83fcb3d4f8d9380210b1c2b8a610463602d80283f0c0c8571c1688e1ad6cbf3a16b345f5bb7212617d4898bedcfa10dff327dc09ec20a112a5bf43a0271375fb - languageName: node - linkType: hard - "nice-try@npm:^1.0.4": version: 1.0.5 resolution: "nice-try@npm:1.0.5" @@ -12419,8 +12454,8 @@ __metadata: linkType: hard "tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.1.15 - resolution: "tar@npm:6.1.15" + version: 6.2.1 + resolution: "tar@npm:6.2.1" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -12428,7 +12463,7 @@ __metadata: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: f23832fceeba7578bf31907aac744ae21e74a66f4a17a9e94507acf460e48f6db598c7023882db33bab75b80e027c21f276d405e4a0322d58f51c7088d428268 + checksum: f1322768c9741a25356c11373bce918483f40fa9a25c69c59410c8a1247632487edef5fe76c5f12ac51a6356d2f1829e96d2bc34098668a2fc34d76050ac2b6c languageName: node linkType: hard @@ -12883,6 +12918,13 @@ __metadata: languageName: node linkType: hard +"type@npm:^2.7.2": + version: 2.7.2 + resolution: "type@npm:2.7.2" + checksum: 0f42379a8adb67fe529add238a3e3d16699d95b42d01adfe7b9a7c5da297f5c1ba93de39265ba30ffeb37dfd0afb3fb66ae09f58d6515da442219c086219f6f4 + languageName: node + linkType: hard + "typescript@npm:^4.4.3, typescript@npm:^4.4.4": version: 4.4.4 resolution: "typescript@npm:4.4.4"