diff --git a/docs/reference/functions.md b/docs/reference/functions.md index d9d655aba..742c6efab 100644 --- a/docs/reference/functions.md +++ b/docs/reference/functions.md @@ -247,7 +247,7 @@ unused-definition: ## or -Communicate that one or more of these properties is required to be defined. FunctionOptions must contain any non-zero number of properties, **or** will require that _at least_ one of them is defined. (For only one property specified, this is the same as the `defined` rule for that property.) +Communicate that one or more of these properties is required to be defined. `functionOptions` must contain at least two properties. **or** will require that _at least_ one of them is defined. @@ -272,7 +272,7 @@ schemas-descriptive-text-exists: ## xor -Communicate that one of these properties is required, and no more than one is allowed to be defined. FunctionOptions must contain any non-zero number of properties, **xor** will require that _exactly_ one of them is defined. (For only one property specified, this is the same as the `defined` rule for that property.) +Communicate that one of these properties is required, and no more than one is allowed to be defined. `functionOptions` must contain at least two properties. **xor** will require that _exactly_ one of them is defined. diff --git a/packages/functions/src/__tests__/or.test.ts b/packages/functions/src/__tests__/or.test.ts index bf576f7ba..bb8541110 100644 --- a/packages/functions/src/__tests__/or.test.ts +++ b/packages/functions/src/__tests__/or.test.ts @@ -56,7 +56,7 @@ describe('Core Functions / Or', () => { ).toEqual([]); }); - it('given none of 1 property, should return an error message', async () => { + it('given one property, should show no error message', async () => { expect( await runOr( { @@ -64,17 +64,11 @@ describe('Core Functions / Or', () => { title: 'Swagger Petstore', termsOfService: 'http://swagger.io/terms/', }, - { properties: ['yada-yada'] }, - ), - ).toEqual([ - { - message: 'At least one of "yada-yada" must be defined', - path: [], - }, - ]); + { properties: ['yada-yada'] }) + ).toEqual([]); }); - it('given only one of 1 property, should return no error message', async () => { + it('given no properties, should show no error message', async () => { expect( await runOr( { @@ -82,7 +76,7 @@ describe('Core Functions / Or', () => { title: 'Swagger Petstore', termsOfService: 'http://swagger.io/terms/', }, - { properties: ['title'] }, + null, ), ).toEqual([]); }); @@ -189,7 +183,7 @@ describe('Core Functions / Or', () => { [ new RulesetValidationError( 'invalid-function-options', - '"or" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.', + '"or" function has invalid options specified. Example valid options: { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.', ['rules', 'my-rule', 'then', 'functionOptions'], ), ], @@ -199,7 +193,7 @@ describe('Core Functions / Or', () => { [ new RulesetValidationError( 'invalid-function-options', - '"or" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.', + '"or" function has invalid options specified. Example valid options: { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.', ['rules', 'my-rule', 'then', 'functionOptions'], ), ], @@ -221,7 +215,7 @@ describe('Core Functions / Or', () => { [ new RulesetValidationError( 'invalid-function-options', - '"or" requires one or more enumerated "properties", i.e. ["id"], ["default", "example"], ["title", "summary", "description"], etc.', + '"or" requires one or more enumerated "properties", i.e. ["default", "example"], ["title", "summary", "description"], etc.', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -231,7 +225,7 @@ describe('Core Functions / Or', () => { [ new RulesetValidationError( 'invalid-function-options', - '"or" requires one or more enumerated "properties", i.e. ["id"], ["default", "example"], ["title", "summary", "description"], etc.', + '"or" requires one or more enumerated "properties", i.e. ["default", "example"], ["title", "summary", "description"], etc.', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], diff --git a/packages/functions/src/__tests__/xor.test.ts b/packages/functions/src/__tests__/xor.test.ts index c9bc5b6b6..02d702b28 100644 --- a/packages/functions/src/__tests__/xor.test.ts +++ b/packages/functions/src/__tests__/xor.test.ts @@ -79,7 +79,7 @@ describe('Core Functions / Xor', () => { ).toEqual([]); }); - it('given none of 1 property, should return an error message', async () => { + it('given 1 property, should return no error message', async () => { expect( await runXor( { @@ -89,15 +89,10 @@ describe('Core Functions / Xor', () => { }, { properties: ['yada-yada'] }, ), - ).toEqual([ - { - message: 'At least one of "yada-yada" must be defined', - path: [], - }, - ]); + ).toEqual([]); }); - it('given only one of 1 property, should return no error message', async () => { + it('given no properties, should return no error message', async () => { expect( await runXor( { @@ -105,7 +100,7 @@ describe('Core Functions / Xor', () => { title: 'Swagger Petstore', termsOfService: 'http://swagger.io/terms/', }, - { properties: ['title'] }, + null, ), ).toEqual([]); }); @@ -178,7 +173,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.', + '"xor" function has invalid options specified. Example valid options: { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.', ['rules', 'my-rule', 'then', 'functionOptions'], ), ], @@ -188,7 +183,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.', + '"xor" function has invalid options specified. Example valid options: { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.', ['rules', 'my-rule', 'then', 'functionOptions'], ), ], @@ -210,7 +205,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" requires one or more enumerated "properties", i.e. ["id"], ["country", "street"], ["one", "two", "three"], etc.', + '"xor" requires at least two enumerated "properties", i.e. ["country", "street"], ["one", "two", "three"], etc.', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], @@ -220,7 +215,7 @@ describe('Core Functions / Xor', () => { [ new RulesetValidationError( 'invalid-function-options', - '"xor" requires one or more enumerated "properties", i.e. ["id"], ["country", "street"], ["one", "two", "three"], etc.', + '"xor" requires at least two enumerated "properties", i.e. ["country", "street"], ["one", "two", "three"], etc.', ['rules', 'my-rule', 'then', 'functionOptions', 'properties'], ), ], diff --git a/packages/functions/src/optionSchemas.ts b/packages/functions/src/optionSchemas.ts index fe619fd01..9377b8eb6 100644 --- a/packages/functions/src/optionSchemas.ts +++ b/packages/functions/src/optionSchemas.ts @@ -104,16 +104,15 @@ export const optionSchemas: Record = { items: { type: 'string', }, - minItems: 1, // OR is valid with one item (then it is redundant with 'defined' function) - // maxItems: 2, // No maximum limit is necessary, OR is valid for any amount, just one must be defined - errorMessage: `"or" requires one or more enumerated "properties", i.e. ["id"], ["default", "example"], ["title", "summary", "description"], etc.`, + minItems: 2, + errorMessage: `"or" requires at least two enumerated "properties", i.e. ["default", "example"], ["title", "summary", "description"], etc.`, description: 'The properties to check.', }, }, additionalProperties: false, required: ['properties'], errorMessage: { - type: `"or" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.`, + type: `"or" function has invalid options specified. Example valid options: { "properties": ["default", "example"] }, { "properties": ["title", "summary", "description"] }, etc.`, }, }, pattern: { @@ -226,16 +225,15 @@ export const optionSchemas: Record = { items: { type: 'string', }, - minItems: 1, // XOR is valid with one item (then it is redundant with 'defined' function) - // maxItems: 2, // No maximum limit is necessary, XOR is valid for any amount, just one must be defined - errorMessage: `"xor" requires one or more enumerated "properties", i.e. ["id"], ["country", "street"], ["one", "two", "three"], etc.`, + minItems: 2, + errorMessage: `"xor" requires at least two enumerated "properties", i.e. ["country", "street"], ["one", "two", "three"], etc.`, description: 'The properties to check.', }, }, additionalProperties: false, required: ['properties'], errorMessage: { - type: `"xor" function has invalid options specified. Example valid options: { "properties": ["id"] }, { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.`, + type: `"xor" function has invalid options specified. Example valid options: { "properties": ["country", "street"] }, { "properties": ["one", "two", "three"] }, etc.`, }, }, }; diff --git a/packages/functions/src/or.ts b/packages/functions/src/or.ts index afed84f66..0d0bd621d 100644 --- a/packages/functions/src/or.ts +++ b/packages/functions/src/or.ts @@ -2,7 +2,7 @@ import { createRulesetFunction, IFunctionResult } from '@stoplight/spectral-core import { optionSchemas } from './optionSchemas'; export type Options = { - /** test to verify if one (but not all) of the provided keys are present in object */ + /** test to verify at least one of the provided keys are present in object */ properties: string[]; }; @@ -14,8 +14,8 @@ export default createRulesetFunction, Options>( options: optionSchemas.or, }, function or(targetVal, { properties }) { - if (properties.length == 0) return; - // There need be no maximum limit on number of properties + if (properties.length < 2) return; + // At least two but no maximum limit on number of properties const results: IFunctionResult[] = []; @@ -29,7 +29,7 @@ export default createRulesetFunction, Options>( message: 'At least one of "' + shortprops.join('" or "') + '" or ' + count, }); } else { - // List all of one to four properties directly in error message + // List all of two to four properties directly in error message results.push({ message: 'At least one of "' + properties.join('" or "') + '" must be defined', }); diff --git a/packages/functions/src/xor.ts b/packages/functions/src/xor.ts index 10683819a..71aca9e63 100644 --- a/packages/functions/src/xor.ts +++ b/packages/functions/src/xor.ts @@ -2,7 +2,7 @@ import { createRulesetFunction, IFunctionResult } from '@stoplight/spectral-core import { optionSchemas } from './optionSchemas'; export type Options = { - /** test to verify if one (but not all) of the provided keys are present in object */ + /** test to verify if exactly one of the provided keys are present in object */ properties: string[]; }; @@ -14,8 +14,8 @@ export default createRulesetFunction, Options>( options: optionSchemas.xor, }, function xor(targetVal, { properties }) { - if (properties.length == 0) return; - // There need be no maximum limit on number of properties + if (properties.length < 2) return; + // At least two but no maximum limit on number of properties const results: IFunctionResult[] = []; @@ -29,7 +29,7 @@ export default createRulesetFunction, Options>( message: 'At least one of "' + shortprops.join('" or "') + '" or ' + count, }); } else { - // List all of one to four properties directly in error message + // List all of two to four properties directly in error message results.push({ message: 'At least one of "' + properties.join('" or "') + '" must be defined', });