@redocly/openapi-core 1.0.0-beta.108 → 1.0.0-beta.110
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/README.md +2 -2
- package/lib/benchmark/benches/resolve-with-no-external.bench.js +1 -1
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +4 -4
- package/lib/config/all.js +3 -1
- package/lib/config/config-resolvers.js +22 -4
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +1 -0
- package/lib/config/load.d.ts +8 -2
- package/lib/config/load.js +4 -2
- package/lib/config/minimal.js +3 -1
- package/lib/config/recommended.js +3 -1
- package/lib/config/rules.js +1 -1
- package/lib/config/types.d.ts +17 -0
- package/lib/config/utils.d.ts +2 -2
- package/lib/config/utils.js +44 -6
- package/lib/decorators/common/registry-dependencies.js +1 -1
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +22 -1
- package/lib/lint.js +2 -2
- package/lib/redocly/registry-api.d.ts +0 -1
- package/lib/redocly/registry-api.js +5 -4
- package/lib/resolve.js +3 -1
- package/lib/rules/ajv.d.ts +1 -1
- package/lib/rules/ajv.js +5 -5
- package/lib/rules/common/assertions/asserts.d.ts +3 -5
- package/lib/rules/common/assertions/asserts.js +137 -97
- package/lib/rules/common/assertions/index.js +2 -6
- package/lib/rules/common/assertions/utils.d.ts +12 -6
- package/lib/rules/common/assertions/utils.js +33 -20
- package/lib/rules/common/no-ambiguous-paths.js +1 -1
- package/lib/rules/common/no-identical-paths.js +1 -1
- package/lib/rules/common/operation-2xx-response.js +1 -1
- package/lib/rules/common/operation-4xx-response.js +1 -1
- package/lib/rules/common/operation-operationId.js +1 -1
- package/lib/rules/common/operation-tag-defined.js +1 -1
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/security-defined.d.ts +2 -0
- package/lib/rules/common/{operation-security-defined.js → security-defined.js} +18 -4
- package/lib/rules/common/spec.js +12 -1
- package/lib/rules/common/tags-alphabetical.js +1 -1
- package/lib/rules/oas2/index.d.ts +1 -1
- package/lib/rules/oas2/index.js +2 -2
- package/lib/rules/oas2/remove-unused-components.js +1 -1
- package/lib/rules/oas2/request-mime-type.js +1 -1
- package/lib/rules/oas2/response-mime-type.js +1 -1
- package/lib/rules/oas3/index.js +6 -2
- package/lib/rules/oas3/no-empty-servers.js +1 -1
- package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -1
- package/lib/rules/oas3/no-unused-components.js +1 -1
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
- package/lib/rules/oas3/remove-unused-components.js +1 -1
- package/lib/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
- package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
- package/lib/rules/other/stats.d.ts +2 -2
- package/lib/rules/other/stats.js +2 -2
- package/lib/rules/utils.js +1 -1
- package/lib/types/oas2.js +5 -5
- package/lib/types/oas3.js +27 -20
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +60 -54
- package/lib/utils.d.ts +3 -3
- package/lib/utils.js +5 -5
- package/lib/visitors.d.ts +11 -11
- package/lib/visitors.js +13 -1
- package/package.json +3 -5
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
- package/src/__tests__/fixtures/extension.js +3 -3
- package/src/__tests__/format.test.ts +76 -0
- package/src/__tests__/lint.test.ts +184 -121
- package/src/__tests__/resolve-http.test.ts +1 -1
- package/src/__tests__/resolve.test.ts +9 -9
- package/src/__tests__/walk.test.ts +78 -10
- package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
- package/src/bundle.ts +4 -4
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -2
- package/src/config/__tests__/config-resolvers.test.ts +37 -1
- package/src/config/__tests__/config.test.ts +5 -0
- package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -19
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
- package/src/config/__tests__/load.test.ts +13 -16
- package/src/config/__tests__/resolve-plugins.test.ts +3 -3
- package/src/config/__tests__/utils.test.ts +64 -4
- package/src/config/all.ts +3 -1
- package/src/config/config-resolvers.ts +30 -7
- package/src/config/config.ts +2 -0
- package/src/config/load.ts +13 -6
- package/src/config/minimal.ts +3 -1
- package/src/config/recommended.ts +3 -1
- package/src/config/rules.ts +2 -2
- package/src/config/types.ts +24 -0
- package/src/config/utils.ts +103 -13
- package/src/decorators/common/registry-dependencies.ts +1 -1
- package/src/format/format.ts +32 -2
- package/src/lint.ts +2 -2
- package/src/redocly/registry-api.ts +5 -4
- package/src/resolve.ts +3 -1
- package/src/rules/__tests__/utils.test.ts +1 -1
- package/src/rules/ajv.ts +4 -4
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
- package/src/rules/common/__tests__/security-defined.test.ts +175 -0
- package/src/rules/common/__tests__/spec.test.ts +79 -0
- package/src/rules/common/assertions/__tests__/asserts.test.ts +491 -428
- package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
- package/src/rules/common/assertions/asserts.ts +155 -97
- package/src/rules/common/assertions/index.ts +2 -11
- package/src/rules/common/assertions/utils.ts +66 -36
- package/src/rules/common/no-ambiguous-paths.ts +1 -1
- package/src/rules/common/no-identical-paths.ts +1 -1
- package/src/rules/common/operation-2xx-response.ts +1 -1
- package/src/rules/common/operation-4xx-response.ts +1 -1
- package/src/rules/common/operation-operationId.ts +1 -1
- package/src/rules/common/operation-tag-defined.ts +1 -1
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +19 -4
- package/src/rules/common/spec.ts +15 -1
- package/src/rules/common/tags-alphabetical.ts +1 -1
- package/src/rules/oas2/index.ts +2 -2
- package/src/rules/oas2/remove-unused-components.ts +1 -1
- package/src/rules/oas2/request-mime-type.ts +1 -1
- package/src/rules/oas2/response-mime-type.ts +1 -1
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
- package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
- package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
- package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
- package/src/rules/oas3/index.ts +6 -2
- package/src/rules/oas3/no-empty-servers.ts +1 -1
- package/src/rules/oas3/no-server-variables-empty-enum.ts +1 -1
- package/src/rules/oas3/no-unused-components.ts +1 -1
- package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
- package/src/rules/oas3/remove-unused-components.ts +1 -1
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
- package/src/rules/other/stats.ts +2 -2
- package/src/rules/utils.ts +2 -1
- package/src/types/index.ts +2 -2
- package/src/types/oas2.ts +5 -5
- package/src/types/oas3.ts +27 -20
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +66 -38
- package/src/utils.ts +11 -7
- package/src/visitors.ts +29 -13
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/operation-security-defined.d.ts +0 -2
- package/src/rules/common/__tests__/operation-security-defined.test.ts +0 -69
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.asserts = exports.runOnValuesSet = exports.runOnKeysSet = void 0;
|
|
3
|
+
exports.buildAssertCustomFunction = exports.asserts = exports.runOnValuesSet = exports.runOnKeysSet = void 0;
|
|
4
4
|
const utils_1 = require("../../../utils");
|
|
5
5
|
const utils_2 = require("./utils");
|
|
6
6
|
exports.runOnKeysSet = new Set([
|
|
@@ -32,145 +32,185 @@ exports.runOnValuesSet = new Set([
|
|
|
32
32
|
exports.asserts = {
|
|
33
33
|
pattern: (value, condition, baseLocation) => {
|
|
34
34
|
if (typeof value === 'undefined')
|
|
35
|
-
return
|
|
35
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
36
36
|
const values = utils_1.isString(value) ? [value] : value;
|
|
37
37
|
const regx = utils_2.regexFromString(condition);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
38
|
+
return values
|
|
39
|
+
.map((_val) => !(regx === null || regx === void 0 ? void 0 : regx.test(_val)) && {
|
|
40
|
+
message: `"${_val}" should match a regex ${condition}`,
|
|
41
|
+
location: utils_1.isString(value) ? baseLocation : baseLocation.key(),
|
|
42
|
+
})
|
|
43
|
+
.filter(utils_1.isTruthy);
|
|
44
44
|
},
|
|
45
45
|
enum: (value, condition, baseLocation) => {
|
|
46
46
|
if (typeof value === 'undefined')
|
|
47
|
-
return
|
|
47
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
48
48
|
const values = utils_1.isString(value) ? [value] : value;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return { isValid: true };
|
|
49
|
+
return values
|
|
50
|
+
.map((_val) => !condition.includes(_val) && {
|
|
51
|
+
message: `"${_val}" should be one of the predefined values`,
|
|
52
|
+
location: utils_1.isString(value) ? baseLocation : baseLocation.child(_val).key(),
|
|
53
|
+
})
|
|
54
|
+
.filter(utils_1.isTruthy);
|
|
58
55
|
},
|
|
59
56
|
defined: (value, condition = true, baseLocation) => {
|
|
60
57
|
const isDefined = typeof value !== 'undefined';
|
|
61
|
-
|
|
58
|
+
const isValid = condition ? isDefined : !isDefined;
|
|
59
|
+
return isValid
|
|
60
|
+
? []
|
|
61
|
+
: [
|
|
62
|
+
{
|
|
63
|
+
message: condition ? `Should be defined` : 'Should be not defined',
|
|
64
|
+
location: baseLocation,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
62
67
|
},
|
|
63
68
|
required: (value, keys, baseLocation) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
69
|
+
return keys
|
|
70
|
+
.map((requiredKey) => !value.includes(requiredKey) && {
|
|
71
|
+
message: `${requiredKey} is required`,
|
|
72
|
+
location: baseLocation.key(),
|
|
73
|
+
})
|
|
74
|
+
.filter(utils_1.isTruthy);
|
|
70
75
|
},
|
|
71
76
|
disallowed: (value, condition, baseLocation) => {
|
|
72
77
|
if (typeof value === 'undefined')
|
|
73
|
-
return
|
|
78
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
74
79
|
const values = utils_1.isString(value) ? [value] : value;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return { isValid: true };
|
|
80
|
+
return values
|
|
81
|
+
.map((_val) => condition.includes(_val) && {
|
|
82
|
+
message: `"${_val}" is disallowed`,
|
|
83
|
+
location: utils_1.isString(value) ? baseLocation : baseLocation.child(_val).key(),
|
|
84
|
+
})
|
|
85
|
+
.filter(utils_1.isTruthy);
|
|
84
86
|
},
|
|
85
87
|
undefined: (value, condition = true, baseLocation) => {
|
|
86
88
|
const isUndefined = typeof value === 'undefined';
|
|
87
|
-
|
|
89
|
+
const isValid = condition ? isUndefined : !isUndefined;
|
|
90
|
+
return isValid
|
|
91
|
+
? []
|
|
92
|
+
: [
|
|
93
|
+
{
|
|
94
|
+
message: condition ? `Should not be defined` : 'Should be defined',
|
|
95
|
+
location: baseLocation,
|
|
96
|
+
},
|
|
97
|
+
];
|
|
88
98
|
},
|
|
89
99
|
nonEmpty: (value, condition = true, baseLocation) => {
|
|
90
100
|
const isEmpty = typeof value === 'undefined' || value === null || value === '';
|
|
91
|
-
|
|
101
|
+
const isValid = condition ? !isEmpty : isEmpty;
|
|
102
|
+
return isValid
|
|
103
|
+
? []
|
|
104
|
+
: [
|
|
105
|
+
{
|
|
106
|
+
message: condition ? `Should not be empty` : 'Should be empty',
|
|
107
|
+
location: baseLocation,
|
|
108
|
+
},
|
|
109
|
+
];
|
|
92
110
|
},
|
|
93
111
|
minLength: (value, condition, baseLocation) => {
|
|
94
|
-
if (typeof value === 'undefined')
|
|
95
|
-
return
|
|
96
|
-
return {
|
|
112
|
+
if (typeof value === 'undefined' || value.length >= condition)
|
|
113
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
114
|
+
return [{ message: `Should have at least ${condition} characters`, location: baseLocation }];
|
|
97
115
|
},
|
|
98
116
|
maxLength: (value, condition, baseLocation) => {
|
|
99
|
-
if (typeof value === 'undefined')
|
|
100
|
-
return
|
|
101
|
-
return {
|
|
117
|
+
if (typeof value === 'undefined' || value.length <= condition)
|
|
118
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
119
|
+
return [{ message: `Should have at most ${condition} characters`, location: baseLocation }];
|
|
102
120
|
},
|
|
103
121
|
casing: (value, condition, baseLocation) => {
|
|
104
122
|
if (typeof value === 'undefined')
|
|
105
|
-
return
|
|
123
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
106
124
|
const values = utils_1.isString(value) ? [value] : value;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
case 'MACRO_CASE':
|
|
123
|
-
matchCase = !!_val.match(/^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g);
|
|
124
|
-
break;
|
|
125
|
-
case 'COBOL-CASE':
|
|
126
|
-
matchCase = !!_val.match(/^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g);
|
|
127
|
-
break;
|
|
128
|
-
case 'flatcase':
|
|
129
|
-
matchCase = !!_val.match(/^[a-z][a-z0-9]+$/g);
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
if (!matchCase) {
|
|
133
|
-
return {
|
|
134
|
-
isValid: false,
|
|
135
|
-
location: utils_1.isString(value) ? baseLocation : baseLocation.child(_val).key(),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return { isValid: true };
|
|
125
|
+
const casingRegexes = {
|
|
126
|
+
camelCase: /^[a-z][a-zA-Z0-9]+$/g,
|
|
127
|
+
'kebab-case': /^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/g,
|
|
128
|
+
snake_case: /^([a-z][a-z0-9]*)(_[a-z0-9]+)*$/g,
|
|
129
|
+
PascalCase: /^[A-Z][a-zA-Z0-9]+$/g,
|
|
130
|
+
MACRO_CASE: /^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g,
|
|
131
|
+
'COBOL-CASE': /^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g,
|
|
132
|
+
flatcase: /^[a-z][a-z0-9]+$/g,
|
|
133
|
+
};
|
|
134
|
+
return values
|
|
135
|
+
.map((_val) => !_val.match(casingRegexes[condition]) && {
|
|
136
|
+
message: `"${_val}" should use ${condition}`,
|
|
137
|
+
location: utils_1.isString(value) ? baseLocation : baseLocation.child(_val).key(),
|
|
138
|
+
})
|
|
139
|
+
.filter(utils_1.isTruthy);
|
|
140
140
|
},
|
|
141
141
|
sortOrder: (value, condition, baseLocation) => {
|
|
142
|
-
if (typeof value === 'undefined')
|
|
143
|
-
return
|
|
144
|
-
|
|
142
|
+
if (typeof value === 'undefined' || utils_2.isOrdered(value, condition))
|
|
143
|
+
return [];
|
|
144
|
+
const direction = condition.direction || condition;
|
|
145
|
+
const property = condition.property;
|
|
146
|
+
return [
|
|
147
|
+
{
|
|
148
|
+
message: `Should be sorted in ${direction === 'asc' ? 'an ascending' : 'a descending'} order${property ? ` by property ${property}` : ''}`,
|
|
149
|
+
location: baseLocation,
|
|
150
|
+
},
|
|
151
|
+
];
|
|
145
152
|
},
|
|
146
153
|
mutuallyExclusive: (value, condition, baseLocation) => {
|
|
147
|
-
|
|
154
|
+
if (utils_2.getIntersectionLength(value, condition) < 2)
|
|
155
|
+
return [];
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
message: `${condition.join(', ')} keys should be mutually exclusive`,
|
|
159
|
+
location: baseLocation.key(),
|
|
160
|
+
},
|
|
161
|
+
];
|
|
148
162
|
},
|
|
149
163
|
mutuallyRequired: (value, condition, baseLocation) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
const isValid = utils_2.getIntersectionLength(value, condition) > 0
|
|
165
|
+
? utils_2.getIntersectionLength(value, condition) === condition.length
|
|
166
|
+
: true;
|
|
167
|
+
return isValid
|
|
168
|
+
? []
|
|
169
|
+
: [
|
|
170
|
+
{
|
|
171
|
+
message: `Properties ${condition.join(', ')} are mutually required`,
|
|
172
|
+
location: baseLocation.key(),
|
|
173
|
+
},
|
|
174
|
+
];
|
|
156
175
|
},
|
|
157
176
|
requireAny: (value, condition, baseLocation) => {
|
|
158
|
-
return
|
|
177
|
+
return utils_2.getIntersectionLength(value, condition) >= 1
|
|
178
|
+
? []
|
|
179
|
+
: [
|
|
180
|
+
{
|
|
181
|
+
message: `Should have any of ${condition.join(', ')}`,
|
|
182
|
+
location: baseLocation.key(),
|
|
183
|
+
},
|
|
184
|
+
];
|
|
159
185
|
},
|
|
160
186
|
ref: (_value, condition, baseLocation, rawValue) => {
|
|
161
187
|
if (typeof rawValue === 'undefined')
|
|
162
|
-
return
|
|
188
|
+
return []; // property doesn't exist, no need to lint it with this assert
|
|
163
189
|
const hasRef = rawValue.hasOwnProperty('$ref');
|
|
164
190
|
if (typeof condition === 'boolean') {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
191
|
+
const isValid = condition ? hasRef : !hasRef;
|
|
192
|
+
return isValid
|
|
193
|
+
? []
|
|
194
|
+
: [
|
|
195
|
+
{
|
|
196
|
+
message: condition ? `should use $ref` : 'should not use $ref',
|
|
197
|
+
location: hasRef ? baseLocation : baseLocation.key(),
|
|
198
|
+
},
|
|
199
|
+
];
|
|
169
200
|
}
|
|
170
201
|
const regex = utils_2.regexFromString(condition);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
202
|
+
const isValid = hasRef && (regex === null || regex === void 0 ? void 0 : regex.test(rawValue['$ref']));
|
|
203
|
+
return isValid
|
|
204
|
+
? []
|
|
205
|
+
: [
|
|
206
|
+
{
|
|
207
|
+
message: `$ref value should match ${condition}`,
|
|
208
|
+
location: hasRef ? baseLocation : baseLocation.key(),
|
|
209
|
+
},
|
|
210
|
+
];
|
|
175
211
|
},
|
|
176
212
|
};
|
|
213
|
+
function buildAssertCustomFunction(fn) {
|
|
214
|
+
return (value, options, baseLocation) => fn.call(null, value, options, baseLocation);
|
|
215
|
+
}
|
|
216
|
+
exports.buildAssertCustomFunction = buildAssertCustomFunction;
|
|
@@ -7,7 +7,7 @@ const Assertions = (opts) => {
|
|
|
7
7
|
const visitors = [];
|
|
8
8
|
// As 'Assertions' has an array of asserts,
|
|
9
9
|
// that array spreads into an 'opts' object on init rules phase here
|
|
10
|
-
// https://github.com/Redocly/redocly-cli/blob/
|
|
10
|
+
// https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/config/config.ts#L311
|
|
11
11
|
// that is why we need to iterate through 'opts' values;
|
|
12
12
|
// before - filter only object 'opts' values
|
|
13
13
|
const assertions = Object.values(opts).filter((opt) => typeof opt === 'object' && opt !== null);
|
|
@@ -23,12 +23,8 @@ const Assertions = (opts) => {
|
|
|
23
23
|
.filter((assertName) => assertion[assertName] !== undefined)
|
|
24
24
|
.map((assertName) => {
|
|
25
25
|
return {
|
|
26
|
-
assertId,
|
|
27
26
|
name: assertName,
|
|
28
27
|
conditions: assertion[assertName],
|
|
29
|
-
message: assertion.message,
|
|
30
|
-
severity: assertion.severity || 'error',
|
|
31
|
-
suggest: assertion.suggest || [],
|
|
32
28
|
runsOnKeys: asserts_1.runOnKeysSet.has(assertName),
|
|
33
29
|
runsOnValues: asserts_1.runOnValuesSet.has(assertName),
|
|
34
30
|
};
|
|
@@ -42,7 +38,7 @@ const Assertions = (opts) => {
|
|
|
42
38
|
throw new Error(`${shouldRunOnKeys.name} can't be used on a single property. Please use 'property'.`);
|
|
43
39
|
}
|
|
44
40
|
for (const subject of subjects) {
|
|
45
|
-
const subjectVisitor = utils_1.buildSubjectVisitor(assertion
|
|
41
|
+
const subjectVisitor = utils_1.buildSubjectVisitor(assertId, assertion, assertsToApply);
|
|
46
42
|
const visitorObject = utils_1.buildVisitorObject(subject, assertion.context, subjectVisitor);
|
|
47
43
|
visitors.push(visitorObject);
|
|
48
44
|
}
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RuleSeverity } from '../../../config';
|
|
2
|
+
import { UserContext } from '../../../walk';
|
|
2
3
|
export declare type OrderDirection = 'asc' | 'desc';
|
|
3
4
|
export declare type OrderOptions = {
|
|
4
5
|
direction: OrderDirection;
|
|
5
6
|
property: string;
|
|
6
7
|
};
|
|
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
|
+
};
|
|
7
16
|
export declare type AssertToApply = {
|
|
8
17
|
name: string;
|
|
9
|
-
assertId?: string;
|
|
10
18
|
conditions: any;
|
|
11
|
-
message?: string;
|
|
12
|
-
severity?: ProblemSeverity;
|
|
13
|
-
suggest?: string[];
|
|
14
19
|
runsOnKeys: boolean;
|
|
15
20
|
runsOnValues: boolean;
|
|
16
21
|
};
|
|
17
22
|
export declare function buildVisitorObject(subject: string, context: Record<string, any>[], subjectVisitor: any): Record<string, any>;
|
|
18
|
-
export declare function buildSubjectVisitor(
|
|
23
|
+
export declare function buildSubjectVisitor(assertId: string, assertion: Assertion, asserts: AssertToApply[]): (node: any, { report, location, rawLocation, key, type, resolve, rawNode }: UserContext) => void;
|
|
19
24
|
export declare function getIntersectionLength(keys: string[], properties: string[]): number;
|
|
20
25
|
export declare function isOrdered(value: any[], options: OrderOptions | OrderDirection): boolean;
|
|
21
26
|
export declare function regexFromString(input: string): RegExp | null;
|
|
27
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.regexFromString = exports.isOrdered = exports.getIntersectionLength = exports.buildSubjectVisitor = exports.buildVisitorObject = void 0;
|
|
4
|
+
const logger_1 = require("../../../logger");
|
|
4
5
|
const ref_utils_1 = require("../../../ref-utils");
|
|
5
6
|
const asserts_1 = require("./asserts");
|
|
6
7
|
function buildVisitorObject(subject, context, subjectVisitor) {
|
|
@@ -44,14 +45,15 @@ function buildVisitorObject(subject, context, subjectVisitor) {
|
|
|
44
45
|
return visitor;
|
|
45
46
|
}
|
|
46
47
|
exports.buildVisitorObject = buildVisitorObject;
|
|
47
|
-
function buildSubjectVisitor(
|
|
48
|
+
function buildSubjectVisitor(assertId, assertion, asserts) {
|
|
48
49
|
return (node, { report, location, rawLocation, key, type, resolve, rawNode }) => {
|
|
49
50
|
var _a;
|
|
51
|
+
let properties = assertion.property;
|
|
50
52
|
// We need to check context's last node if it has the same type as subject node;
|
|
51
53
|
// if yes - that means we didn't create context's last node visitor,
|
|
52
54
|
// so we need to handle 'matchParentKeys' and 'excludeParentKeys' conditions here;
|
|
53
|
-
if (context) {
|
|
54
|
-
const lastContextNode = context[context.length - 1];
|
|
55
|
+
if (assertion.context) {
|
|
56
|
+
const lastContextNode = assertion.context[assertion.context.length - 1];
|
|
55
57
|
if (lastContextNode.type === type.name) {
|
|
56
58
|
const matchParentKeys = lastContextNode.matchParentKeys;
|
|
57
59
|
const excludeParentKeys = lastContextNode.excludeParentKeys;
|
|
@@ -66,35 +68,55 @@ function buildSubjectVisitor(properties, asserts, context) {
|
|
|
66
68
|
if (properties) {
|
|
67
69
|
properties = Array.isArray(properties) ? properties : [properties];
|
|
68
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 = [];
|
|
69
73
|
for (const assert of asserts) {
|
|
70
74
|
const currentLocation = assert.name === 'ref' ? rawLocation : location;
|
|
71
75
|
if (properties) {
|
|
72
76
|
for (const property of properties) {
|
|
73
77
|
// we can have resolvable scalar so need to resolve value here.
|
|
74
78
|
const value = ref_utils_1.isRef(node[property]) ? (_a = resolve(node[property])) === null || _a === void 0 ? void 0 : _a.node : node[property];
|
|
75
|
-
runAssertion({
|
|
79
|
+
assertResults.push(runAssertion({
|
|
76
80
|
values: value,
|
|
77
81
|
rawValues: rawNode[property],
|
|
78
82
|
assert,
|
|
79
83
|
location: currentLocation.child(property),
|
|
80
|
-
|
|
81
|
-
});
|
|
84
|
+
}));
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
else {
|
|
85
88
|
const value = assert.name === 'ref' ? rawNode : Object.keys(node);
|
|
86
|
-
runAssertion({
|
|
89
|
+
assertResults.push(runAssertion({
|
|
87
90
|
values: Object.keys(node),
|
|
88
91
|
rawValues: value,
|
|
89
92
|
assert,
|
|
90
93
|
location: currentLocation,
|
|
91
|
-
|
|
92
|
-
});
|
|
94
|
+
}));
|
|
93
95
|
}
|
|
94
96
|
}
|
|
97
|
+
const problems = assertResults.flat();
|
|
98
|
+
if (problems.length) {
|
|
99
|
+
const message = assertion.message || defaultMessage;
|
|
100
|
+
report({
|
|
101
|
+
message: message.replace('{{problems}}', getProblemsMessage(problems)),
|
|
102
|
+
location: getProblemsLocation(problems) || location,
|
|
103
|
+
forceSeverity: assertion.severity || 'error',
|
|
104
|
+
suggest: assertion.suggest || [],
|
|
105
|
+
ruleId: assertId,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
95
108
|
};
|
|
96
109
|
}
|
|
97
110
|
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
|
+
}
|
|
98
120
|
function getIntersectionLength(keys, properties) {
|
|
99
121
|
const props = new Set(properties);
|
|
100
122
|
let count = 0;
|
|
@@ -127,17 +149,8 @@ function isOrdered(value, options) {
|
|
|
127
149
|
return true;
|
|
128
150
|
}
|
|
129
151
|
exports.isOrdered = isOrdered;
|
|
130
|
-
function runAssertion({ values, rawValues, assert, location
|
|
131
|
-
|
|
132
|
-
if (!lintResult.isValid) {
|
|
133
|
-
report({
|
|
134
|
-
message: assert.message || `The ${assert.assertId} doesn't meet required conditions`,
|
|
135
|
-
location: lintResult.location || location,
|
|
136
|
-
forceSeverity: assert.severity,
|
|
137
|
-
suggest: assert.suggest,
|
|
138
|
-
ruleId: assert.assertId,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
152
|
+
function runAssertion({ values, rawValues, assert, location }) {
|
|
153
|
+
return asserts_1.asserts[assert.name](values, assert.conditions, location, rawValues);
|
|
141
154
|
}
|
|
142
155
|
function regexFromString(input) {
|
|
143
156
|
const matches = input.match(/^\/(.*)\/(.*)|(.*)/);
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NoAmbiguousPaths = void 0;
|
|
4
4
|
const NoAmbiguousPaths = () => {
|
|
5
5
|
return {
|
|
6
|
-
|
|
6
|
+
PathsMap(pathMap, { report, location }) {
|
|
7
7
|
const seenPaths = [];
|
|
8
8
|
for (const currentPath of Object.keys(pathMap)) {
|
|
9
9
|
const ambiguousPath = seenPaths.find((seenPath) => arePathsAmbiguous(seenPath, currentPath));
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.NoIdenticalPaths = void 0;
|
|
4
4
|
const NoIdenticalPaths = () => {
|
|
5
5
|
return {
|
|
6
|
-
|
|
6
|
+
PathsMap(pathMap, { report, location }) {
|
|
7
7
|
const pathsMap = new Map();
|
|
8
8
|
for (const pathName of Object.keys(pathMap)) {
|
|
9
9
|
const id = pathName.replace(/{.+?}/g, '{VARIABLE}');
|
|
@@ -7,7 +7,7 @@ const Operation2xxResponse = () => {
|
|
|
7
7
|
const codes = Object.keys(responses);
|
|
8
8
|
if (!codes.some((code) => code === 'default' || /2[Xx0-9]{2}/.test(code))) {
|
|
9
9
|
report({
|
|
10
|
-
message: 'Operation must have at least one `
|
|
10
|
+
message: 'Operation must have at least one `2XX` response.',
|
|
11
11
|
location: { reportOnKey: true },
|
|
12
12
|
});
|
|
13
13
|
}
|
|
@@ -7,7 +7,7 @@ const Operation4xxResponse = () => {
|
|
|
7
7
|
const codes = Object.keys(responses);
|
|
8
8
|
if (!codes.some((code) => /4[Xx0-9]{2}/.test(code))) {
|
|
9
9
|
report({
|
|
10
|
-
message: 'Operation must have at least one `
|
|
10
|
+
message: 'Operation must have at least one `4XX` response.',
|
|
11
11
|
location: { reportOnKey: true },
|
|
12
12
|
});
|
|
13
13
|
}
|
|
@@ -4,7 +4,7 @@ exports.OperationOperationId = void 0;
|
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const OperationOperationId = () => {
|
|
6
6
|
return {
|
|
7
|
-
|
|
7
|
+
Root: {
|
|
8
8
|
PathItem: {
|
|
9
9
|
Operation(operation, ctx) {
|
|
10
10
|
utils_1.validateDefinedAndNonEmpty('operationId', operation, ctx);
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PathNotIncludeQuery = void 0;
|
|
4
4
|
const PathNotIncludeQuery = () => {
|
|
5
5
|
return {
|
|
6
|
-
|
|
6
|
+
PathsMap: {
|
|
7
7
|
PathItem(_operation, { report, key }) {
|
|
8
8
|
if (key.toString().includes('?')) {
|
|
9
9
|
report({
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
3
|
+
exports.SecurityDefined = void 0;
|
|
4
|
+
const SecurityDefined = () => {
|
|
5
5
|
const referencedSchemes = new Map();
|
|
6
|
+
let eachOperationHasSecurity = true;
|
|
6
7
|
return {
|
|
7
8
|
DefinitionRoot: {
|
|
8
|
-
leave(
|
|
9
|
+
leave(root, { report }) {
|
|
9
10
|
for (const [name, scheme] of referencedSchemes.entries()) {
|
|
10
11
|
if (scheme.defined)
|
|
11
12
|
continue;
|
|
@@ -16,6 +17,14 @@ const OperationSecurityDefined = () => {
|
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
if (root.security || eachOperationHasSecurity) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
report({
|
|
25
|
+
message: `Every API should have security defined on the root level or for each operation.`,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
19
28
|
},
|
|
20
29
|
},
|
|
21
30
|
SecurityScheme(_securityScheme, { key }) {
|
|
@@ -33,6 +42,11 @@ const OperationSecurityDefined = () => {
|
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
44
|
},
|
|
45
|
+
Operation(operation) {
|
|
46
|
+
if (!(operation === null || operation === void 0 ? void 0 : operation.security)) {
|
|
47
|
+
eachOperationHasSecurity = false;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
36
50
|
};
|
|
37
51
|
};
|
|
38
|
-
exports.
|
|
52
|
+
exports.SecurityDefined = SecurityDefined;
|