Skip to content

Commit

Permalink
Require minimum 2 properties per PR comments
Browse files Browse the repository at this point in the history
  • Loading branch information
cuttingclyde committed Mar 7, 2025
1 parent 71013fc commit c45139e
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 46 deletions.
4 changes: 2 additions & 2 deletions docs/reference/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- title: functionOptions -->

Expand All @@ -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.

<!-- title: functionOptions -->

Expand Down
24 changes: 9 additions & 15 deletions packages/functions/src/__tests__/or.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,33 +56,27 @@ 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(
{
version: '1.0.0',
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(
{
version: '1.0.0',
title: 'Swagger Petstore',
termsOfService: 'http://swagger.io/terms/',
},
{ properties: ['title'] },
null,
),
).toEqual([]);
});
Expand Down Expand Up @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand Down
21 changes: 8 additions & 13 deletions packages/functions/src/__tests__/xor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -89,23 +89,18 @@ 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(
{
version: '1.0.0',
title: 'Swagger Petstore',
termsOfService: 'http://swagger.io/terms/',
},
{ properties: ['title'] },
null,
),
).toEqual([]);
});
Expand Down Expand Up @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand All @@ -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'],
),
],
Expand Down
14 changes: 6 additions & 8 deletions packages/functions/src/optionSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,15 @@ export const optionSchemas: Record<string, CustomFunctionOptionsSchema> = {
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: {
Expand Down Expand Up @@ -226,16 +225,15 @@ export const optionSchemas: Record<string, CustomFunctionOptionsSchema> = {
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.`,
},
},
};
8 changes: 4 additions & 4 deletions packages/functions/src/or.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
};

Expand All @@ -14,8 +14,8 @@ export default createRulesetFunction<Record<string, unknown>, 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[] = [];

Expand All @@ -29,7 +29,7 @@ export default createRulesetFunction<Record<string, unknown>, 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',
});
Expand Down
8 changes: 4 additions & 4 deletions packages/functions/src/xor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
};

Expand All @@ -14,8 +14,8 @@ export default createRulesetFunction<Record<string, unknown>, 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[] = [];

Expand All @@ -29,7 +29,7 @@ export default createRulesetFunction<Record<string, unknown>, 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',
});
Expand Down

0 comments on commit c45139e

Please sign in to comment.