mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-10 04:21:20 +00:00
feat: new assert notPattern (#942)
This commit is contained in:
@@ -7,6 +7,6 @@ rules:
|
||||
subject:
|
||||
type: Operation
|
||||
property: summary
|
||||
message: Operation summary should have 'test' word
|
||||
assertions:
|
||||
pattern: '/test/'
|
||||
notPattern: /summary/
|
||||
|
||||
@@ -5,7 +5,21 @@ exports[`E2E lint assertions-pattern-error 1`] = `
|
||||
validating /openapi.yaml...
|
||||
[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:
|
||||
19 | operationId: example
|
||||
@@ -19,7 +33,7 @@ Error was generated by the summary-pattern assertion rule.
|
||||
|
||||
/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.
|
||||
|
||||
|
||||
|
||||
@@ -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).
|
||||
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).
|
||||
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).
|
||||
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).|
|
||||
@@ -381,6 +382,20 @@ rules:
|
||||
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
|
||||
|
||||
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', () => {
|
||||
it('value should have ref', () => {
|
||||
expect(asserts.ref({ $ref: 'text' }, true, baseLocation, { $ref: 'text' })).toEqual([]);
|
||||
|
||||
@@ -18,6 +18,7 @@ export type AssertionFn = (
|
||||
|
||||
export type Asserts = {
|
||||
pattern: AssertionFn;
|
||||
notPattern: AssertionFn;
|
||||
enum: AssertionFn;
|
||||
defined: AssertionFn;
|
||||
required: AssertionFn;
|
||||
@@ -40,6 +41,7 @@ export const runOnKeysSet = new Set<keyof Asserts>([
|
||||
'mutuallyRequired',
|
||||
'enum',
|
||||
'pattern',
|
||||
'notPattern',
|
||||
'minLength',
|
||||
'maxLength',
|
||||
'casing',
|
||||
@@ -53,6 +55,7 @@ export const runOnKeysSet = new Set<keyof Asserts>([
|
||||
]);
|
||||
export const runOnValuesSet = new Set<keyof Asserts>([
|
||||
'pattern',
|
||||
'notPattern',
|
||||
'enum',
|
||||
'defined',
|
||||
'undefined',
|
||||
@@ -69,18 +72,33 @@ export const asserts: Asserts = {
|
||||
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
|
||||
const values = runOnValue(value) ? [value] : value;
|
||||
const regx = regexFromString(condition);
|
||||
const regex = regexFromString(condition);
|
||||
|
||||
return values
|
||||
.map(
|
||||
(_val) =>
|
||||
!regx?.test(_val) && {
|
||||
!regex?.test(_val) && {
|
||||
message: `"${_val}" should match a regex ${condition}`,
|
||||
location: runOnValue(value) ? baseLocation : baseLocation.key(),
|
||||
}
|
||||
)
|
||||
.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) => {
|
||||
if (typeof value === 'undefined') return []; // property doesn't exist, no need to lint it with this assert
|
||||
const values = runOnValue(value) ? [value] : value;
|
||||
|
||||
@@ -258,6 +258,7 @@ const AssertionDefinitionAssertions: NodeType = {
|
||||
properties: {
|
||||
enum: { type: 'array', items: { type: 'string' } },
|
||||
pattern: { type: 'string' },
|
||||
notPattern: { type: 'string' },
|
||||
casing: {
|
||||
enum: [
|
||||
'camelCase',
|
||||
|
||||
Reference in New Issue
Block a user