@redocly/openapi-core 1.0.0-beta.110 → 1.0.0-beta.111

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.
Files changed (57) hide show
  1. package/lib/config/config-resolvers.js +23 -22
  2. package/lib/rules/common/no-ambiguous-paths.js +1 -1
  3. package/lib/rules/common/no-identical-paths.js +4 -4
  4. package/lib/rules/common/operation-2xx-response.js +2 -2
  5. package/lib/rules/common/operation-4xx-response.js +2 -2
  6. package/lib/rules/common/path-not-include-query.js +1 -1
  7. package/lib/rules/common/path-params-defined.js +7 -2
  8. package/lib/rules/common/response-contains-header.js +2 -2
  9. package/lib/rules/common/security-defined.js +10 -5
  10. package/lib/rules/common/spec.js +14 -12
  11. package/lib/rules/oas3/request-mime-type.js +1 -1
  12. package/lib/rules/oas3/response-mime-type.js +1 -1
  13. package/lib/rules/other/stats.d.ts +1 -1
  14. package/lib/rules/other/stats.js +1 -1
  15. package/lib/rules/utils.d.ts +1 -0
  16. package/lib/rules/utils.js +17 -1
  17. package/lib/types/oas2.js +6 -6
  18. package/lib/types/oas3.js +11 -11
  19. package/lib/types/oas3_1.js +3 -3
  20. package/lib/types/redocly-yaml.js +14 -4
  21. package/lib/utils.d.ts +1 -0
  22. package/lib/utils.js +13 -1
  23. package/lib/visitors.d.ts +7 -6
  24. package/lib/visitors.js +11 -3
  25. package/package.json +1 -1
  26. package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
  27. package/src/__tests__/utils.test.ts +11 -0
  28. package/src/__tests__/walk.test.ts +2 -2
  29. package/src/config/__tests__/config-resolvers.test.ts +25 -0
  30. package/src/config/config-resolvers.ts +2 -1
  31. package/src/rules/common/__tests__/operation-2xx-response.test.ts +37 -0
  32. package/src/rules/common/__tests__/operation-4xx-response.test.ts +37 -0
  33. package/src/rules/common/__tests__/path-params-defined.test.ts +69 -0
  34. package/src/rules/common/__tests__/security-defined.test.ts +6 -6
  35. package/src/rules/common/__tests__/spec.test.ts +125 -0
  36. package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
  37. package/src/rules/common/no-ambiguous-paths.ts +1 -1
  38. package/src/rules/common/no-identical-paths.ts +4 -4
  39. package/src/rules/common/operation-2xx-response.ts +2 -2
  40. package/src/rules/common/operation-4xx-response.ts +2 -2
  41. package/src/rules/common/path-not-include-query.ts +1 -1
  42. package/src/rules/common/path-params-defined.ts +9 -2
  43. package/src/rules/common/response-contains-header.ts +6 -1
  44. package/src/rules/common/security-defined.ts +10 -5
  45. package/src/rules/common/spec.ts +15 -11
  46. package/src/rules/oas3/__tests__/response-contains-header.test.ts +116 -0
  47. package/src/rules/oas3/request-mime-type.ts +1 -1
  48. package/src/rules/oas3/response-mime-type.ts +1 -1
  49. package/src/rules/other/stats.ts +1 -1
  50. package/src/rules/utils.ts +22 -0
  51. package/src/types/oas2.ts +6 -6
  52. package/src/types/oas3.ts +11 -11
  53. package/src/types/oas3_1.ts +3 -3
  54. package/src/types/redocly-yaml.ts +14 -4
  55. package/src/utils.ts +13 -0
  56. package/src/visitors.ts +25 -10
  57. package/tsconfig.tsbuildinfo +1 -1
@@ -3,8 +3,8 @@ import { UserContext } from '../../walk';
3
3
 
4
4
  export const Operation4xxResponse: Oas3Rule | Oas2Rule = () => {
5
5
  return {
6
- ResponsesMap(responses: Record<string, object>, { report }: UserContext) {
7
- const codes = Object.keys(responses);
6
+ Responses(responses: Record<string, object>, { report }: UserContext) {
7
+ const codes = Object.keys(responses || {});
8
8
 
9
9
  if (!codes.some((code) => /4[Xx0-9]{2}/.test(code))) {
10
10
  report({
@@ -3,7 +3,7 @@ import { UserContext } from '../../walk';
3
3
 
4
4
  export const PathNotIncludeQuery: Oas3Rule | Oas2Rule = () => {
5
5
  return {
6
- PathsMap: {
6
+ Paths: {
7
7
  PathItem(_operation: object, { report, key }: UserContext) {
8
8
  if (key.toString().includes('?')) {
9
9
  report({
@@ -9,6 +9,7 @@ export const PathParamsDefined: Oas3Rule | Oas2Rule = () => {
9
9
  let pathTemplateParams: Set<string>;
10
10
  let definedPathParams: Set<string>;
11
11
  let currentPath: string;
12
+ let definedOperationParams: Set<string>;
12
13
 
13
14
  return {
14
15
  PathItem: {
@@ -31,9 +32,15 @@ export const PathParamsDefined: Oas3Rule | Oas2Rule = () => {
31
32
  }
32
33
  },
33
34
  Operation: {
35
+ enter() {
36
+ definedOperationParams = new Set();
37
+ },
34
38
  leave(_op: object, { report, location }: UserContext) {
35
39
  for (const templateParam of Array.from(pathTemplateParams.keys())) {
36
- if (!definedPathParams.has(templateParam)) {
40
+ if (
41
+ !definedOperationParams.has(templateParam) &&
42
+ !definedPathParams.has(templateParam)
43
+ ) {
37
44
  report({
38
45
  message: `The operation does not define the path parameter \`{${templateParam}}\` expected by path \`${currentPath}\`.`,
39
46
  location: location.child(['parameters']).key(), // report on operation
@@ -43,7 +50,7 @@ export const PathParamsDefined: Oas3Rule | Oas2Rule = () => {
43
50
  },
44
51
  Parameter(parameter: Oas2Parameter | Oas3Parameter, { report, location }: UserContext) {
45
52
  if (parameter.in === 'path' && parameter.name) {
46
- definedPathParams.add(parameter.name);
53
+ definedOperationParams.add(parameter.name);
47
54
  if (!pathTemplateParams.has(parameter.name)) {
48
55
  report({
49
56
  message: `Path parameter \`${parameter.name}\` is not used in the path \`${currentPath}\`.`,
@@ -16,7 +16,12 @@ export const ResponseContainsHeader: Oas3Rule | Oas2Rule = (options) => {
16
16
  names[getMatchingStatusCodeRange(key).toLowerCase()] ||
17
17
  [];
18
18
  for (const expectedHeader of expectedHeaders) {
19
- if (!response.headers?.[expectedHeader]) {
19
+ if (
20
+ !response?.headers ||
21
+ !Object.keys(response?.headers).some(
22
+ (header) => header.toLowerCase() === expectedHeader.toLowerCase()
23
+ )
24
+ ) {
20
25
  report({
21
26
  message: `Response object must contain a "${expectedHeader}" header.`,
22
27
  location: location.child('headers').key(),
@@ -13,10 +13,11 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
13
13
  }
14
14
  >();
15
15
 
16
+ const operationsWithoutSecurity: Location[] = [];
16
17
  let eachOperationHasSecurity: boolean = true;
17
18
 
18
19
  return {
19
- DefinitionRoot: {
20
+ Root: {
20
21
  leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) {
21
22
  for (const [name, scheme] of referencedSchemes.entries()) {
22
23
  if (scheme.defined) continue;
@@ -31,9 +32,12 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
31
32
  if (root.security || eachOperationHasSecurity) {
32
33
  return;
33
34
  } else {
34
- report({
35
- message: `Every API should have security defined on the root level or for each operation.`,
36
- });
35
+ for (const operationLocation of operationsWithoutSecurity) {
36
+ report({
37
+ message: `Every operation should have security defined on it or on the root level.`,
38
+ location: operationLocation.key(),
39
+ });
40
+ }
37
41
  }
38
42
  },
39
43
  },
@@ -51,9 +55,10 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = () => {
51
55
  }
52
56
  }
53
57
  },
54
- Operation(operation: Oas2Operation | Oas3Operation) {
58
+ Operation(operation: Oas2Operation | Oas3Operation, { location }: UserContext) {
55
59
  if (!operation?.security) {
56
60
  eachOperationHasSecurity = false;
61
+ operationsWithoutSecurity.push(location);
57
62
  }
58
63
  },
59
64
  };
@@ -1,8 +1,9 @@
1
1
  import type { Oas3Rule, Oas2Rule } from '../../visitors';
2
2
  import { isNamedType } from '../../types';
3
- import { oasTypeOf, matchesJsonSchemaType, getSuggest } from '../utils';
3
+ import { oasTypeOf, matchesJsonSchemaType, getSuggest, validateSchemaEnumType } from '../utils';
4
4
  import { isRef } from '../../ref-utils';
5
5
  import { isPlainObject } from '../../utils';
6
+ import { UserContext } from '../../walk';
6
7
 
7
8
  export const OasSpec: Oas3Rule | Oas2Rule = () => {
8
9
  return {
@@ -114,17 +115,20 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
114
115
  propValue = resolve(propValue).node;
115
116
  }
116
117
 
117
- if (propSchema.enum) {
118
- if (!propSchema.enum.includes(propValue)) {
119
- report({
120
- location: propLocation,
121
- message: `\`${propName}\` can be one of the following only: ${propSchema.enum
122
- .map((i) => `"${i}"`)
123
- .join(', ')}.`,
124
- from: refLocation,
125
- suggest: getSuggest(propValue, propSchema.enum),
126
- });
118
+ if (propSchema.items && propSchema.items?.enum && Array.isArray(propValue)) {
119
+ for (let i = 0; i < propValue.length; i++) {
120
+ validateSchemaEnumType(propSchema.items?.enum, propValue[i], propName, refLocation, {
121
+ report,
122
+ location: location.child([propName, i]),
123
+ } as UserContext);
127
124
  }
125
+ }
126
+
127
+ if (propSchema.enum) {
128
+ validateSchemaEnumType(propSchema.enum, propValue, propName, refLocation, {
129
+ report,
130
+ location: location.child([propName]),
131
+ } as UserContext);
128
132
  } else if (propSchema.type && !matchesJsonSchemaType(propValue, propSchema.type, false)) {
129
133
  report({
130
134
  message: `Expected type \`${propSchema.type}\` but got \`${propValueType}\`.`,
@@ -270,4 +270,120 @@ describe('Oas3 response-contains-header', () => {
270
270
  });
271
271
  expect(results).toMatchInlineSnapshot(`Array []`);
272
272
  });
273
+
274
+ it('should not report response object containing header name upper cased', async () => {
275
+ const document = parseYamlToDocument(outdent`
276
+ openapi: 3.0.3
277
+ info:
278
+ version: 3.0.0
279
+ paths:
280
+ /store/subscribe:
281
+ post:
282
+ responses:
283
+ '200':
284
+ description: successful operation
285
+ headers:
286
+ X-Test-Header:
287
+ description: calls per hour allowed by the user
288
+ schema:
289
+ type: integer
290
+ format: int32
291
+ `);
292
+
293
+ const results = await lintDocument({
294
+ externalRefResolver: new BaseResolver(),
295
+ document,
296
+ config: await makeConfig({
297
+ 'response-contains-header': {
298
+ severity: 'error',
299
+ names: { '2XX': ['x-test-header'] },
300
+ },
301
+ }),
302
+ });
303
+ expect(results).toMatchInlineSnapshot(`Array []`);
304
+ });
305
+
306
+ it('should not report response object containing header name in the rule upper cased', async () => {
307
+ const document = parseYamlToDocument(outdent`
308
+ openapi: 3.0.3
309
+ info:
310
+ version: 3.0.0
311
+ paths:
312
+ /store/subscribe:
313
+ post:
314
+ responses:
315
+ '200':
316
+ description: successful operation
317
+ headers:
318
+ x-test-header:
319
+ description: calls per hour allowed by the user
320
+ schema:
321
+ type: integer
322
+ format: int32
323
+ `);
324
+
325
+ const results = await lintDocument({
326
+ externalRefResolver: new BaseResolver(),
327
+ document,
328
+ config: await makeConfig({
329
+ 'response-contains-header': {
330
+ severity: 'error',
331
+ names: { '2XX': ['X-Test-Header'] },
332
+ },
333
+ }),
334
+ });
335
+ expect(results).toMatchInlineSnapshot(`Array []`);
336
+ });
337
+
338
+ it('should report even if the response is null', async () => {
339
+ const document = parseYamlToDocument(
340
+ outdent`
341
+ openapi: 3.0.0
342
+ paths:
343
+ '/test/':
344
+ put:
345
+ responses:
346
+ '200': null
347
+ `,
348
+ 'foobar.yaml'
349
+ );
350
+
351
+ const results = await lintDocument({
352
+ externalRefResolver: new BaseResolver(),
353
+ document,
354
+ config: await makeConfig({
355
+ 'response-contains-header': {
356
+ severity: 'error',
357
+ names: { '2XX': ['X-Test-Header'] },
358
+ },
359
+ }),
360
+ });
361
+
362
+ expect(results).toMatchInlineSnapshot(`
363
+ Array [
364
+ Object {
365
+ "location": Array [
366
+ Object {
367
+ "pointer": "#/paths/~1test~1/put/responses/200/headers",
368
+ "reportOnKey": true,
369
+ "source": Source {
370
+ "absoluteRef": "foobar.yaml",
371
+ "body": "openapi: 3.0.0
372
+ paths:
373
+ '/test/':
374
+ put:
375
+ responses:
376
+ '200': null",
377
+ "mimeType": undefined,
378
+ },
379
+ },
380
+ ],
381
+ "message": "Response object must contain a \\"X-Test-Header\\" header.",
382
+ "ruleId": "response-contains-header",
383
+ "severity": "error",
384
+ "suggest": Array [],
385
+ },
386
+ ]
387
+ `);
388
+ });
273
389
  });
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
5
5
 
6
6
  export const RequestMimeType: Oas3Rule = ({ allowedValues }) => {
7
7
  return {
8
- PathsMap: {
8
+ Paths: {
9
9
  RequestBody: {
10
10
  leave(requestBody: Oas3RequestBody, ctx: UserContext) {
11
11
  validateMimeTypeOAS3({ type: 'consumes', value: requestBody }, ctx, allowedValues);
@@ -5,7 +5,7 @@ import { validateMimeTypeOAS3 } from '../../utils';
5
5
 
6
6
  export const ResponseMimeType: Oas3Rule = ({ allowedValues }) => {
7
7
  return {
8
- PathsMap: {
8
+ Paths: {
9
9
  Response: {
10
10
  leave(response: Oas3Response, ctx: UserContext) {
11
11
  validateMimeTypeOAS3({ type: 'produces', value: response }, ctx, allowedValues);
@@ -41,7 +41,7 @@ export const Stats = (statsAccumulator: StatsAccumulator) => {
41
41
  },
42
42
  },
43
43
  },
44
- PathsMap: {
44
+ Paths: {
45
45
  PathItem: {
46
46
  leave() {
47
47
  statsAccumulator.pathItems.total++;
@@ -138,3 +138,25 @@ export function getAdditionalPropertiesOption(opts: Record<string, any>): boolea
138
138
  showWarningForDeprecatedField('disallowAdditionalProperties', 'allowAdditionalProperties');
139
139
  return !opts.disallowAdditionalProperties;
140
140
  }
141
+
142
+ export function validateSchemaEnumType(
143
+ schemaEnum: string[],
144
+ propertyValue: string,
145
+ propName: string,
146
+ refLocation: Location | undefined,
147
+ { report, location }: UserContext
148
+ ) {
149
+ if (!schemaEnum) {
150
+ return;
151
+ }
152
+ if (!schemaEnum.includes(propertyValue === null ? 'null' : propertyValue)) {
153
+ report({
154
+ location,
155
+ message: `\`${propName}\` can be one of the following only: ${schemaEnum
156
+ .map((type) => `"${type}"`)
157
+ .join(', ')}.`,
158
+ from: refLocation,
159
+ suggest: getSuggest(propertyValue, schemaEnum),
160
+ });
161
+ }
162
+ }
package/src/types/oas2.ts CHANGED
@@ -11,7 +11,7 @@ const Root: NodeType = {
11
11
  schemes: { type: 'array', items: { type: 'string' } },
12
12
  consumes: { type: 'array', items: { type: 'string' } },
13
13
  produces: { type: 'array', items: { type: 'string' } },
14
- paths: 'PathsMap',
14
+ paths: 'Paths',
15
15
  definitions: 'NamedSchemas',
16
16
  parameters: 'NamedParameters',
17
17
  responses: 'NamedResponses',
@@ -51,7 +51,7 @@ const License: NodeType = {
51
51
  required: ['name'],
52
52
  };
53
53
 
54
- const PathsMap: NodeType = {
54
+ const Paths: NodeType = {
55
55
  properties: {},
56
56
  additionalProperties: (_value: any, key: string) =>
57
57
  key.startsWith('/') ? 'PathItem' : undefined,
@@ -82,7 +82,7 @@ const Operation: NodeType = {
82
82
  consumes: { type: 'array', items: { type: 'string' } },
83
83
  produces: { type: 'array', items: { type: 'string' } },
84
84
  parameters: listOf('Parameter'),
85
- responses: 'ResponsesMap',
85
+ responses: 'Responses',
86
86
  schemes: { type: 'array', items: { type: 'string' } },
87
87
  deprecated: { type: 'boolean' },
88
88
  security: listOf('SecurityRequirement'),
@@ -180,7 +180,7 @@ const ParameterItems: NodeType = {
180
180
  },
181
181
  };
182
182
 
183
- const ResponsesMap: NodeType = {
183
+ const Responses: NodeType = {
184
184
  properties: {
185
185
  default: 'Response',
186
186
  },
@@ -376,14 +376,14 @@ export const Oas2Types: Record<string, NodeType> = {
376
376
  Info,
377
377
  Contact,
378
378
  License,
379
- PathsMap,
379
+ Paths,
380
380
  PathItem,
381
381
  Parameter,
382
382
  ParameterItems,
383
383
  Operation,
384
384
  Examples,
385
385
  Header,
386
- ResponsesMap,
386
+ Responses,
387
387
  Response,
388
388
  Schema,
389
389
  Xml,
package/src/types/oas3.ts CHANGED
@@ -10,7 +10,7 @@ const Root: NodeType = {
10
10
  security: listOf('SecurityRequirement'),
11
11
  tags: listOf('Tag'),
12
12
  externalDocs: 'ExternalDocs',
13
- paths: 'PathsMap',
13
+ paths: 'Paths',
14
14
  components: 'Components',
15
15
  'x-webhooks': 'WebhooksMap',
16
16
  },
@@ -88,7 +88,7 @@ const License: NodeType = {
88
88
  required: ['name'],
89
89
  };
90
90
 
91
- const PathsMap: NodeType = {
91
+ const Paths: NodeType = {
92
92
  properties: {},
93
93
  additionalProperties: (_value: any, key: string) =>
94
94
  key.startsWith('/') ? 'PathItem' : undefined,
@@ -153,7 +153,7 @@ const Operation: NodeType = {
153
153
  security: listOf('SecurityRequirement'),
154
154
  servers: listOf('Server'),
155
155
  requestBody: 'RequestBody',
156
- responses: 'ResponsesMap',
156
+ responses: 'Responses',
157
157
  deprecated: { type: 'boolean' },
158
158
  callbacks: 'CallbacksMap',
159
159
  'x-codeSamples': listOf('XCodeSample'),
@@ -190,7 +190,7 @@ const MediaType: NodeType = {
190
190
  schema: 'Schema',
191
191
  example: { isExample: true },
192
192
  examples: 'ExamplesMap',
193
- encoding: 'EncodingsMap',
193
+ encoding: 'EncodingMap',
194
194
  },
195
195
  };
196
196
 
@@ -234,7 +234,7 @@ const Header: NodeType = {
234
234
  requiredOneOf: ['schema', 'content'],
235
235
  };
236
236
 
237
- const ResponsesMap: NodeType = {
237
+ const Responses: NodeType = {
238
238
  properties: { default: 'Response' },
239
239
  additionalProperties: (_v: any, key: string) =>
240
240
  responseCodeRegexp.test(key) ? 'Response' : undefined,
@@ -408,7 +408,7 @@ const AuthorizationCode: NodeType = {
408
408
  required: ['authorizationUrl', 'tokenUrl', 'scopes'],
409
409
  };
410
410
 
411
- const SecuritySchemeFlows: NodeType = {
411
+ const OAuth2Flows: NodeType = {
412
412
  properties: {
413
413
  implicit: 'ImplicitFlow',
414
414
  password: 'PasswordFlow',
@@ -425,7 +425,7 @@ const SecurityScheme: NodeType = {
425
425
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
426
426
  scheme: { type: 'string' },
427
427
  bearerFormat: { type: 'string' },
428
- flows: 'SecuritySchemeFlows',
428
+ flows: 'OAuth2Flows',
429
429
  openIdConnectUrl: { type: 'string' },
430
430
  },
431
431
  required(value) {
@@ -470,7 +470,7 @@ export const Oas3Types: Record<string, NodeType> = {
470
470
  Info,
471
471
  Contact,
472
472
  License,
473
- PathsMap,
473
+ Paths,
474
474
  PathItem,
475
475
  Parameter,
476
476
  Operation,
@@ -482,10 +482,10 @@ export const Oas3Types: Record<string, NodeType> = {
482
482
  Example,
483
483
  ExamplesMap: mapOf('Example'),
484
484
  Encoding,
485
- EncodingsMap: mapOf('Encoding'),
485
+ EncodingMap: mapOf('Encoding'),
486
486
  Header,
487
487
  HeadersMap: mapOf('Header'),
488
- ResponsesMap,
488
+ Responses,
489
489
  Response,
490
490
  Link,
491
491
  Schema,
@@ -508,7 +508,7 @@ export const Oas3Types: Record<string, NodeType> = {
508
508
  PasswordFlow,
509
509
  ClientCredentials,
510
510
  AuthorizationCode,
511
- SecuritySchemeFlows,
511
+ OAuth2Flows,
512
512
  SecurityScheme,
513
513
  XCodeSample,
514
514
  WebhooksMap,
@@ -9,7 +9,7 @@ const Root: NodeType = {
9
9
  security: listOf('SecurityRequirement'),
10
10
  tags: listOf('Tag'),
11
11
  externalDocs: 'ExternalDocs',
12
- paths: 'PathsMap',
12
+ paths: 'Paths',
13
13
  webhooks: 'WebhooksMap',
14
14
  components: 'Components',
15
15
  jsonSchemaDialect: { type: 'string' },
@@ -69,7 +69,7 @@ const Operation: NodeType = {
69
69
  security: listOf('SecurityRequirement'),
70
70
  servers: listOf('Server'),
71
71
  requestBody: 'RequestBody',
72
- responses: 'ResponsesMap',
72
+ responses: 'Responses',
73
73
  deprecated: { type: 'boolean' },
74
74
  callbacks: mapOf('Callback'),
75
75
  'x-codeSamples': listOf('XCodeSample'),
@@ -176,7 +176,7 @@ const SecurityScheme: NodeType = {
176
176
  in: { type: 'string', enum: ['query', 'header', 'cookie'] },
177
177
  scheme: { type: 'string' },
178
178
  bearerFormat: { type: 'string' },
179
- flows: 'SecuritySchemeFlows',
179
+ flows: 'OAuth2Flows',
180
180
  openIdConnectUrl: { type: 'string' },
181
181
  },
182
182
  required(value) {
@@ -66,7 +66,7 @@ const nodeTypesList = [
66
66
  'Info',
67
67
  'Contact',
68
68
  'License',
69
- 'PathsMap',
69
+ 'Paths',
70
70
  'PathItem',
71
71
  'Parameter',
72
72
  'Operation',
@@ -78,10 +78,10 @@ const nodeTypesList = [
78
78
  'Example',
79
79
  'ExamplesMap',
80
80
  'Encoding',
81
- 'EncodingsMap',
81
+ 'EncodingMap',
82
82
  'Header',
83
83
  'HeadersMap',
84
- 'ResponsesMap',
84
+ 'Responses',
85
85
  'Response',
86
86
  'Link',
87
87
  'LinksMap',
@@ -104,7 +104,7 @@ const nodeTypesList = [
104
104
  'PasswordFlow',
105
105
  'ClientCredentials',
106
106
  'AuthorizationCode',
107
- 'SecuritySchemeFlows',
107
+ 'OAuth2Flows',
108
108
  'SecurityScheme',
109
109
  'XCodeSample',
110
110
  'WebhooksMap',
@@ -890,6 +890,16 @@ const ConfigReferenceDocs: NodeType = {
890
890
  unstable_externalDescription: { type: 'boolean' }, // deprecated
891
891
  unstable_ignoreMimeParameters: { type: 'boolean' },
892
892
  untrustedDefinition: { type: 'boolean' },
893
+ mockServer: {
894
+ properties: {
895
+ url: { type: 'string' },
896
+ position: { enum: ['first', 'last', 'replace', 'off'] },
897
+ description: { type: 'string' },
898
+ },
899
+ },
900
+ showAccessMode: { type: 'boolean' },
901
+ preserveOriginalExtensionsName: { type: 'boolean' },
902
+ markdownHeadingsAnchorLevel: { type: 'number' },
893
903
  },
894
904
  additionalProperties: { type: 'string' },
895
905
  };
package/src/utils.ts CHANGED
@@ -231,3 +231,16 @@ export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
231
231
  export function identity<T>(value: T): T {
232
232
  return value;
233
233
  }
234
+
235
+ export function pickDefined<T extends Record<string, unknown>>(
236
+ obj?: T
237
+ ): Record<string, unknown> | undefined {
238
+ if (!obj) return undefined;
239
+ const res: Record<string, unknown> = {};
240
+ for (const key in obj) {
241
+ if (obj[key] !== undefined) {
242
+ res[key] = obj[key];
243
+ }
244
+ }
245
+ return res;
246
+ }
package/src/visitors.ts CHANGED
@@ -136,9 +136,10 @@ type Oas3FlatVisitor = {
136
136
  Info?: VisitFunctionOrObject<Oas3Info>;
137
137
  Contact?: VisitFunctionOrObject<Oas3Contact>;
138
138
  License?: VisitFunctionOrObject<Oas3License>;
139
- PathsMap?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
139
+ Paths?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
140
140
  PathItem?: VisitFunctionOrObject<Oas3PathItem>;
141
- Callback?: VisitFunctionOrObject<Record<string, Oas3PathItem>>;
141
+ Callback?: VisitFunctionOrObject<Oas3Callback>;
142
+ CallbacksMap?: VisitFunctionOrObject<Record<string, Oas3Callback>>;
142
143
  Parameter?: VisitFunctionOrObject<Oas3Parameter>;
143
144
  Operation?: VisitFunctionOrObject<Oas3Operation>;
144
145
  RequestBody?: VisitFunctionOrObject<Oas3RequestBody>;
@@ -147,7 +148,7 @@ type Oas3FlatVisitor = {
147
148
  Example?: VisitFunctionOrObject<Oas3Example>;
148
149
  Encoding?: VisitFunctionOrObject<Oas3Encoding>;
149
150
  Header?: VisitFunctionOrObject<Oas3Header>;
150
- ResponsesMap?: VisitFunctionOrObject<Record<string, Oas3Response>>;
151
+ Responses?: VisitFunctionOrObject<Record<string, Oas3Response>>;
151
152
  Response?: VisitFunctionOrObject<Oas3Response>;
152
153
  Link?: VisitFunctionOrObject<Oas3Link>;
153
154
  Schema?: VisitFunctionOrObject<Oas3Schema>;
@@ -169,7 +170,7 @@ type Oas3FlatVisitor = {
169
170
  PasswordFlow?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['password']>;
170
171
  ClientCredentials?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['clientCredentials']>;
171
172
  AuthorizationCode?: VisitFunctionOrObject<Oas3SecurityScheme['flows']['authorizationCode']>;
172
- SecuritySchemeFlows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
173
+ OAuth2Flows?: VisitFunctionOrObject<Oas3SecurityScheme['flows']>;
173
174
  SecurityScheme?: VisitFunctionOrObject<Oas3SecurityScheme>;
174
175
  };
175
176
 
@@ -181,13 +182,13 @@ type Oas2FlatVisitor = {
181
182
  Info?: VisitFunctionOrObject<Oas2Info>;
182
183
  Contact?: VisitFunctionOrObject<Oas2Contact>;
183
184
  License?: VisitFunctionOrObject<Oas2License>;
184
- PathsMap?: VisitFunctionOrObject<Record<string, Oas2PathItem>>;
185
+ Paths?: VisitFunctionOrObject<Record<string, Oas2PathItem>>;
185
186
  PathItem?: VisitFunctionOrObject<Oas2PathItem>;
186
187
  Parameter?: VisitFunctionOrObject<any>;
187
188
  Operation?: VisitFunctionOrObject<Oas2Operation>;
188
189
  Examples?: VisitFunctionOrObject<Record<string, any>>;
189
190
  Header?: VisitFunctionOrObject<Oas2Header>;
190
- ResponsesMap?: VisitFunctionOrObject<Record<string, Oas2Response>>;
191
+ Responses?: VisitFunctionOrObject<Record<string, Oas2Response>>;
191
192
  Response?: VisitFunctionOrObject<Oas2Response>;
192
193
  Schema?: VisitFunctionOrObject<Oas2Schema>;
193
194
  Xml?: VisitFunctionOrObject<Oas2Xml>;
@@ -201,13 +202,14 @@ type Oas2FlatVisitor = {
201
202
  const legacyTypesMap = {
202
203
  Root: 'DefinitionRoot',
203
204
  ServerVariablesMap: 'ServerVariableMap',
204
- PathsMap: 'PathMap',
205
+ Paths: ['PathMap', 'PathsMap'],
205
206
  CallbacksMap: 'CallbackMap',
206
207
  MediaTypesMap: 'MediaTypeMap',
207
208
  ExamplesMap: 'ExampleMap',
208
- EncodingsMap: 'EncodingMap',
209
+ EncodingMap: 'EncodingsMap',
209
210
  HeadersMap: 'HeaderMap',
210
211
  LinksMap: 'LinkMap',
212
+ OAuth2Flows: 'SecuritySchemeFlows',
211
213
  };
212
214
 
213
215
  type Oas3NestedVisitor = {
@@ -372,6 +374,18 @@ export function normalizeVisitors<T extends BaseVisitor>(
372
374
  }
373
375
  }
374
376
 
377
+ function findLegacyVisitorNode<T>(
378
+ visitor: NestedVisitObject<any, T>,
379
+ typeName: keyof T | Array<keyof T>
380
+ ) {
381
+ if (Array.isArray(typeName)) {
382
+ const name = typeName.find((name) => visitor[name]) || undefined;
383
+ return name && visitor[name];
384
+ }
385
+
386
+ return visitor[typeName];
387
+ }
388
+
375
389
  function normalizeVisitorLevel(
376
390
  ruleConf: RuleInstanceConfig,
377
391
  visitor: NestedVisitObject<any, T>,
@@ -394,9 +408,10 @@ export function normalizeVisitors<T extends BaseVisitor>(
394
408
 
395
409
  for (const typeName of visitorKeys as Array<keyof T>) {
396
410
  const typeVisitor = (visitor[typeName] ||
397
- visitor[
411
+ findLegacyVisitorNode(
412
+ visitor,
398
413
  legacyTypesMap[typeName as keyof typeof legacyTypesMap] as keyof T
399
- ]) as any as NestedVisitObject<any, T>;
414
+ )) as any as NestedVisitObject<any, T>;
400
415
  const normalizedTypeVisitor = normalizedVisitors[typeName];
401
416
 
402
417
  if (!typeVisitor) continue;