mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-09 20:57:44 +00:00
feat: new assert notPattern (#942)
This commit is contained in:
@@ -4,9 +4,9 @@ apis:
|
|||||||
|
|
||||||
rules:
|
rules:
|
||||||
assert/summary-pattern:
|
assert/summary-pattern:
|
||||||
subject:
|
subject:
|
||||||
type: Operation
|
type: Operation
|
||||||
property: summary
|
property: summary
|
||||||
message: Operation summary should have 'test' word
|
assertions:
|
||||||
assertions:
|
|
||||||
pattern: '/test/'
|
pattern: '/test/'
|
||||||
|
notPattern: /summary/
|
||||||
|
|||||||
@@ -5,7 +5,21 @@ exports[`E2E lint assertions-pattern-error 1`] = `
|
|||||||
validating /openapi.yaml...
|
validating /openapi.yaml...
|
||||||
[1] openapi.yaml:20:16 at #/paths/~1pet~1findByStatus/get/summary
|
[1] openapi.yaml:20:16 at #/paths/~1pet~1findByStatus/get/summary
|
||||||
|
|
||||||
Operation summary should have 'test' word
|
summary-pattern assertion failed because the Operation summary didn't meet the assertions: "summary example" should match a regex /test/
|
||||||
|
|
||||||
|
18 | get:
|
||||||
|
19 | operationId: example
|
||||||
|
20 | summary: summary example
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
21 | tags:
|
||||||
|
22 | - foo
|
||||||
|
|
||||||
|
Error was generated by the summary-pattern assertion rule.
|
||||||
|
|
||||||
|
|
||||||
|
[2] openapi.yaml:20:16 at #/paths/~1pet~1findByStatus/get/summary
|
||||||
|
|
||||||
|
summary-pattern assertion failed because the Operation summary didn't meet the assertions: "summary example" should not match a regex /summary/
|
||||||
|
|
||||||
18 | get:
|
18 | get:
|
||||||
19 | operationId: example
|
19 | operationId: example
|
||||||
@@ -19,7 +33,7 @@ Error was generated by the summary-pattern assertion rule.
|
|||||||
|
|
||||||
/openapi.yaml: validated in <test>ms
|
/openapi.yaml: validated in <test>ms
|
||||||
|
|
||||||
❌ Validation failed with 1 error.
|
❌ Validation failed with 2 errors.
|
||||||
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
run \`openapi lint --generate-ignore-file\` to add all problems to the ignore file.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ maxLength | integer | Asserts a maximum length (exclusive) of a string or list (
|
|||||||
minLength | integer | Asserts a minimum length (inclusive) of a string or list (array). See [minLength example](#minlength-example).
|
minLength | integer | Asserts a minimum length (inclusive) of a string or list (array). See [minLength example](#minlength-example).
|
||||||
nonEmpty | boolean | Asserts a property is not empty. See [nonEmpty example](#nonempty-example).
|
nonEmpty | boolean | Asserts a property is not empty. See [nonEmpty example](#nonempty-example).
|
||||||
pattern | string | Asserts a value matches a regex pattern. See [regex pattern example](#pattern-example).
|
pattern | string | Asserts a value matches a regex pattern. See [regex pattern example](#pattern-example).
|
||||||
|
notPattern | string | Asserts a value doesn't match a regex pattern. See [regex notPattern example](#notpattern-example).
|
||||||
mutuallyExclusive | [string] | Asserts that listed properties (key names only) are mutually exclusive. See [mutuallyExclusive example](#mutuallyexclusive-example).
|
mutuallyExclusive | [string] | Asserts that listed properties (key names only) are mutually exclusive. See [mutuallyExclusive example](#mutuallyexclusive-example).
|
||||||
mutuallyRequired | [string] | Asserts that listed properties (key names only) are mutually required. See [mutuallyRequired example](#mutuallyrequired-example).
|
mutuallyRequired | [string] | Asserts that listed properties (key names only) are mutually required. See [mutuallyRequired example](#mutuallyrequired-example).
|
||||||
ref | boolean \| string | Asserts a reference object presence in object's property. A boolean value of `true` means the property has a `$ref` defined. A boolean value of `false` means the property has not defined a `$ref` (it has an in-place value). A string value means that the `$ref` is defined and the unresolved value must match the pattern (for example, `'/paths\/. *\.yaml$/'`). See [ref example](#ref-example).|
|
ref | boolean \| string | Asserts a reference object presence in object's property. A boolean value of `true` means the property has a `$ref` defined. A boolean value of `false` means the property has not defined a `$ref` (it has an in-place value). A string value means that the `$ref` is defined and the unresolved value must match the pattern (for example, `'/paths\/. *\.yaml$/'`). See [ref example](#ref-example).|
|
||||||
@@ -381,6 +382,20 @@ rules:
|
|||||||
pattern: /test/
|
pattern: /test/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `notPattern` example
|
||||||
|
|
||||||
|
The following example asserts that the operation summary doesn't start with "The".
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rules:
|
||||||
|
assert/operation-summary-contains-test:
|
||||||
|
subject:
|
||||||
|
type: Operation
|
||||||
|
property: The summary
|
||||||
|
assertions:
|
||||||
|
notPattern: /^The/
|
||||||
|
```
|
||||||
|
|
||||||
### `casing` example
|
### `casing` example
|
||||||
|
|
||||||
The following example asserts the casing style is `PascalCase` for `NamedExamples` map keys.
|
The following example asserts the casing style is `PascalCase` for `NamedExamples` map keys.
|
||||||
|
|||||||
@@ -43,6 +43,24 @@ describe('oas3 assertions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('notPattern', () => {
|
||||||
|
it('value should not match regex pattern', () => {
|
||||||
|
expect(asserts.notPattern('test string', '/test me/', baseLocation)).toEqual([]);
|
||||||
|
expect(asserts.notPattern('test string', '/test/', baseLocation)).toEqual([
|
||||||
|
{ location: baseLocation, message: '"test string" should not match a regex /test/' },
|
||||||
|
]);
|
||||||
|
expect(
|
||||||
|
asserts.notPattern(['test string', 'test me'], '/test other/', baseLocation)
|
||||||
|
).toEqual([]);
|
||||||
|
expect(asserts.notPattern(['test string', 'test me'], '/test me/', baseLocation)).toEqual([
|
||||||
|
{
|
||||||
|
message: '"test me" should not match a regex /test me/',
|
||||||
|
location: baseLocation.key(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('ref', () => {
|
describe('ref', () => {
|
||||||
it('value should have ref', () => {
|
it('value should have ref', () => {
|
||||||
expect(asserts.ref({ $ref: 'text' }, true, baseLocation, { $ref: 'text' })).toEqual([]);
|
expect(asserts.ref({ $ref: 'text' }, true, baseLocation, { $ref: 'text' })).toEqual([]);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export type AssertionFn = (
|
|||||||
|
|
||||||
export type Asserts = {
|
export type Asserts = {
|
||||||
pattern: AssertionFn;
|
pattern: AssertionFn;
|
||||||
|
notPattern: AssertionFn;
|
||||||
enum: AssertionFn;
|
enum: AssertionFn;
|
||||||
defined: AssertionFn;
|
defined: AssertionFn;
|
||||||
required: AssertionFn;
|
required: AssertionFn;
|
||||||
@@ -40,6 +41,7 @@ export const runOnKeysSet = new Set<keyof Asserts>([
|
|||||||
'mutuallyRequired',
|
'mutuallyRequired',
|
||||||
'enum',
|
'enum',
|
||||||
'pattern',
|
'pattern',
|
||||||
|
'notPattern',
|
||||||
'minLength',
|
'minLength',
|
||||||
'maxLength',
|
'maxLength',
|
||||||
'casing',
|
'casing',
|
||||||
@@ -53,6 +55,7 @@ export const runOnKeysSet = new Set<keyof Asserts>([
|
|||||||
]);
|
]);
|
||||||
export const runOnValuesSet = new Set<keyof Asserts>([
|
export const runOnValuesSet = new Set<keyof Asserts>([
|
||||||
'pattern',
|
'pattern',
|
||||||
|
'notPattern',
|
||||||
'enum',
|
'enum',
|
||||||
'defined',
|
'defined',
|
||||||
'undefined',
|
'undefined',
|
||||||
@@ -69,18 +72,33 @@ export const asserts: Asserts = {
|
|||||||
pattern: (value: string | string[], condition: string, baseLocation: Location) => {
|
pattern: (value: string | string[], condition: string, baseLocation: Location) => {
|
||||||
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
||||||
const values = runOnValue(value) ? [value] : value;
|
const values = runOnValue(value) ? [value] : value;
|
||||||
const regx = regexFromString(condition);
|
const regex = regexFromString(condition);
|
||||||
|
|
||||||
return values
|
return values
|
||||||
.map(
|
.map(
|
||||||
(_val) =>
|
(_val) =>
|
||||||
!regx?.test(_val) && {
|
!regex?.test(_val) && {
|
||||||
message: `"${_val}" should match a regex ${condition}`,
|
message: `"${_val}" should match a regex ${condition}`,
|
||||||
location: runOnValue(value) ? baseLocation : baseLocation.key(),
|
location: runOnValue(value) ? baseLocation : baseLocation.key(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.filter(isTruthy);
|
.filter(isTruthy);
|
||||||
},
|
},
|
||||||
|
notPattern: (value: string | string[], condition: string, baseLocation: Location) => {
|
||||||
|
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
||||||
|
const values = runOnValue(value) ? [value] : value;
|
||||||
|
const regex = regexFromString(condition);
|
||||||
|
|
||||||
|
return values
|
||||||
|
.map(
|
||||||
|
(_val) =>
|
||||||
|
regex?.test(_val) && {
|
||||||
|
message: `"${_val}" should not match a regex ${condition}`,
|
||||||
|
location: runOnValue(value) ? baseLocation : baseLocation.key(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.filter(isTruthy);
|
||||||
|
},
|
||||||
enum: (value: string | string[], condition: string[], baseLocation: Location) => {
|
enum: (value: string | string[], condition: string[], baseLocation: Location) => {
|
||||||
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
||||||
const values = runOnValue(value) ? [value] : value;
|
const values = runOnValue(value) ? [value] : value;
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ const AssertionDefinitionAssertions: NodeType = {
|
|||||||
properties: {
|
properties: {
|
||||||
enum: { type: 'array', items: { type: 'string' } },
|
enum: { type: 'array', items: { type: 'string' } },
|
||||||
pattern: { type: 'string' },
|
pattern: { type: 'string' },
|
||||||
|
notPattern: { type: 'string' },
|
||||||
casing: {
|
casing: {
|
||||||
enum: [
|
enum: [
|
||||||
'camelCase',
|
'camelCase',
|
||||||
|
|||||||
Reference in New Issue
Block a user