@redocly/openapi-core 1.0.0-beta.110 → 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 +42 -34
- 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/common/no-ambiguous-paths.js +1 -1
- package/lib/rules/common/no-identical-paths.js +4 -4
- package/lib/rules/common/operation-2xx-response.js +2 -2
- package/lib/rules/common/operation-4xx-response.js +2 -2
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/path-params-defined.js +7 -2
- package/lib/rules/common/response-contains-header.js +2 -2
- package/lib/rules/common/security-defined.js +10 -5
- package/lib/rules/common/spec.js +14 -12
- 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/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/other/stats.d.ts +1 -1
- package/lib/rules/other/stats.js +1 -1
- package/lib/rules/utils.d.ts +1 -0
- package/lib/rules/utils.js +17 -1
- package/lib/types/oas2.js +6 -6
- package/lib/types/oas3.js +11 -11
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +58 -31
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +19 -1
- package/lib/visitors.d.ts +9 -7
- package/lib/visitors.js +12 -3
- package/lib/walk.js +7 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
- package/src/__tests__/lint.test.ts +24 -5
- package/src/__tests__/utils.test.ts +11 -0
- package/src/__tests__/walk.test.ts +2 -2
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +1 -3
- package/src/config/__tests__/config-resolvers.test.ts +30 -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 +44 -20
- 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/__tests__/operation-2xx-response.test.ts +37 -0
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +37 -0
- package/src/rules/common/__tests__/path-params-defined.test.ts +69 -0
- package/src/rules/common/__tests__/security-defined.test.ts +6 -6
- package/src/rules/common/__tests__/spec.test.ts +125 -0
- 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 +44 -18
- 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/common/no-ambiguous-paths.ts +1 -1
- package/src/rules/common/no-identical-paths.ts +4 -4
- package/src/rules/common/operation-2xx-response.ts +2 -2
- package/src/rules/common/operation-4xx-response.ts +2 -2
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/path-params-defined.ts +9 -2
- package/src/rules/common/response-contains-header.ts +6 -1
- package/src/rules/common/security-defined.ts +10 -5
- package/src/rules/common/spec.ts +15 -11
- package/src/rules/oas2/index.ts +0 -2
- package/src/rules/oas3/__tests__/response-contains-header.test.ts +116 -0
- package/src/rules/oas3/index.ts +0 -2
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/other/stats.ts +1 -1
- package/src/rules/utils.ts +22 -0
- package/src/types/oas2.ts +6 -6
- package/src/types/oas3.ts +11 -11
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +58 -33
- package/src/utils.ts +18 -0
- package/src/visitors.ts +32 -11
- 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/types/oas3.js
CHANGED
|
@@ -12,7 +12,7 @@ const Root = {
|
|
|
12
12
|
security: _1.listOf('SecurityRequirement'),
|
|
13
13
|
tags: _1.listOf('Tag'),
|
|
14
14
|
externalDocs: 'ExternalDocs',
|
|
15
|
-
paths: '
|
|
15
|
+
paths: 'Paths',
|
|
16
16
|
components: 'Components',
|
|
17
17
|
'x-webhooks': 'WebhooksMap',
|
|
18
18
|
},
|
|
@@ -81,7 +81,7 @@ const License = {
|
|
|
81
81
|
},
|
|
82
82
|
required: ['name'],
|
|
83
83
|
};
|
|
84
|
-
const
|
|
84
|
+
const Paths = {
|
|
85
85
|
properties: {},
|
|
86
86
|
additionalProperties: (_value, key) => key.startsWith('/') ? 'PathItem' : undefined,
|
|
87
87
|
};
|
|
@@ -141,7 +141,7 @@ const Operation = {
|
|
|
141
141
|
security: _1.listOf('SecurityRequirement'),
|
|
142
142
|
servers: _1.listOf('Server'),
|
|
143
143
|
requestBody: 'RequestBody',
|
|
144
|
-
responses: '
|
|
144
|
+
responses: 'Responses',
|
|
145
145
|
deprecated: { type: 'boolean' },
|
|
146
146
|
callbacks: 'CallbacksMap',
|
|
147
147
|
'x-codeSamples': _1.listOf('XCodeSample'),
|
|
@@ -174,7 +174,7 @@ const MediaType = {
|
|
|
174
174
|
schema: 'Schema',
|
|
175
175
|
example: { isExample: true },
|
|
176
176
|
examples: 'ExamplesMap',
|
|
177
|
-
encoding: '
|
|
177
|
+
encoding: 'EncodingMap',
|
|
178
178
|
},
|
|
179
179
|
};
|
|
180
180
|
const Example = {
|
|
@@ -214,7 +214,7 @@ const Header = {
|
|
|
214
214
|
},
|
|
215
215
|
requiredOneOf: ['schema', 'content'],
|
|
216
216
|
};
|
|
217
|
-
const
|
|
217
|
+
const Responses = {
|
|
218
218
|
properties: { default: 'Response' },
|
|
219
219
|
additionalProperties: (_v, key) => responseCodeRegexp.test(key) ? 'Response' : undefined,
|
|
220
220
|
};
|
|
@@ -378,7 +378,7 @@ const AuthorizationCode = {
|
|
|
378
378
|
},
|
|
379
379
|
required: ['authorizationUrl', 'tokenUrl', 'scopes'],
|
|
380
380
|
};
|
|
381
|
-
const
|
|
381
|
+
const OAuth2Flows = {
|
|
382
382
|
properties: {
|
|
383
383
|
implicit: 'ImplicitFlow',
|
|
384
384
|
password: 'PasswordFlow',
|
|
@@ -394,7 +394,7 @@ const SecurityScheme = {
|
|
|
394
394
|
in: { type: 'string', enum: ['query', 'header', 'cookie'] },
|
|
395
395
|
scheme: { type: 'string' },
|
|
396
396
|
bearerFormat: { type: 'string' },
|
|
397
|
-
flows: '
|
|
397
|
+
flows: 'OAuth2Flows',
|
|
398
398
|
openIdConnectUrl: { type: 'string' },
|
|
399
399
|
},
|
|
400
400
|
required(value) {
|
|
@@ -438,7 +438,7 @@ exports.Oas3Types = {
|
|
|
438
438
|
Info,
|
|
439
439
|
Contact,
|
|
440
440
|
License,
|
|
441
|
-
|
|
441
|
+
Paths,
|
|
442
442
|
PathItem,
|
|
443
443
|
Parameter,
|
|
444
444
|
Operation,
|
|
@@ -450,10 +450,10 @@ exports.Oas3Types = {
|
|
|
450
450
|
Example,
|
|
451
451
|
ExamplesMap: _1.mapOf('Example'),
|
|
452
452
|
Encoding,
|
|
453
|
-
|
|
453
|
+
EncodingMap: _1.mapOf('Encoding'),
|
|
454
454
|
Header,
|
|
455
455
|
HeadersMap: _1.mapOf('Header'),
|
|
456
|
-
|
|
456
|
+
Responses,
|
|
457
457
|
Response,
|
|
458
458
|
Link,
|
|
459
459
|
Schema,
|
|
@@ -476,7 +476,7 @@ exports.Oas3Types = {
|
|
|
476
476
|
PasswordFlow,
|
|
477
477
|
ClientCredentials,
|
|
478
478
|
AuthorizationCode,
|
|
479
|
-
|
|
479
|
+
OAuth2Flows,
|
|
480
480
|
SecurityScheme,
|
|
481
481
|
XCodeSample,
|
|
482
482
|
WebhooksMap,
|
package/lib/types/oas3_1.js
CHANGED
|
@@ -11,7 +11,7 @@ const Root = {
|
|
|
11
11
|
security: _1.listOf('SecurityRequirement'),
|
|
12
12
|
tags: _1.listOf('Tag'),
|
|
13
13
|
externalDocs: 'ExternalDocs',
|
|
14
|
-
paths: '
|
|
14
|
+
paths: 'Paths',
|
|
15
15
|
webhooks: 'WebhooksMap',
|
|
16
16
|
components: 'Components',
|
|
17
17
|
jsonSchemaDialect: { type: 'string' },
|
|
@@ -67,7 +67,7 @@ const Operation = {
|
|
|
67
67
|
security: _1.listOf('SecurityRequirement'),
|
|
68
68
|
servers: _1.listOf('Server'),
|
|
69
69
|
requestBody: 'RequestBody',
|
|
70
|
-
responses: '
|
|
70
|
+
responses: 'Responses',
|
|
71
71
|
deprecated: { type: 'boolean' },
|
|
72
72
|
callbacks: _1.mapOf('Callback'),
|
|
73
73
|
'x-codeSamples': _1.listOf('XCodeSample'),
|
|
@@ -175,7 +175,7 @@ const SecurityScheme = {
|
|
|
175
175
|
in: { type: 'string', enum: ['query', 'header', 'cookie'] },
|
|
176
176
|
scheme: { type: 'string' },
|
|
177
177
|
bearerFormat: { type: 'string' },
|
|
178
|
-
flows: '
|
|
178
|
+
flows: 'OAuth2Flows',
|
|
179
179
|
openIdConnectUrl: { type: 'string' },
|
|
180
180
|
},
|
|
181
181
|
required(value) {
|
|
@@ -5,7 +5,6 @@ const _1 = require(".");
|
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const builtInRulesList = [
|
|
7
7
|
'spec',
|
|
8
|
-
'info-description',
|
|
9
8
|
'info-contact',
|
|
10
9
|
'info-license',
|
|
11
10
|
'info-license-url',
|
|
@@ -68,7 +67,7 @@ const nodeTypesList = [
|
|
|
68
67
|
'Info',
|
|
69
68
|
'Contact',
|
|
70
69
|
'License',
|
|
71
|
-
'
|
|
70
|
+
'Paths',
|
|
72
71
|
'PathItem',
|
|
73
72
|
'Parameter',
|
|
74
73
|
'Operation',
|
|
@@ -80,10 +79,10 @@ const nodeTypesList = [
|
|
|
80
79
|
'Example',
|
|
81
80
|
'ExamplesMap',
|
|
82
81
|
'Encoding',
|
|
83
|
-
'
|
|
82
|
+
'EncodingMap',
|
|
84
83
|
'Header',
|
|
85
84
|
'HeadersMap',
|
|
86
|
-
'
|
|
85
|
+
'Responses',
|
|
87
86
|
'Response',
|
|
88
87
|
'Link',
|
|
89
88
|
'LinksMap',
|
|
@@ -106,7 +105,7 @@ const nodeTypesList = [
|
|
|
106
105
|
'PasswordFlow',
|
|
107
106
|
'ClientCredentials',
|
|
108
107
|
'AuthorizationCode',
|
|
109
|
-
'
|
|
108
|
+
'OAuth2Flows',
|
|
110
109
|
'SecurityScheme',
|
|
111
110
|
'XCodeSample',
|
|
112
111
|
'WebhooksMap',
|
|
@@ -140,11 +139,7 @@ const RootConfigStyleguide = {
|
|
|
140
139
|
} }, ConfigStyleguide.properties),
|
|
141
140
|
};
|
|
142
141
|
const ConfigRoot = {
|
|
143
|
-
properties: Object.assign(Object.assign({ organization: { type: 'string' }, apis: 'ConfigApis',
|
|
144
|
-
type: 'object',
|
|
145
|
-
properties: {},
|
|
146
|
-
additionalProperties: { properties: { type: 'string' } },
|
|
147
|
-
} }, RootConfigStyleguide.properties), { styleguide: 'RootConfigStyleguide', lint: 'RootConfigStyleguide', 'features.openapi': 'ConfigReferenceDocs', referenceDocs: 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', region: { enum: ['us', 'eu'] }, resolve: {
|
|
142
|
+
properties: Object.assign(Object.assign({ organization: { type: 'string' }, apis: 'ConfigApis' }, RootConfigStyleguide.properties), { 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', region: { enum: ['us', 'eu'] }, resolve: {
|
|
148
143
|
properties: {
|
|
149
144
|
http: 'ConfigHTTP',
|
|
150
145
|
doNotResolveExamples: { type: 'boolean' },
|
|
@@ -209,16 +204,9 @@ const ObjectRule = {
|
|
|
209
204
|
additionalProperties: {},
|
|
210
205
|
required: ['severity'],
|
|
211
206
|
};
|
|
212
|
-
const
|
|
207
|
+
const AssertionDefinitionSubject = {
|
|
213
208
|
properties: {
|
|
214
|
-
|
|
215
|
-
if (Array.isArray(value)) {
|
|
216
|
-
return { type: 'array', items: { enum: nodeTypesList } };
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
return { enum: nodeTypesList };
|
|
220
|
-
}
|
|
221
|
-
},
|
|
209
|
+
type: { enum: nodeTypesList },
|
|
222
210
|
property: (value) => {
|
|
223
211
|
if (Array.isArray(value)) {
|
|
224
212
|
return { type: 'array', items: { type: 'string' } };
|
|
@@ -230,10 +218,14 @@ const Assert = {
|
|
|
230
218
|
return { type: 'string' };
|
|
231
219
|
}
|
|
232
220
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
221
|
+
filterInParentKeys: { type: 'array', items: { type: 'string' } },
|
|
222
|
+
filterOutParentKeys: { type: 'array', items: { type: 'string' } },
|
|
223
|
+
matchParentKeys: { type: 'string' },
|
|
224
|
+
},
|
|
225
|
+
required: ['type'],
|
|
226
|
+
};
|
|
227
|
+
const AssertionDefinitionAssertions = {
|
|
228
|
+
properties: {
|
|
237
229
|
enum: { type: 'array', items: { type: 'string' } },
|
|
238
230
|
pattern: { type: 'string' },
|
|
239
231
|
casing: {
|
|
@@ -253,26 +245,49 @@ const Assert = {
|
|
|
253
245
|
requireAny: { type: 'array', items: { type: 'string' } },
|
|
254
246
|
disallowed: { type: 'array', items: { type: 'string' } },
|
|
255
247
|
defined: { type: 'boolean' },
|
|
256
|
-
undefined: { type: 'boolean' },
|
|
248
|
+
// undefined: { type: 'boolean' }, // TODO: Remove `undefined` assertion from codebase overall
|
|
257
249
|
nonEmpty: { type: 'boolean' },
|
|
258
250
|
minLength: { type: 'integer' },
|
|
259
251
|
maxLength: { type: 'integer' },
|
|
260
252
|
ref: (value) => typeof value === 'string' ? { type: 'string' } : { type: 'boolean' },
|
|
253
|
+
const: (value) => {
|
|
254
|
+
if (typeof value === 'string') {
|
|
255
|
+
return { type: 'string' };
|
|
256
|
+
}
|
|
257
|
+
if (typeof value === 'number') {
|
|
258
|
+
return { type: 'number' };
|
|
259
|
+
}
|
|
260
|
+
if (typeof value === 'boolean') {
|
|
261
|
+
return { type: 'boolean' };
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
},
|
|
261
267
|
},
|
|
262
268
|
additionalProperties: (_value, key) => {
|
|
263
269
|
if (/^\w+\/\w+$/.test(key))
|
|
264
270
|
return { type: 'object' };
|
|
265
271
|
return;
|
|
266
272
|
},
|
|
267
|
-
required: ['subject'],
|
|
268
273
|
};
|
|
269
|
-
const
|
|
274
|
+
const AssertDefinition = {
|
|
270
275
|
properties: {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
excludeParentKeys: { type: 'array', items: { type: 'string' } },
|
|
276
|
+
subject: 'AssertionDefinitionSubject',
|
|
277
|
+
assertions: 'AssertionDefinitionAssertions',
|
|
274
278
|
},
|
|
275
|
-
required: ['
|
|
279
|
+
required: ['subject', 'assertions'],
|
|
280
|
+
};
|
|
281
|
+
const Assert = {
|
|
282
|
+
properties: {
|
|
283
|
+
subject: 'AssertionDefinitionSubject',
|
|
284
|
+
assertions: 'AssertionDefinitionAssertions',
|
|
285
|
+
where: _1.listOf('AssertDefinition'),
|
|
286
|
+
message: { type: 'string' },
|
|
287
|
+
suggest: { type: 'array', items: { type: 'string' } },
|
|
288
|
+
severity: { enum: ['error', 'warn', 'off'] },
|
|
289
|
+
},
|
|
290
|
+
required: ['subject', 'assertions'],
|
|
276
291
|
};
|
|
277
292
|
const ConfigLanguage = {
|
|
278
293
|
properties: {
|
|
@@ -758,6 +773,16 @@ const ConfigReferenceDocs = {
|
|
|
758
773
|
unstable_externalDescription: { type: 'boolean' },
|
|
759
774
|
unstable_ignoreMimeParameters: { type: 'boolean' },
|
|
760
775
|
untrustedDefinition: { type: 'boolean' },
|
|
776
|
+
mockServer: {
|
|
777
|
+
properties: {
|
|
778
|
+
url: { type: 'string' },
|
|
779
|
+
position: { enum: ['first', 'last', 'replace', 'off'] },
|
|
780
|
+
description: { type: 'string' },
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
showAccessMode: { type: 'boolean' },
|
|
784
|
+
preserveOriginalExtensionsName: { type: 'boolean' },
|
|
785
|
+
markdownHeadingsAnchorLevel: { type: 'number' },
|
|
761
786
|
},
|
|
762
787
|
additionalProperties: { type: 'string' },
|
|
763
788
|
};
|
|
@@ -782,7 +807,7 @@ exports.ConfigTypes = {
|
|
|
782
807
|
ConfigSidebarLinks,
|
|
783
808
|
CommonConfigSidebarLinks,
|
|
784
809
|
ConfigTheme,
|
|
785
|
-
|
|
810
|
+
AssertDefinition,
|
|
786
811
|
ThemeColors,
|
|
787
812
|
CommonThemeColors,
|
|
788
813
|
BorderThemeColors,
|
|
@@ -829,4 +854,6 @@ exports.ConfigTypes = {
|
|
|
829
854
|
Sidebar,
|
|
830
855
|
Heading,
|
|
831
856
|
Typography,
|
|
857
|
+
AssertionDefinitionAssertions,
|
|
858
|
+
AssertionDefinitionSubject,
|
|
832
859
|
};
|
package/lib/utils.d.ts
CHANGED
|
@@ -46,3 +46,5 @@ export declare function showErrorForDeprecatedField(deprecatedField: string, upd
|
|
|
46
46
|
export declare type Falsy = undefined | null | false | '' | 0;
|
|
47
47
|
export declare function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy;
|
|
48
48
|
export declare function identity<T>(value: T): T;
|
|
49
|
+
export declare function keysOf<T>(obj: T): (keyof T)[];
|
|
50
|
+
export declare function pickDefined<T extends Record<string, unknown>>(obj?: T): Record<string, unknown> | undefined;
|
package/lib/utils.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
12
|
+
exports.pickDefined = exports.keysOf = exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.isDefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
const path_1 = require("path");
|
|
15
15
|
const minimatch = require("minimatch");
|
|
@@ -205,3 +205,21 @@ function identity(value) {
|
|
|
205
205
|
return value;
|
|
206
206
|
}
|
|
207
207
|
exports.identity = identity;
|
|
208
|
+
function keysOf(obj) {
|
|
209
|
+
if (!obj)
|
|
210
|
+
return [];
|
|
211
|
+
return Object.keys(obj);
|
|
212
|
+
}
|
|
213
|
+
exports.keysOf = keysOf;
|
|
214
|
+
function pickDefined(obj) {
|
|
215
|
+
if (!obj)
|
|
216
|
+
return undefined;
|
|
217
|
+
const res = {};
|
|
218
|
+
for (const key in obj) {
|
|
219
|
+
if (obj[key] !== undefined) {
|
|
220
|
+
res[key] = obj[key];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return res;
|
|
224
|
+
}
|
|
225
|
+
exports.pickDefined = pickDefined;
|
package/lib/visitors.d.ts
CHANGED
|
@@ -4,11 +4,12 @@ import type { NormalizedNodeType } from './types';
|
|
|
4
4
|
import type { Stack } from './utils';
|
|
5
5
|
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
6
6
|
import type { Location } from './ref-utils';
|
|
7
|
+
export declare type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
|
|
7
8
|
export declare type VisitFunction<T> = (node: T, ctx: UserContext & {
|
|
8
9
|
ignoreNextVisitorsOnNode: () => void;
|
|
9
10
|
}, parents?: any, context?: any) => void;
|
|
10
11
|
declare type VisitRefFunction = (node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) => void;
|
|
11
|
-
declare type SkipFunction<T> = (node: T, key: string | number) => boolean;
|
|
12
|
+
declare type SkipFunction<T> = (node: T, key: string | number, ctx: SkipFunctionContext) => boolean;
|
|
12
13
|
declare type VisitObject<T> = {
|
|
13
14
|
enter?: VisitFunction<T>;
|
|
14
15
|
leave?: VisitFunction<T>;
|
|
@@ -70,9 +71,10 @@ declare type Oas3FlatVisitor = {
|
|
|
70
71
|
Info?: VisitFunctionOrObject<Oas3Info>;
|
|
71
72
|
Contact?: VisitFunctionOrObject<Oas3Contact>;
|
|
72
73
|
License?: VisitFunctionOrObject<Oas3License>;
|
|
73
|
-
|
|
74
|
+
Paths?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
|
|
74
75
|
PathItem?: VisitFunctionOrObject<Oas3PathItem>;
|
|
75
|
-
Callback?: VisitFunctionOrObject<
|
|
76
|
+
Callback?: VisitFunctionOrObject<Oas3Callback>;
|
|
77
|
+
CallbacksMap?: VisitFunctionOrObject<Record<string, Oas3Callback>>;
|
|
76
78
|
Parameter?: VisitFunctionOrObject<Oas3Parameter>;
|
|
77
79
|
Operation?: VisitFunctionOrObject<Oas3Operation>;
|
|
78
80
|
RequestBody?: VisitFunctionOrObject<Oas3RequestBody>;
|
|
@@ -81,7 +83,7 @@ declare type Oas3FlatVisitor = {
|
|
|
81
83
|
Example?: VisitFunctionOrObject<Oas3Example>;
|
|
82
84
|
Encoding?: VisitFunctionOrObject<Oas3Encoding>;
|
|
83
85
|
Header?: VisitFunctionOrObject<Oas3Header>;
|
|
84
|
-
|
|
86
|
+
Responses?: VisitFunctionOrObject<Record<string, Oas3Response>>;
|
|
85
87
|
Response?: VisitFunctionOrObject<Oas3Response>;
|
|
86
88
|
Link?: VisitFunctionOrObject<Oas3Link>;
|
|
87
89
|
Schema?: VisitFunctionOrObject<Oas3Schema>;
|
|
@@ -103,7 +105,7 @@ declare type Oas3FlatVisitor = {
|
|
|
103
105
|
PasswordFlow?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['password']>;
|
|
104
106
|
ClientCredentials?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['clientCredentials']>;
|
|
105
107
|
AuthorizationCode?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['authorizationCode']>;
|
|
106
|
-
|
|
108
|
+
OAuth2Flows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
|
|
107
109
|
SecurityScheme?: VisitFunctionOrObject<Oas3SecurityScheme>;
|
|
108
110
|
};
|
|
109
111
|
declare type Oas2FlatVisitor = {
|
|
@@ -114,13 +116,13 @@ declare type Oas2FlatVisitor = {
|
|
|
114
116
|
Info?: VisitFunctionOrObject<Oas2Info>;
|
|
115
117
|
Contact?: VisitFunctionOrObject<Oas2Contact>;
|
|
116
118
|
License?: VisitFunctionOrObject<Oas2License>;
|
|
117
|
-
|
|
119
|
+
Paths?: VisitFunctionOrObject<Record<string, Oas2PathItem>>;
|
|
118
120
|
PathItem?: VisitFunctionOrObject<Oas2PathItem>;
|
|
119
121
|
Parameter?: VisitFunctionOrObject<any>;
|
|
120
122
|
Operation?: VisitFunctionOrObject<Oas2Operation>;
|
|
121
123
|
Examples?: VisitFunctionOrObject<Record<string, any>>;
|
|
122
124
|
Header?: VisitFunctionOrObject<Oas2Header>;
|
|
123
|
-
|
|
125
|
+
Responses?: VisitFunctionOrObject<Record<string, Oas2Response>>;
|
|
124
126
|
Response?: VisitFunctionOrObject<Oas2Response>;
|
|
125
127
|
Schema?: VisitFunctionOrObject<Oas2Schema>;
|
|
126
128
|
Xml?: VisitFunctionOrObject<Oas2Xml>;
|
package/lib/visitors.js
CHANGED
|
@@ -4,13 +4,15 @@ exports.normalizeVisitors = void 0;
|
|
|
4
4
|
const legacyTypesMap = {
|
|
5
5
|
Root: 'DefinitionRoot',
|
|
6
6
|
ServerVariablesMap: 'ServerVariableMap',
|
|
7
|
-
|
|
7
|
+
Paths: ['PathMap', 'PathsMap'],
|
|
8
8
|
CallbacksMap: 'CallbackMap',
|
|
9
9
|
MediaTypesMap: 'MediaTypeMap',
|
|
10
10
|
ExamplesMap: 'ExampleMap',
|
|
11
|
-
|
|
11
|
+
EncodingMap: 'EncodingsMap',
|
|
12
12
|
HeadersMap: 'HeaderMap',
|
|
13
13
|
LinksMap: 'LinkMap',
|
|
14
|
+
OAuth2Flows: 'SecuritySchemeFlows',
|
|
15
|
+
Responses: 'ResponsesMap',
|
|
14
16
|
};
|
|
15
17
|
function normalizeVisitors(visitorsConfig, types) {
|
|
16
18
|
const normalizedVisitors = {};
|
|
@@ -85,6 +87,13 @@ function normalizeVisitors(visitorsConfig, types) {
|
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
90
|
+
function findLegacyVisitorNode(visitor, typeName) {
|
|
91
|
+
if (Array.isArray(typeName)) {
|
|
92
|
+
const name = typeName.find((name) => visitor[name]) || undefined;
|
|
93
|
+
return name && visitor[name];
|
|
94
|
+
}
|
|
95
|
+
return visitor[typeName];
|
|
96
|
+
}
|
|
88
97
|
function normalizeVisitorLevel(ruleConf, visitor, parentContext, depth = 0) {
|
|
89
98
|
const visitorKeys = Object.keys(types);
|
|
90
99
|
if (depth === 0) {
|
|
@@ -101,7 +110,7 @@ function normalizeVisitors(visitorsConfig, types) {
|
|
|
101
110
|
}
|
|
102
111
|
for (const typeName of visitorKeys) {
|
|
103
112
|
const typeVisitor = (visitor[typeName] ||
|
|
104
|
-
visitor
|
|
113
|
+
findLegacyVisitorNode(visitor, legacyTypesMap[typeName]));
|
|
105
114
|
const normalizedTypeVisitor = normalizedVisitors[typeName];
|
|
106
115
|
if (!typeVisitor)
|
|
107
116
|
continue;
|
package/lib/walk.js
CHANGED
|
@@ -112,7 +112,13 @@ function walkDocument(opts) {
|
|
|
112
112
|
location: resolvedLocation,
|
|
113
113
|
nextLevelTypeActivated: null,
|
|
114
114
|
withParentNode: (_g = (_f = context.parent) === null || _f === void 0 ? void 0 : _f.activatedOn) === null || _g === void 0 ? void 0 : _g.value.node,
|
|
115
|
-
skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) ||
|
|
115
|
+
skipped: (_k = (((_j = (_h = context.parent) === null || _h === void 0 ? void 0 : _h.activatedOn) === null || _j === void 0 ? void 0 : _j.value.skipped) ||
|
|
116
|
+
(skip === null || skip === void 0 ? void 0 : skip(resolvedNode, key, {
|
|
117
|
+
location,
|
|
118
|
+
rawLocation,
|
|
119
|
+
resolve,
|
|
120
|
+
rawNode: node,
|
|
121
|
+
})))) !== null && _k !== void 0 ? _k : false,
|
|
116
122
|
};
|
|
117
123
|
context.activatedOn = utils_1.pushStack(context.activatedOn, activatedOn);
|
|
118
124
|
let ctx = context.parent;
|
package/package.json
CHANGED
|
@@ -61,11 +61,13 @@ describe('lint', () => {
|
|
|
61
61
|
path-http-verbs-order: error
|
|
62
62
|
boolean-parameter-prefixes: off
|
|
63
63
|
assert/operation-summary-length:
|
|
64
|
-
subject:
|
|
65
|
-
|
|
64
|
+
subject:
|
|
65
|
+
type: Operation
|
|
66
|
+
property: summary
|
|
66
67
|
message: Operation summary should start with an active verb
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
assertions:
|
|
69
|
+
local/checkWordsCount:
|
|
70
|
+
min: 3
|
|
69
71
|
features.openapi:
|
|
70
72
|
showConsole: true
|
|
71
73
|
layout:
|
|
@@ -247,7 +249,24 @@ describe('lint', () => {
|
|
|
247
249
|
);
|
|
248
250
|
const results = await lintConfig({ document });
|
|
249
251
|
|
|
250
|
-
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
252
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
253
|
+
Array [
|
|
254
|
+
Object {
|
|
255
|
+
"from": undefined,
|
|
256
|
+
"location": Array [
|
|
257
|
+
Object {
|
|
258
|
+
"pointer": "#/referenceDocs",
|
|
259
|
+
"reportOnKey": true,
|
|
260
|
+
"source": "",
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
"message": "Property \`referenceDocs\` is not expected here.",
|
|
264
|
+
"ruleId": "configuration spec",
|
|
265
|
+
"severity": "error",
|
|
266
|
+
"suggest": Array [],
|
|
267
|
+
},
|
|
268
|
+
]
|
|
269
|
+
`);
|
|
251
270
|
});
|
|
252
271
|
|
|
253
272
|
it("'plugins' shouldn't be allowed in 'apis'", async () => {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
slash,
|
|
5
5
|
getMatchingStatusCodeRange,
|
|
6
6
|
doesYamlFileExist,
|
|
7
|
+
pickDefined,
|
|
7
8
|
} from '../utils';
|
|
8
9
|
import { isBrowser } from '../env';
|
|
9
10
|
import * as fs from 'fs';
|
|
@@ -81,6 +82,16 @@ describe('utils', () => {
|
|
|
81
82
|
});
|
|
82
83
|
});
|
|
83
84
|
|
|
85
|
+
describe('pickDefined', () => {
|
|
86
|
+
it('returns undefined for undefined', () => {
|
|
87
|
+
expect(pickDefined(undefined)).toBeUndefined();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('picks only defined values', () => {
|
|
91
|
+
expect(pickDefined({ a: 1, b: undefined, c: 3 })).toStrictEqual({ a: 1, c: 3 });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
84
95
|
describe('getMatchingStatusCodeRange', () => {
|
|
85
96
|
it('should get the generalized form of status codes', () => {
|
|
86
97
|
expect(getMatchingStatusCodeRange('202')).toEqual('2XX');
|
|
@@ -1115,7 +1115,7 @@ describe('walk order', () => {
|
|
|
1115
1115
|
expect(calls).toMatchInlineSnapshot(`
|
|
1116
1116
|
Array [
|
|
1117
1117
|
"enter Root",
|
|
1118
|
-
"enter
|
|
1118
|
+
"enter Paths",
|
|
1119
1119
|
"enter PathItem",
|
|
1120
1120
|
"enter ParameterList",
|
|
1121
1121
|
"enter Parameter",
|
|
@@ -1134,7 +1134,7 @@ describe('walk order', () => {
|
|
|
1134
1134
|
"leave ParameterList",
|
|
1135
1135
|
"leave Operation",
|
|
1136
1136
|
"leave PathItem",
|
|
1137
|
-
"leave
|
|
1137
|
+
"leave Paths",
|
|
1138
1138
|
"enter Components",
|
|
1139
1139
|
"enter NamedParameters",
|
|
1140
1140
|
"leave NamedParameters",
|
|
@@ -39,7 +39,6 @@ Object {
|
|
|
39
39
|
"assertions": "warn",
|
|
40
40
|
"boolean-parameter-prefixes": "error",
|
|
41
41
|
"info-contact": "off",
|
|
42
|
-
"info-description": "warn",
|
|
43
42
|
"info-license": "warn",
|
|
44
43
|
"info-license-url": "warn",
|
|
45
44
|
"local/operation-id-not-test": "error",
|
|
@@ -73,7 +72,7 @@ Object {
|
|
|
73
72
|
}
|
|
74
73
|
`;
|
|
75
74
|
|
|
76
|
-
exports[`resolveStyleguideConfig should resolve extends with local file config
|
|
75
|
+
exports[`resolveStyleguideConfig should resolve extends with local file config which contains path to nested config 1`] = `
|
|
77
76
|
Object {
|
|
78
77
|
"decorators": Object {},
|
|
79
78
|
"doNotResolveExamples": undefined,
|
|
@@ -129,7 +128,6 @@ Object {
|
|
|
129
128
|
],
|
|
130
129
|
"boolean-parameter-prefixes": "error",
|
|
131
130
|
"info-contact": "off",
|
|
132
|
-
"info-description": "warn",
|
|
133
131
|
"info-license": "warn",
|
|
134
132
|
"info-license-url": "warn",
|
|
135
133
|
"local/operation-id-not-test": "error",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { colorize } from '../../logger';
|
|
2
|
-
import { asserts } from '../../rules/common/assertions/asserts';
|
|
2
|
+
import { Asserts, asserts } from '../../rules/common/assertions/asserts';
|
|
3
3
|
import { resolveStyleguideConfig, resolveApis, resolveConfig } from '../config-resolvers';
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
@@ -100,7 +100,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
100
100
|
}).toThrow('Circular dependency in config file');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
it('should resolve extends with local file config
|
|
103
|
+
it('should resolve extends with local file config which contains path to nested config', async () => {
|
|
104
104
|
const styleguideConfig = {
|
|
105
105
|
extends: ['local-config-with-file.yaml'],
|
|
106
106
|
};
|
|
@@ -143,7 +143,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
143
143
|
|
|
144
144
|
expect(plugins).toBeDefined();
|
|
145
145
|
expect(plugins?.length).toBe(2);
|
|
146
|
-
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
146
|
+
expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
it('should throw error when custom assertion load not exist plugin', async () => {
|
|
@@ -163,7 +163,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
166
|
+
expect(asserts['test-plugin/checkWordsCount' as keyof Asserts]).toBeDefined();
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
it('should correctly merge assertions from nested config', async () => {
|
|
@@ -197,7 +197,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
197
197
|
]);
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
-
it('should resolve extends with url file config
|
|
200
|
+
it('should resolve extends with url file config which contains path to nested config', async () => {
|
|
201
201
|
const styleguideConfig = {
|
|
202
202
|
// This points to ./fixtures/resolve-remote-configs/remote-config.yaml
|
|
203
203
|
extends: [
|
|
@@ -464,4 +464,29 @@ describe('resolveConfig', () => {
|
|
|
464
464
|
delete apis['petstore'].styleguide.pluginPaths;
|
|
465
465
|
expect(apis['petstore'].styleguide).toMatchSnapshot();
|
|
466
466
|
});
|
|
467
|
+
|
|
468
|
+
it('should default to the extends from the main config if no extends defined', async () => {
|
|
469
|
+
const rawConfig: RawConfig = {
|
|
470
|
+
apis: {
|
|
471
|
+
petstore: {
|
|
472
|
+
root: 'some/path',
|
|
473
|
+
styleguide: {
|
|
474
|
+
rules: {
|
|
475
|
+
'operation-4xx-response': 'error',
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
styleguide: {
|
|
481
|
+
extends: ['minimal'],
|
|
482
|
+
rules: {
|
|
483
|
+
'operation-2xx-response': 'warn',
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const { apis } = await resolveConfig(rawConfig, configPath);
|
|
489
|
+
expect(apis['petstore'].styleguide.rules).toBeDefined();
|
|
490
|
+
expect(apis['petstore'].styleguide.rules?.['operation-2xx-response']).toEqual('warn'); // from minimal ruleset
|
|
491
|
+
});
|
|
467
492
|
});
|