@redocly/openapi-core 1.0.0-beta.111 → 1.0.0-beta.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config/all.js +0 -1
- package/lib/config/config-resolvers.js +19 -12
- package/lib/config/load.d.ts +1 -1
- package/lib/config/load.js +5 -5
- package/lib/config/minimal.js +0 -1
- package/lib/config/recommended.js +0 -1
- package/lib/rules/common/assertions/asserts.d.ts +22 -5
- package/lib/rules/common/assertions/asserts.js +25 -0
- package/lib/rules/common/assertions/index.d.ts +27 -2
- package/lib/rules/common/assertions/index.js +6 -29
- package/lib/rules/common/assertions/utils.d.ts +7 -14
- package/lib/rules/common/assertions/utils.js +129 -97
- package/lib/rules/oas2/index.d.ts +0 -1
- package/lib/rules/oas2/index.js +0 -2
- package/lib/rules/oas3/index.js +0 -2
- package/lib/types/redocly-yaml.js +44 -27
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +7 -1
- package/lib/visitors.d.ts +2 -1
- package/lib/visitors.js +1 -0
- package/lib/walk.js +7 -1
- package/package.json +1 -1
- package/src/__tests__/lint.test.ts +24 -5
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +1 -3
- package/src/config/__tests__/config-resolvers.test.ts +5 -5
- package/src/config/__tests__/fixtures/load-redocly.yaml +4 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +6 -4
- package/src/config/__tests__/load.test.ts +4 -1
- package/src/config/all.ts +0 -1
- package/src/config/config-resolvers.ts +42 -19
- package/src/config/load.ts +8 -5
- package/src/config/minimal.ts +0 -1
- package/src/config/recommended.ts +0 -1
- package/src/rules/common/assertions/__tests__/asserts.test.ts +7 -3
- package/src/rules/common/assertions/__tests__/index.test.ts +41 -20
- package/src/rules/common/assertions/__tests__/utils.test.ts +43 -17
- package/src/rules/common/assertions/asserts.ts +60 -8
- package/src/rules/common/assertions/index.ts +36 -46
- package/src/rules/common/assertions/utils.ts +204 -127
- package/src/rules/oas2/index.ts +0 -2
- package/src/rules/oas3/index.ts +0 -2
- package/src/types/redocly-yaml.ts +44 -29
- package/src/utils.ts +5 -0
- package/src/visitors.ts +7 -1
- package/src/walk.ts +8 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/info-description.d.ts +0 -2
- package/lib/rules/common/info-description.js +0 -12
- package/src/rules/common/__tests__/info-description.test.ts +0 -102
- package/src/rules/common/info-description.ts +0 -10
package/lib/config/all.js
CHANGED
|
@@ -254,18 +254,10 @@ function groupStyleguideAssertionRules({ rules, plugins, }) {
|
|
|
254
254
|
if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
|
|
255
255
|
const assertion = rule;
|
|
256
256
|
if (plugins) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const plugin = plugins.find((plugin) => plugin.id === pluginId);
|
|
262
|
-
if (!plugin) {
|
|
263
|
-
throw Error(logger_1.colorize.red(`Plugin ${logger_1.colorize.blue(pluginId)} isn't found.`));
|
|
264
|
-
}
|
|
265
|
-
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
266
|
-
throw Error(`Plugin ${logger_1.colorize.red(pluginId)} doesn't export assertions function with name ${logger_1.colorize.red(fn)}.`);
|
|
267
|
-
}
|
|
268
|
-
asserts_1.asserts[field] = asserts_1.buildAssertCustomFunction(plugin.assertions[fn]);
|
|
257
|
+
registerCustomAssertions(plugins, assertion);
|
|
258
|
+
// We may have custom assertion inside where block
|
|
259
|
+
for (const context of assertion.where || []) {
|
|
260
|
+
registerCustomAssertions(plugins, context);
|
|
269
261
|
}
|
|
270
262
|
}
|
|
271
263
|
assertions.push(Object.assign(Object.assign({}, assertion), { assertionId: ruleKey.replace('assert/', '') }));
|
|
@@ -280,3 +272,18 @@ function groupStyleguideAssertionRules({ rules, plugins, }) {
|
|
|
280
272
|
}
|
|
281
273
|
return transformedRules;
|
|
282
274
|
}
|
|
275
|
+
function registerCustomAssertions(plugins, assertion) {
|
|
276
|
+
for (const field of utils_3.keysOf(assertion.assertions)) {
|
|
277
|
+
const [pluginId, fn] = field.split('/');
|
|
278
|
+
if (!pluginId || !fn)
|
|
279
|
+
continue;
|
|
280
|
+
const plugin = plugins.find((plugin) => plugin.id === pluginId);
|
|
281
|
+
if (!plugin) {
|
|
282
|
+
throw Error(logger_1.colorize.red(`Plugin ${logger_1.colorize.blue(pluginId)} isn't found.`));
|
|
283
|
+
}
|
|
284
|
+
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
285
|
+
throw Error(`Plugin ${logger_1.colorize.red(pluginId)} doesn't export assertions function with name ${logger_1.colorize.red(fn)}.`);
|
|
286
|
+
}
|
|
287
|
+
asserts_1.asserts[field] = asserts_1.buildAssertCustomFunction(plugin.assertions[fn]);
|
|
288
|
+
}
|
|
289
|
+
}
|
package/lib/config/load.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare function loadConfig(options?: {
|
|
|
10
10
|
}): Promise<Config>;
|
|
11
11
|
export declare const CONFIG_FILE_NAMES: string[];
|
|
12
12
|
export declare function findConfig(dir?: string): string | undefined;
|
|
13
|
-
export declare function getConfig(configPath?: string | undefined): Promise<RawConfig>;
|
|
13
|
+
export declare function getConfig(configPath?: string | undefined, processRawConfig?: (rawConfig: RawConfig) => void | Promise<void>): Promise<RawConfig>;
|
|
14
14
|
declare type CreateConfigOptions = {
|
|
15
15
|
extends?: string[];
|
|
16
16
|
tokens?: RegionalTokenWithValidity[];
|
package/lib/config/load.js
CHANGED
|
@@ -62,11 +62,8 @@ function addConfigMetadata({ rawConfig, customExtends, configPath, tokens, }) {
|
|
|
62
62
|
function loadConfig(options = {}) {
|
|
63
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
64
|
const { configPath = findConfig(), customExtends, processRawConfig, files, region } = options;
|
|
65
|
-
const config = yield getConfig(configPath);
|
|
65
|
+
const config = yield getConfig(configPath, processRawConfig);
|
|
66
66
|
const rawConfig = Object.assign(Object.assign({}, config), { files: files !== null && files !== void 0 ? files : config.files, region: region !== null && region !== void 0 ? region : config.region });
|
|
67
|
-
if (typeof processRawConfig === 'function') {
|
|
68
|
-
yield processRawConfig(rawConfig);
|
|
69
|
-
}
|
|
70
67
|
const redoclyClient = new redocly_1.RedoclyClient();
|
|
71
68
|
const tokens = yield redoclyClient.getTokens();
|
|
72
69
|
return addConfigMetadata({
|
|
@@ -93,12 +90,15 @@ function findConfig(dir) {
|
|
|
93
90
|
return existingConfigFiles[0];
|
|
94
91
|
}
|
|
95
92
|
exports.findConfig = findConfig;
|
|
96
|
-
function getConfig(configPath = findConfig()) {
|
|
93
|
+
function getConfig(configPath = findConfig(), processRawConfig) {
|
|
97
94
|
return __awaiter(this, void 0, void 0, function* () {
|
|
98
95
|
if (!configPath || !utils_1.doesYamlFileExist(configPath))
|
|
99
96
|
return {};
|
|
100
97
|
try {
|
|
101
98
|
const rawConfig = (yield utils_1.loadYaml(configPath)) || {};
|
|
99
|
+
if (typeof processRawConfig === 'function') {
|
|
100
|
+
yield processRawConfig(rawConfig);
|
|
101
|
+
}
|
|
102
102
|
return utils_2.transformConfig(rawConfig);
|
|
103
103
|
}
|
|
104
104
|
catch (e) {
|
package/lib/config/minimal.js
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { AssertResult, CustomFunction } from 'core/src/config/types';
|
|
2
2
|
import { Location } from '../../../ref-utils';
|
|
3
|
-
declare type
|
|
4
|
-
export declare
|
|
5
|
-
|
|
3
|
+
export declare type AssertionFn = (value: any, condition: any, baseLocation: Location, rawValue?: any) => AssertResult[];
|
|
4
|
+
export declare type Asserts = {
|
|
5
|
+
pattern: AssertionFn;
|
|
6
|
+
enum: AssertionFn;
|
|
7
|
+
defined: AssertionFn;
|
|
8
|
+
required: AssertionFn;
|
|
9
|
+
disallowed: AssertionFn;
|
|
10
|
+
undefined: AssertionFn;
|
|
11
|
+
nonEmpty: AssertionFn;
|
|
12
|
+
minLength: AssertionFn;
|
|
13
|
+
maxLength: AssertionFn;
|
|
14
|
+
casing: AssertionFn;
|
|
15
|
+
sortOrder: AssertionFn;
|
|
16
|
+
mutuallyExclusive: AssertionFn;
|
|
17
|
+
mutuallyRequired: AssertionFn;
|
|
18
|
+
requireAny: AssertionFn;
|
|
19
|
+
ref: AssertionFn;
|
|
20
|
+
const: AssertionFn;
|
|
21
|
+
};
|
|
22
|
+
export declare const runOnKeysSet: Set<keyof Asserts>;
|
|
23
|
+
export declare const runOnValuesSet: Set<keyof Asserts>;
|
|
6
24
|
export declare const asserts: Asserts;
|
|
7
|
-
export declare function buildAssertCustomFunction(fn: CustomFunction):
|
|
8
|
-
export {};
|
|
25
|
+
export declare function buildAssertCustomFunction(fn: CustomFunction): AssertionFn;
|
|
@@ -16,6 +16,8 @@ exports.runOnKeysSet = new Set([
|
|
|
16
16
|
'required',
|
|
17
17
|
'requireAny',
|
|
18
18
|
'ref',
|
|
19
|
+
'const',
|
|
20
|
+
'defined', // In case if `property` for assertions is not added
|
|
19
21
|
]);
|
|
20
22
|
exports.runOnValuesSet = new Set([
|
|
21
23
|
'pattern',
|
|
@@ -28,6 +30,7 @@ exports.runOnValuesSet = new Set([
|
|
|
28
30
|
'casing',
|
|
29
31
|
'sortOrder',
|
|
30
32
|
'ref',
|
|
33
|
+
'const',
|
|
31
34
|
]);
|
|
32
35
|
exports.asserts = {
|
|
33
36
|
pattern: (value, condition, baseLocation) => {
|
|
@@ -84,6 +87,28 @@ exports.asserts = {
|
|
|
84
87
|
})
|
|
85
88
|
.filter(utils_1.isTruthy);
|
|
86
89
|
},
|
|
90
|
+
const: (value, condition, baseLocation) => {
|
|
91
|
+
if (typeof value === 'undefined')
|
|
92
|
+
return [];
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
return value
|
|
95
|
+
.map((_val) => condition !== _val && {
|
|
96
|
+
message: `"${_val}" should be equal ${condition} `,
|
|
97
|
+
location: utils_1.isString(value) ? baseLocation : baseLocation.child(_val).key(),
|
|
98
|
+
})
|
|
99
|
+
.filter(utils_1.isTruthy);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return value !== condition
|
|
103
|
+
? [
|
|
104
|
+
{
|
|
105
|
+
message: `${value} should be equal ${condition}`,
|
|
106
|
+
location: baseLocation,
|
|
107
|
+
},
|
|
108
|
+
]
|
|
109
|
+
: [];
|
|
110
|
+
}
|
|
111
|
+
},
|
|
87
112
|
undefined: (value, condition = true, baseLocation) => {
|
|
88
113
|
const isUndefined = typeof value === 'undefined';
|
|
89
114
|
const isValid = condition ? isUndefined : !isUndefined;
|
|
@@ -1,2 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { asserts, AssertionFn } from './asserts';
|
|
2
|
+
import { Oas2Visitor, Oas3Visitor } from '../../../visitors';
|
|
3
|
+
import { RuleSeverity } from '../../../config';
|
|
4
|
+
export declare type AssertionLocators = {
|
|
5
|
+
filterInParentKeys?: (string | number)[];
|
|
6
|
+
filterOutParentKeys?: (string | number)[];
|
|
7
|
+
matchParentKeys?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare type AssertionDefinition = {
|
|
10
|
+
subject: {
|
|
11
|
+
type: string;
|
|
12
|
+
property?: string | string[];
|
|
13
|
+
} & AssertionLocators;
|
|
14
|
+
assertions: {
|
|
15
|
+
[name in keyof typeof asserts]?: AssertionFn;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare type RawAssertion = AssertionDefinition & {
|
|
19
|
+
where?: AssertionDefinition[];
|
|
20
|
+
message?: string;
|
|
21
|
+
suggest?: string[];
|
|
22
|
+
severity?: RuleSeverity;
|
|
23
|
+
};
|
|
24
|
+
export declare type Assertion = RawAssertion & {
|
|
25
|
+
assertionId: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const Assertions: (opts: Record<string, Assertion>) => (Oas3Visitor | Oas2Visitor)[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Assertions = void 0;
|
|
4
|
-
const asserts_1 = require("./asserts");
|
|
5
4
|
const utils_1 = require("./utils");
|
|
5
|
+
const utils_2 = require("../../../utils");
|
|
6
6
|
const Assertions = (opts) => {
|
|
7
7
|
const visitors = [];
|
|
8
8
|
// As 'Assertions' has an array of asserts,
|
|
@@ -13,35 +13,12 @@ const Assertions = (opts) => {
|
|
|
13
13
|
const assertions = Object.values(opts).filter((opt) => typeof opt === 'object' && opt !== null);
|
|
14
14
|
for (const [index, assertion] of assertions.entries()) {
|
|
15
15
|
const assertId = (assertion.assertionId && `${assertion.assertionId} assertion`) || `assertion #${index + 1}`;
|
|
16
|
-
if (!assertion.subject) {
|
|
17
|
-
throw new Error(`${assertId}: '
|
|
18
|
-
}
|
|
19
|
-
const subjects = Array.isArray(assertion.subject)
|
|
20
|
-
? assertion.subject
|
|
21
|
-
: [assertion.subject];
|
|
22
|
-
const assertsToApply = Object.keys(asserts_1.asserts)
|
|
23
|
-
.filter((assertName) => assertion[assertName] !== undefined)
|
|
24
|
-
.map((assertName) => {
|
|
25
|
-
return {
|
|
26
|
-
name: assertName,
|
|
27
|
-
conditions: assertion[assertName],
|
|
28
|
-
runsOnKeys: asserts_1.runOnKeysSet.has(assertName),
|
|
29
|
-
runsOnValues: asserts_1.runOnValuesSet.has(assertName),
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
const shouldRunOnKeys = assertsToApply.find((assert) => assert.runsOnKeys && !assert.runsOnValues);
|
|
33
|
-
const shouldRunOnValues = assertsToApply.find((assert) => assert.runsOnValues && !assert.runsOnKeys);
|
|
34
|
-
if (shouldRunOnValues && !assertion.property) {
|
|
35
|
-
throw new Error(`${shouldRunOnValues.name} can't be used on all keys. Please provide a single property.`);
|
|
36
|
-
}
|
|
37
|
-
if (shouldRunOnKeys && assertion.property) {
|
|
38
|
-
throw new Error(`${shouldRunOnKeys.name} can't be used on a single property. Please use 'property'.`);
|
|
39
|
-
}
|
|
40
|
-
for (const subject of subjects) {
|
|
41
|
-
const subjectVisitor = utils_1.buildSubjectVisitor(assertId, assertion, assertsToApply);
|
|
42
|
-
const visitorObject = utils_1.buildVisitorObject(subject, assertion.context, subjectVisitor);
|
|
43
|
-
visitors.push(visitorObject);
|
|
16
|
+
if (!utils_2.isString(assertion.subject.type)) {
|
|
17
|
+
throw new Error(`${assertId}: 'type' (String) is required`);
|
|
44
18
|
}
|
|
19
|
+
const subjectVisitor = utils_1.buildSubjectVisitor(assertId, assertion);
|
|
20
|
+
const visitorObject = utils_1.buildVisitorObject(assertion, subjectVisitor);
|
|
21
|
+
visitors.push(visitorObject);
|
|
45
22
|
}
|
|
46
23
|
return visitors;
|
|
47
24
|
};
|
|
@@ -1,27 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { Asserts } from './asserts';
|
|
2
|
+
import type { Assertion, AssertionDefinition } from '.';
|
|
3
|
+
import type { Oas2Visitor, Oas3Visitor, VisitFunction } from '../../../visitors';
|
|
3
4
|
export declare type OrderDirection = 'asc' | 'desc';
|
|
4
5
|
export declare type OrderOptions = {
|
|
5
6
|
direction: OrderDirection;
|
|
6
7
|
property: string;
|
|
7
8
|
};
|
|
8
|
-
declare type Assertion = {
|
|
9
|
-
property: string | string[];
|
|
10
|
-
context?: Record<string, any>[];
|
|
11
|
-
severity?: RuleSeverity;
|
|
12
|
-
suggest?: any[];
|
|
13
|
-
message?: string;
|
|
14
|
-
subject: string;
|
|
15
|
-
};
|
|
16
9
|
export declare type AssertToApply = {
|
|
17
|
-
name:
|
|
10
|
+
name: keyof Asserts;
|
|
18
11
|
conditions: any;
|
|
19
12
|
runsOnKeys: boolean;
|
|
20
13
|
runsOnValues: boolean;
|
|
21
14
|
};
|
|
22
|
-
export declare function
|
|
23
|
-
export declare function
|
|
15
|
+
export declare function getAssertsToApply(assertion: AssertionDefinition): AssertToApply[];
|
|
16
|
+
export declare function buildVisitorObject(assertion: Assertion, subjectVisitor: VisitFunction<any>): Oas2Visitor | Oas3Visitor;
|
|
17
|
+
export declare function buildSubjectVisitor(assertId: string, assertion: Assertion): VisitFunction<any>;
|
|
24
18
|
export declare function getIntersectionLength(keys: string[], properties: string[]): number;
|
|
25
19
|
export declare function isOrdered(value: any[], options: OrderOptions | OrderDirection): boolean;
|
|
26
20
|
export declare function regexFromString(input: string): RegExp | null;
|
|
27
|
-
export {};
|
|
@@ -1,122 +1,154 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.regexFromString = exports.isOrdered = exports.getIntersectionLength = exports.buildSubjectVisitor = exports.buildVisitorObject = void 0;
|
|
3
|
+
exports.regexFromString = exports.isOrdered = exports.getIntersectionLength = exports.buildSubjectVisitor = exports.buildVisitorObject = exports.getAssertsToApply = void 0;
|
|
4
|
+
const asserts_1 = require("./asserts");
|
|
4
5
|
const logger_1 = require("../../../logger");
|
|
5
6
|
const ref_utils_1 = require("../../../ref-utils");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const utils_1 = require("../../../utils");
|
|
8
|
+
const assertionMessageTemplates = {
|
|
9
|
+
problems: '{{problems}}',
|
|
10
|
+
};
|
|
11
|
+
function getPredicatesFromLocators(locators) {
|
|
12
|
+
const { filterInParentKeys, filterOutParentKeys, matchParentKeys } = locators;
|
|
13
|
+
const keyMatcher = matchParentKeys && regexFromString(matchParentKeys);
|
|
14
|
+
const matchKeysPredicate = keyMatcher && ((key) => keyMatcher.test(key.toString()));
|
|
15
|
+
const filterInPredicate = Array.isArray(filterInParentKeys) &&
|
|
16
|
+
((key) => filterInParentKeys.includes(key.toString()));
|
|
17
|
+
const filterOutPredicate = Array.isArray(filterOutParentKeys) &&
|
|
18
|
+
((key) => !filterOutParentKeys.includes(key.toString()));
|
|
19
|
+
return [matchKeysPredicate, filterInPredicate, filterOutPredicate].filter(utils_1.isTruthy);
|
|
20
|
+
}
|
|
21
|
+
function getAssertsToApply(assertion) {
|
|
22
|
+
const assertsToApply = utils_1.keysOf(asserts_1.asserts)
|
|
23
|
+
.filter((assertName) => assertion.assertions[assertName] !== undefined)
|
|
24
|
+
.map((assertName) => {
|
|
25
|
+
return {
|
|
26
|
+
name: assertName,
|
|
27
|
+
conditions: assertion.assertions[assertName],
|
|
28
|
+
runsOnKeys: asserts_1.runOnKeysSet.has(assertName),
|
|
29
|
+
runsOnValues: asserts_1.runOnValuesSet.has(assertName),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const shouldRunOnKeys = assertsToApply.find((assert) => assert.runsOnKeys && !assert.runsOnValues);
|
|
33
|
+
const shouldRunOnValues = assertsToApply.find((assert) => assert.runsOnValues && !assert.runsOnKeys);
|
|
34
|
+
if (shouldRunOnValues && !assertion.subject.property) {
|
|
35
|
+
throw new Error(`${shouldRunOnValues.name} can't be used on all keys. Please provide a single property`);
|
|
36
|
+
}
|
|
37
|
+
if (shouldRunOnKeys && assertion.subject.property) {
|
|
38
|
+
throw new Error(`${shouldRunOnKeys.name} can't be used on a single property. Please use 'property'.`);
|
|
39
|
+
}
|
|
40
|
+
return assertsToApply;
|
|
41
|
+
}
|
|
42
|
+
exports.getAssertsToApply = getAssertsToApply;
|
|
43
|
+
function getAssertionProperties({ subject }) {
|
|
44
|
+
return (Array.isArray(subject.property) ? subject.property : [subject === null || subject === void 0 ? void 0 : subject.property]).filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
function applyAssertions(assertionDefinition, asserts, { rawLocation, rawNode, resolve, location, node }) {
|
|
47
|
+
var _a;
|
|
48
|
+
const properties = getAssertionProperties(assertionDefinition);
|
|
49
|
+
const assertResults = [];
|
|
50
|
+
for (const assert of asserts) {
|
|
51
|
+
const currentLocation = assert.name === 'ref' ? rawLocation : location;
|
|
52
|
+
if (properties.length) {
|
|
53
|
+
for (const property of properties) {
|
|
54
|
+
// we can have resolvable scalar so need to resolve value here.
|
|
55
|
+
const value = ref_utils_1.isRef(node[property]) ? (_a = resolve(node[property])) === null || _a === void 0 ? void 0 : _a.node : node[property];
|
|
56
|
+
assertResults.push(runAssertion({
|
|
57
|
+
values: value,
|
|
58
|
+
rawValues: rawNode[property],
|
|
59
|
+
assert,
|
|
60
|
+
location: currentLocation.child(property),
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const value = assert.name === 'ref' ? rawNode : Object.keys(node);
|
|
66
|
+
assertResults.push(runAssertion({
|
|
67
|
+
values: Object.keys(node),
|
|
68
|
+
rawValues: value,
|
|
69
|
+
assert,
|
|
70
|
+
location: currentLocation,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return assertResults.flat();
|
|
75
|
+
}
|
|
76
|
+
function buildVisitorObject(assertion, subjectVisitor) {
|
|
77
|
+
var _a, _b;
|
|
78
|
+
const targetVisitorLocatorPredicates = getPredicatesFromLocators(assertion.subject);
|
|
79
|
+
const targetVisitorSkipFunction = targetVisitorLocatorPredicates.length
|
|
80
|
+
? (node, key) => !targetVisitorLocatorPredicates.every((predicate) => predicate(key))
|
|
81
|
+
: undefined;
|
|
82
|
+
const targetVisitor = {
|
|
83
|
+
[assertion.subject.type]: Object.assign({ enter: subjectVisitor }, (targetVisitorSkipFunction && { skip: targetVisitorSkipFunction })),
|
|
84
|
+
};
|
|
85
|
+
if (!Array.isArray(assertion.where)) {
|
|
86
|
+
return targetVisitor;
|
|
10
87
|
}
|
|
11
88
|
let currentVisitorLevel = {};
|
|
12
89
|
const visitor = currentVisitorLevel;
|
|
90
|
+
const context = assertion.where;
|
|
13
91
|
for (let index = 0; index < context.length; index++) {
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
// as a workaround for that we don't create separate visitor for the last element
|
|
18
|
-
// which is the same as subject;
|
|
19
|
-
// we will check includes/excludes it in the last visitor.
|
|
20
|
-
continue;
|
|
92
|
+
const assertionDefinitionNode = context[index];
|
|
93
|
+
if (!utils_1.isString((_a = assertionDefinitionNode.subject) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
94
|
+
throw new Error(`${assertion.assertionId} -> where -> [${index}]: 'type' (String) is required`);
|
|
21
95
|
}
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
96
|
+
const locatorPredicates = getPredicatesFromLocators(assertionDefinitionNode.subject);
|
|
97
|
+
const assertsToApply = getAssertsToApply(assertionDefinitionNode);
|
|
98
|
+
const skipFunction = (node, key, { location, rawLocation, resolve, rawNode }) => !locatorPredicates.every((predicate) => predicate(key)) ||
|
|
99
|
+
!!applyAssertions(assertionDefinitionNode, assertsToApply, {
|
|
100
|
+
location,
|
|
101
|
+
node,
|
|
102
|
+
rawLocation,
|
|
103
|
+
rawNode,
|
|
104
|
+
resolve,
|
|
105
|
+
}).length;
|
|
106
|
+
const nodeVisitor = Object.assign({}, ((locatorPredicates.length || assertsToApply.length) && { skip: skipFunction }));
|
|
107
|
+
if (assertionDefinitionNode.subject.type === assertion.subject.type &&
|
|
108
|
+
index === context.length - 1) {
|
|
109
|
+
// We have to merge the visitors if the last node inside the `where` is the same as the subject.
|
|
110
|
+
targetVisitor[assertion.subject.type] = Object.assign({ enter: subjectVisitor }, ((nodeVisitor.skip && { skip: nodeVisitor.skip }) ||
|
|
111
|
+
(targetVisitorSkipFunction && {
|
|
112
|
+
skip: (node, key, ctx // We may have locators defined on assertion level and on where level for the same node type
|
|
113
|
+
) => { var _a; return !!(((_a = nodeVisitor.skip) === null || _a === void 0 ? void 0 : _a.call(nodeVisitor, node, key, ctx)) || (targetVisitorSkipFunction === null || targetVisitorSkipFunction === void 0 ? void 0 : targetVisitorSkipFunction(node, key))); },
|
|
114
|
+
})));
|
|
38
115
|
}
|
|
39
116
|
else {
|
|
40
|
-
currentVisitorLevel[
|
|
117
|
+
currentVisitorLevel = currentVisitorLevel[(_b = assertionDefinitionNode.subject) === null || _b === void 0 ? void 0 : _b.type] =
|
|
118
|
+
nodeVisitor;
|
|
41
119
|
}
|
|
42
|
-
currentVisitorLevel = currentVisitorLevel[node.type];
|
|
43
120
|
}
|
|
44
|
-
currentVisitorLevel[subject] =
|
|
121
|
+
currentVisitorLevel[assertion.subject.type] = targetVisitor[assertion.subject.type];
|
|
45
122
|
return visitor;
|
|
46
123
|
}
|
|
47
124
|
exports.buildVisitorObject = buildVisitorObject;
|
|
48
|
-
function buildSubjectVisitor(assertId, assertion
|
|
49
|
-
return (node, { report, location, rawLocation,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const excludeParentKeys = lastContextNode.excludeParentKeys;
|
|
60
|
-
if (matchParentKeys && !matchParentKeys.includes(key)) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (excludeParentKeys && excludeParentKeys.includes(key)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (properties) {
|
|
69
|
-
properties = Array.isArray(properties) ? properties : [properties];
|
|
70
|
-
}
|
|
71
|
-
const defaultMessage = `${logger_1.colorize.blue(assertId)} failed because the ${logger_1.colorize.blue(assertion.subject)}${logger_1.colorize.blue(properties ? ` ${properties.join(', ')}` : '')} didn't meet the assertions: {{problems}}`;
|
|
72
|
-
const assertResults = [];
|
|
73
|
-
for (const assert of asserts) {
|
|
74
|
-
const currentLocation = assert.name === 'ref' ? rawLocation : location;
|
|
75
|
-
if (properties) {
|
|
76
|
-
for (const property of properties) {
|
|
77
|
-
// we can have resolvable scalar so need to resolve value here.
|
|
78
|
-
const value = ref_utils_1.isRef(node[property]) ? (_a = resolve(node[property])) === null || _a === void 0 ? void 0 : _a.node : node[property];
|
|
79
|
-
assertResults.push(runAssertion({
|
|
80
|
-
values: value,
|
|
81
|
-
rawValues: rawNode[property],
|
|
82
|
-
assert,
|
|
83
|
-
location: currentLocation.child(property),
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
const value = assert.name === 'ref' ? rawNode : Object.keys(node);
|
|
89
|
-
assertResults.push(runAssertion({
|
|
90
|
-
values: Object.keys(node),
|
|
91
|
-
rawValues: value,
|
|
92
|
-
assert,
|
|
93
|
-
location: currentLocation,
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const problems = assertResults.flat();
|
|
125
|
+
function buildSubjectVisitor(assertId, assertion) {
|
|
126
|
+
return (node, { report, location, rawLocation, resolve, rawNode }) => {
|
|
127
|
+
const properties = getAssertionProperties(assertion);
|
|
128
|
+
const defaultMessage = `${logger_1.colorize.blue(assertId)} failed because the ${logger_1.colorize.blue(assertion.subject.type)} ${logger_1.colorize.blue(properties.join(', '))} didn't meet the assertions: ${assertionMessageTemplates.problems}`.replace(/ +/g, ' ');
|
|
129
|
+
const problems = applyAssertions(assertion, getAssertsToApply(assertion), {
|
|
130
|
+
rawLocation,
|
|
131
|
+
rawNode,
|
|
132
|
+
resolve,
|
|
133
|
+
location,
|
|
134
|
+
node,
|
|
135
|
+
});
|
|
98
136
|
if (problems.length) {
|
|
99
137
|
const message = assertion.message || defaultMessage;
|
|
100
|
-
|
|
101
|
-
message
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
for (const problem of problems) {
|
|
139
|
+
const problemMessage = problem.message ? problem.message : defaultMessage;
|
|
140
|
+
report({
|
|
141
|
+
message: message.replace(assertionMessageTemplates.problems, problemMessage),
|
|
142
|
+
location: problem.location || location,
|
|
143
|
+
forceSeverity: assertion.severity || 'error',
|
|
144
|
+
suggest: assertion.suggest || [],
|
|
145
|
+
ruleId: assertId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
107
148
|
}
|
|
108
149
|
};
|
|
109
150
|
}
|
|
110
151
|
exports.buildSubjectVisitor = buildSubjectVisitor;
|
|
111
|
-
function getProblemsLocation(problems) {
|
|
112
|
-
return problems.length ? problems[0].location : undefined;
|
|
113
|
-
}
|
|
114
|
-
function getProblemsMessage(problems) {
|
|
115
|
-
var _a;
|
|
116
|
-
return problems.length === 1
|
|
117
|
-
? (_a = problems[0].message) !== null && _a !== void 0 ? _a : ''
|
|
118
|
-
: problems.map((problem) => { var _a; return `\n- ${(_a = problem.message) !== null && _a !== void 0 ? _a : ''}`; }).join('');
|
|
119
|
-
}
|
|
120
152
|
function getIntersectionLength(keys, properties) {
|
|
121
153
|
const props = new Set(properties);
|
|
122
154
|
let count = 0;
|
package/lib/rules/oas2/index.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.preprocessors = exports.rules = void 0;
|
|
|
4
4
|
const spec_1 = require("../common/spec");
|
|
5
5
|
const no_invalid_schema_examples_1 = require("../common/no-invalid-schema-examples");
|
|
6
6
|
const no_invalid_parameter_examples_1 = require("../common/no-invalid-parameter-examples");
|
|
7
|
-
const info_description_1 = require("../common/info-description");
|
|
8
7
|
const info_contact_1 = require("../common/info-contact");
|
|
9
8
|
const info_license_1 = require("../common/info-license");
|
|
10
9
|
const info_license_url_1 = require("../common/info-license-url");
|
|
@@ -46,7 +45,6 @@ exports.rules = {
|
|
|
46
45
|
spec: spec_1.OasSpec,
|
|
47
46
|
'no-invalid-schema-examples': no_invalid_schema_examples_1.NoInvalidSchemaExamples,
|
|
48
47
|
'no-invalid-parameter-examples': no_invalid_parameter_examples_1.NoInvalidParameterExamples,
|
|
49
|
-
'info-description': info_description_1.InfoDescription,
|
|
50
48
|
'info-contact': info_contact_1.InfoContact,
|
|
51
49
|
'info-license': info_license_1.InfoLicense,
|
|
52
50
|
'info-license-url': info_license_url_1.InfoLicenseUrl,
|
package/lib/rules/oas3/index.js
CHANGED
|
@@ -17,7 +17,6 @@ const operation_operationId_url_safe_1 = require("../common/operation-operationI
|
|
|
17
17
|
const tags_alphabetical_1 = require("../common/tags-alphabetical");
|
|
18
18
|
const no_server_example_com_1 = require("./no-server-example.com");
|
|
19
19
|
const no_server_trailing_slash_1 = require("./no-server-trailing-slash");
|
|
20
|
-
const info_description_1 = require("../common/info-description");
|
|
21
20
|
const tag_description_1 = require("../common/tag-description");
|
|
22
21
|
const info_contact_1 = require("../common/info-contact");
|
|
23
22
|
const info_license_1 = require("../common/info-license");
|
|
@@ -54,7 +53,6 @@ const spec_components_invalid_map_name_1 = require("./spec-components-invalid-ma
|
|
|
54
53
|
const operation_4xx_problem_details_rfc7807_1 = require("./operation-4xx-problem-details-rfc7807");
|
|
55
54
|
exports.rules = {
|
|
56
55
|
spec: spec_1.OasSpec,
|
|
57
|
-
'info-description': info_description_1.InfoDescription,
|
|
58
56
|
'info-contact': info_contact_1.InfoContact,
|
|
59
57
|
'info-license': info_license_1.InfoLicense,
|
|
60
58
|
'info-license-url': info_license_url_1.InfoLicenseUrl,
|