@redocly/openapi-core 1.0.0-beta.49 → 1.0.0-beta.53

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 (136) hide show
  1. package/__tests__/normalizeVisitors.test.ts +1 -1
  2. package/__tests__/utils.ts +1 -1
  3. package/__tests__/walk.test.ts +77 -4
  4. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -1
  5. package/lib/benchmark/utils.d.ts +1 -1
  6. package/lib/bundle.d.ts +1 -1
  7. package/lib/bundle.js +10 -10
  8. package/lib/config/builtIn.d.ts +2 -1
  9. package/lib/config/builtIn.js +9 -1
  10. package/lib/config/config.d.ts +5 -7
  11. package/lib/config/config.js +40 -100
  12. package/lib/config/load.d.ts +2 -0
  13. package/lib/config/load.js +65 -0
  14. package/lib/config/rules.d.ts +1 -1
  15. package/lib/format/codeframes.js +1 -1
  16. package/lib/index.d.ts +4 -3
  17. package/lib/index.js +13 -11
  18. package/lib/lint.d.ts +6 -19
  19. package/lib/lint.js +13 -43
  20. package/lib/oas-types.d.ts +19 -0
  21. package/lib/oas-types.js +42 -0
  22. package/lib/resolve.d.ts +6 -2
  23. package/lib/resolve.js +18 -5
  24. package/lib/rules/ajv.d.ts +3 -3
  25. package/lib/rules/ajv.js +21 -18
  26. package/lib/rules/common/info-contact.js +2 -1
  27. package/lib/rules/common/info-description.js +2 -1
  28. package/lib/rules/common/info-license-url.js +2 -1
  29. package/lib/rules/common/license-url.js +2 -1
  30. package/lib/rules/common/no-ambiguous-paths.js +2 -1
  31. package/lib/rules/common/no-enum-type-mismatch.js +2 -1
  32. package/lib/rules/common/no-identical-paths.js +2 -1
  33. package/lib/rules/common/no-path-trailing-slash.js +2 -1
  34. package/lib/rules/common/operation-2xx-response.js +2 -1
  35. package/lib/rules/common/operation-description.js +2 -1
  36. package/lib/rules/common/operation-operationId-unique.js +2 -1
  37. package/lib/rules/common/operation-operationId-url-safe.js +2 -1
  38. package/lib/rules/common/operation-operationId.js +9 -4
  39. package/lib/rules/common/operation-parameters-unique.js +2 -1
  40. package/lib/rules/common/operation-security-defined.js +2 -1
  41. package/lib/rules/common/operation-singular-tag.js +2 -1
  42. package/lib/rules/common/operation-summary.js +2 -1
  43. package/lib/rules/common/operation-tag-defined.js +2 -1
  44. package/lib/rules/common/parameter-description.js +2 -1
  45. package/lib/rules/common/path-declaration-must-exist.js +2 -1
  46. package/lib/rules/common/path-http-verbs-order.js +2 -1
  47. package/lib/rules/common/path-not-include-query.js +2 -1
  48. package/lib/rules/common/path-params-defined.js +3 -2
  49. package/lib/rules/common/paths-kebab-case.js +2 -1
  50. package/lib/rules/common/registry-dependencies.js +2 -1
  51. package/lib/rules/common/spec.js +5 -2
  52. package/lib/rules/common/tag-description.js +2 -1
  53. package/lib/rules/common/tags-alphabetical.js +2 -1
  54. package/lib/rules/no-unresolved-refs.js +2 -1
  55. package/lib/rules/oas2/boolean-parameter-prefixes.js +2 -1
  56. package/lib/rules/oas2/index.d.ts +1 -1
  57. package/lib/rules/oas2/index.js +1 -1
  58. package/lib/rules/oas3/boolean-parameter-prefixes.js +2 -1
  59. package/lib/rules/oas3/index.d.ts +2 -1
  60. package/lib/rules/oas3/index.js +2 -2
  61. package/lib/rules/oas3/no-empty-servers.js +2 -1
  62. package/lib/rules/oas3/no-example-value-and-externalValue.js +2 -1
  63. package/lib/rules/oas3/no-invalid-media-type-examples.js +20 -10
  64. package/lib/rules/oas3/no-server-example.com.js +2 -1
  65. package/lib/rules/oas3/no-server-trailing-slash.js +2 -1
  66. package/lib/rules/oas3/no-servers-empty-enum.js +2 -1
  67. package/lib/rules/oas3/no-undefined-server-variable.js +2 -1
  68. package/lib/rules/oas3/no-unused-components.js +2 -1
  69. package/lib/rules/other/stats.js +2 -1
  70. package/lib/types/oas2.js +1 -1
  71. package/lib/types/oas3.js +1 -1
  72. package/lib/types/oas3_1.js +1 -1
  73. package/lib/utils.d.ts +1 -1
  74. package/lib/visitors.d.ts +3 -1
  75. package/lib/visitors.js +4 -4
  76. package/lib/walk.d.ts +1 -1
  77. package/lib/walk.js +11 -2
  78. package/package.json +7 -7
  79. package/src/__tests__/lint.test.ts +44 -0
  80. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -1
  81. package/src/benchmark/benchmark.js +3 -1
  82. package/src/benchmark/utils.ts +1 -1
  83. package/src/bundle.ts +2 -2
  84. package/src/config/__tests__/resolve-plugins.test.ts +1 -1
  85. package/src/config/builtIn.ts +11 -1
  86. package/src/config/config.ts +30 -78
  87. package/src/config/load.ts +60 -0
  88. package/src/config/rules.ts +1 -1
  89. package/src/format/codeframes.ts +1 -1
  90. package/src/index.ts +4 -3
  91. package/src/lint.ts +19 -56
  92. package/src/oas-types.ts +58 -0
  93. package/src/resolve.ts +17 -4
  94. package/src/rules/__tests__/config.ts +10 -0
  95. package/src/rules/__tests__/no-unresolved-refs.test.ts +9 -21
  96. package/src/rules/ajv.ts +27 -23
  97. package/src/rules/common/__tests__/info-description.test.ts +8 -16
  98. package/src/rules/common/__tests__/info-license.test.ts +3 -3
  99. package/src/rules/common/__tests__/license-url.test.ts +3 -3
  100. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +2 -2
  101. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +50 -5
  102. package/src/rules/common/__tests__/no-identical-paths.test.ts +2 -2
  103. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +4 -4
  104. package/src/rules/common/__tests__/operation-2xx-response.test.ts +4 -4
  105. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +3 -3
  106. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +2 -5
  107. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +5 -17
  108. package/src/rules/common/__tests__/operation-security-defined.test.ts +3 -3
  109. package/src/rules/common/__tests__/operation-singular-tag.test.ts +3 -3
  110. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +3 -3
  111. package/src/rules/common/__tests__/path-not-include-query.test.ts +3 -3
  112. package/src/rules/common/__tests__/path-params-defined.test.ts +4 -4
  113. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  114. package/src/rules/common/__tests__/tag-description.test.ts +3 -3
  115. package/src/rules/common/__tests__/tags-alphabetical.test.ts +3 -3
  116. package/src/rules/common/operation-operationId.ts +7 -3
  117. package/src/rules/common/spec.ts +4 -1
  118. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +7 -10
  119. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +3 -6
  120. package/src/rules/oas2/__tests__/spec/utils.ts +2 -0
  121. package/src/rules/oas2/index.ts +1 -10
  122. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +7 -10
  123. package/src/rules/oas3/__tests__/fixtures/common.yaml +11 -0
  124. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +7 -7
  125. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +3 -9
  126. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +103 -29
  127. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +3 -3
  128. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  129. package/src/rules/oas3/__tests__/no-unused-components.test.ts +2 -2
  130. package/src/rules/oas3/__tests__/spec/spec.test.ts +3 -1
  131. package/src/rules/oas3/__tests__/spec/utils.ts +2 -0
  132. package/src/rules/oas3/index.ts +3 -4
  133. package/src/rules/oas3/no-invalid-media-type-examples.ts +27 -19
  134. package/src/visitors.ts +12 -8
  135. package/src/walk.ts +24 -8
  136. package/tsconfig.tsbuildinfo +1 -3084
@@ -1,7 +1,7 @@
1
1
  import { outdent } from 'outdent';
2
- import { LintConfig } from '../../../config/config';
3
2
  import { lintDocument } from '../../../lint';
4
3
  import { parseYamlToDocument, replaceSourceWithRef } from '../../../../__tests__/utils';
4
+ import { makeConfig } from '../../__tests__/config';
5
5
  import { BaseResolver } from '../../../resolve';
6
6
 
7
7
  describe('no-invalid-media-type-examples', () => {
@@ -34,7 +34,7 @@ describe('no-invalid-media-type-examples', () => {
34
34
  const results = await lintDocument({
35
35
  externalRefResolver: new BaseResolver(),
36
36
  document,
37
- config: new LintConfig({ extends: [], rules: { 'no-invalid-media-type-examples': 'error' } }),
37
+ config: makeConfig({ 'no-invalid-media-type-examples': 'error' }),
38
38
  });
39
39
 
40
40
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -51,7 +51,7 @@ describe('no-invalid-media-type-examples', () => {
51
51
  "source": "foobar.yaml",
52
52
  },
53
53
  ],
54
- "message": "Example value must conform to the schema: \`a\` property type should be string.",
54
+ "message": "Example value must conform to the schema: \`a\` property type must be string.",
55
55
  "ruleId": "no-invalid-media-type-examples",
56
56
  "severity": "error",
57
57
  "suggest": Array [],
@@ -68,7 +68,7 @@ describe('no-invalid-media-type-examples', () => {
68
68
  "source": "foobar.yaml",
69
69
  },
70
70
  ],
71
- "message": "Example value must conform to the schema: \`b\` property type should be number.",
71
+ "message": "Example value must conform to the schema: \`b\` property type must be number.",
72
72
  "ruleId": "no-invalid-media-type-examples",
73
73
  "severity": "error",
74
74
  "suggest": Array [],
@@ -107,13 +107,10 @@ describe('no-invalid-media-type-examples', () => {
107
107
  const results = await lintDocument({
108
108
  externalRefResolver: new BaseResolver(),
109
109
  document,
110
- config: new LintConfig({
111
- extends: [],
112
- rules: {
113
- 'no-invalid-media-type-examples': {
114
- severity: 'error',
115
- disallowAdditionalProperties: true,
116
- },
110
+ config: makeConfig({
111
+ 'no-invalid-media-type-examples': {
112
+ severity: 'error',
113
+ disallowAdditionalProperties: true,
117
114
  },
118
115
  }),
119
116
  });
@@ -132,7 +129,7 @@ describe('no-invalid-media-type-examples', () => {
132
129
  "source": "foobar.yaml",
133
130
  },
134
131
  ],
135
- "message": "Example value must conform to the schema: should NOT have additional properties \`c\`.",
132
+ "message": "Example value must conform to the schema: must NOT have additional properties \`c\`.",
136
133
  "ruleId": "no-invalid-media-type-examples",
137
134
  "severity": "error",
138
135
  "suggest": Array [],
@@ -170,13 +167,10 @@ describe('no-invalid-media-type-examples', () => {
170
167
  const results = await lintDocument({
171
168
  externalRefResolver: new BaseResolver(),
172
169
  document,
173
- config: new LintConfig({
174
- extends: [],
175
- rules: {
176
- 'no-invalid-media-type-examples': {
177
- severity: 'error',
178
- disallowAdditionalProperties: true,
179
- },
170
+ config: makeConfig({
171
+ 'no-invalid-media-type-examples': {
172
+ severity: 'error',
173
+ disallowAdditionalProperties: true,
180
174
  },
181
175
  }),
182
176
  });
@@ -223,13 +217,10 @@ describe('no-invalid-media-type-examples', () => {
223
217
  const results = await lintDocument({
224
218
  externalRefResolver: new BaseResolver(),
225
219
  document,
226
- config: new LintConfig({
227
- extends: [],
228
- rules: {
229
- 'no-invalid-media-type-examples': {
230
- severity: 'error',
231
- disallowAdditionalProperties: true,
232
- },
220
+ config: makeConfig({
221
+ 'no-invalid-media-type-examples': {
222
+ severity: 'error',
223
+ disallowAdditionalProperties: true,
233
224
  },
234
225
  }),
235
226
  });
@@ -248,7 +239,7 @@ describe('no-invalid-media-type-examples', () => {
248
239
  "source": "foobar.yaml",
249
240
  },
250
241
  ],
251
- "message": "Example value must conform to the schema: \`a\` property type should be string.",
242
+ "message": "Example value must conform to the schema: \`a\` property type must be string.",
252
243
  "ruleId": "no-invalid-media-type-examples",
253
244
  "severity": "error",
254
245
  "suggest": Array [],
@@ -279,7 +270,7 @@ describe('no-invalid-media-type-examples', () => {
279
270
  const results = await lintDocument({
280
271
  externalRefResolver: new BaseResolver(),
281
272
  document,
282
- config: new LintConfig({ extends: [], rules: { 'no-invalid-media-type-examples': 'error' } }),
273
+ config: makeConfig({ 'no-invalid-media-type-examples': 'error' }),
283
274
  });
284
275
 
285
276
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
@@ -311,9 +302,92 @@ describe('no-invalid-media-type-examples', () => {
311
302
  const results = await lintDocument({
312
303
  externalRefResolver: new BaseResolver(),
313
304
  document,
314
- config: new LintConfig({ extends: [], rules: { 'no-invalid-media-type-examples': 'error' } }),
305
+ config: makeConfig({ 'no-invalid-media-type-examples': 'error' }),
315
306
  });
316
307
 
317
308
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
318
309
  });
310
+
311
+ it('should work with cross-file $ref', async () => {
312
+ const document = parseYamlToDocument(
313
+ outdent`
314
+ openapi: 3.0.0
315
+ components:
316
+ schemas:
317
+ C:
318
+ $ref: './fixtures/common.yaml#/components/schemas/A'
319
+ paths:
320
+ /pet:
321
+ get:
322
+ responses:
323
+ 200:
324
+ content:
325
+ application/json:
326
+ example: {
327
+ "a": "test",
328
+ "b": "test"
329
+ }
330
+ schema:
331
+ $ref: '#/components/schemas/C'
332
+
333
+ `,
334
+ __dirname + '/foobar.yaml',
335
+ );
336
+
337
+ const results = await lintDocument({
338
+ externalRefResolver: new BaseResolver(),
339
+ document,
340
+ config: makeConfig({ 'no-invalid-media-type-examples': 'error' }),
341
+ });
342
+
343
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
344
+ });
345
+
346
+ it('should not throw for ajv throw', async () => {
347
+ const document = parseYamlToDocument(
348
+ outdent`
349
+ openapi: 3.0.0
350
+ paths:
351
+ /pet:
352
+ get:
353
+ responses:
354
+ 200:
355
+ content:
356
+ application/json:
357
+ example: {}
358
+ schema:
359
+ nullable: true
360
+
361
+ `,
362
+ 'foobar.yaml',
363
+ );
364
+
365
+ const results = await lintDocument({
366
+ externalRefResolver: new BaseResolver(),
367
+ document,
368
+ config: makeConfig({ 'no-invalid-media-type-examples': 'error' }),
369
+ });
370
+
371
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
372
+ Array [
373
+ Object {
374
+ "from": Object {
375
+ "pointer": "#/paths/~1pet/get/responses/200/content/application~1json",
376
+ "source": "foobar.yaml",
377
+ },
378
+ "location": Array [
379
+ Object {
380
+ "pointer": "#/paths/~1pet/get/responses/200/content/application~1json/schema",
381
+ "reportOnKey": false,
382
+ "source": "foobar.yaml",
383
+ },
384
+ ],
385
+ "message": "Example validation errored: \\"nullable\\" cannot be used without \\"type\\".",
386
+ "ruleId": "no-invalid-media-type-examples",
387
+ "severity": "error",
388
+ "suggest": Array [],
389
+ },
390
+ ]
391
+ `);
392
+ });
319
393
  });
@@ -1,7 +1,7 @@
1
1
  import { outdent } from 'outdent';
2
- import { LintConfig } from '../../../config/config';
3
2
  import { lintDocument } from '../../../lint';
4
3
  import { parseYamlToDocument, replaceSourceWithRef } from '../../../../__tests__/utils';
4
+ import { makeConfig } from '../../__tests__/config';
5
5
  import { BaseResolver } from '../../../resolve';
6
6
 
7
7
  describe('Oas3 oas3-no-server-example.com', () => {
@@ -18,7 +18,7 @@ describe('Oas3 oas3-no-server-example.com', () => {
18
18
  const results = await lintDocument({
19
19
  externalRefResolver: new BaseResolver(),
20
20
  document,
21
- config: new LintConfig({ extends: [], rules: { 'no-server-example.com': 'error' } }),
21
+ config: makeConfig({ 'no-server-example.com': 'error' }),
22
22
  });
23
23
 
24
24
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -53,7 +53,7 @@ describe('Oas3 oas3-no-server-example.com', () => {
53
53
  const results = await lintDocument({
54
54
  externalRefResolver: new BaseResolver(),
55
55
  document,
56
- config: new LintConfig({ extends: [], rules: { 'no-server-example.com': 'error' } }),
56
+ config: makeConfig({ 'no-server-example.com': 'error' }),
57
57
  });
58
58
 
59
59
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
@@ -1,7 +1,7 @@
1
1
  import { outdent } from 'outdent';
2
- import { LintConfig } from '../../../config/config';
3
2
  import { lintDocument } from '../../../lint';
4
3
  import { parseYamlToDocument, replaceSourceWithRef } from '../../../../__tests__/utils';
4
+ import { makeConfig } from '../../__tests__/config';
5
5
  import { BaseResolver } from '../../../resolve';
6
6
 
7
7
  describe('Oas3 oas3-no-server-trailing-slash', () => {
@@ -18,7 +18,7 @@ describe('Oas3 oas3-no-server-trailing-slash', () => {
18
18
  const results = await lintDocument({
19
19
  externalRefResolver: new BaseResolver(),
20
20
  document,
21
- config: new LintConfig({ extends: [], rules: { 'no-server-trailing-slash': 'error' } }),
21
+ config: makeConfig({ 'no-server-trailing-slash': 'error' }),
22
22
  });
23
23
 
24
24
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -53,7 +53,7 @@ describe('Oas3 oas3-no-server-trailing-slash', () => {
53
53
  const results = await lintDocument({
54
54
  externalRefResolver: new BaseResolver(),
55
55
  document,
56
- config: new LintConfig({ extends: [], rules: { 'no-server-trailing-slash': 'error' } }),
56
+ config: makeConfig({ 'no-server-trailing-slash': 'error' }),
57
57
  });
58
58
 
59
59
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
@@ -1,7 +1,7 @@
1
1
  import { outdent } from 'outdent';
2
- import { LintConfig } from '../../../config/config';
3
2
  import { lintDocument } from '../../../lint';
4
3
  import { parseYamlToDocument, replaceSourceWithRef } from '../../../../__tests__/utils';
4
+ import { makeConfig } from '../../__tests__/config';
5
5
  import { BaseResolver } from '../../../resolve';
6
6
 
7
7
  describe('Oas3 no-unused-components', () => {
@@ -43,7 +43,7 @@ describe('Oas3 no-unused-components', () => {
43
43
  const results = await lintDocument({
44
44
  externalRefResolver: new BaseResolver(),
45
45
  document,
46
- config: new LintConfig({ extends: [], rules: { 'no-unused-components': 'error' } }),
46
+ config: makeConfig({ 'no-unused-components': 'error' }),
47
47
  });
48
48
 
49
49
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -2,9 +2,11 @@ import { outdent } from 'outdent';
2
2
  import { lintDocument } from '../../../../lint';
3
3
  import { parseYamlToDocument, replaceSourceWithRef } from '../../../../../__tests__/utils';
4
4
  import { LintConfig } from '../../../../config/config';
5
+ import { defaultPlugin } from '../../../../config/builtIn';
6
+
5
7
  import { BaseResolver } from '../../../../resolve';
6
8
 
7
- const allConfig = new LintConfig({ extends: ['all'] });
9
+ const allConfig = new LintConfig({ plugins: [defaultPlugin], extends: ['all'] });
8
10
 
9
11
  describe('Oas3 Structural visitor basic', () => {
10
12
  it('should report wrong types', async () => {
@@ -2,6 +2,7 @@ import { LintConfig, RuleConfig } from '../../../../config/config';
2
2
  import { parseYamlToDocument } from '../../../../../__tests__/utils';
3
3
  import { lintDocument } from '../../../../lint';
4
4
  import { BaseResolver } from '../../../../resolve';
5
+ import { defaultPlugin } from '../../../../config/builtIn';
5
6
 
6
7
  export async function validateDoc(
7
8
  source: string,
@@ -13,6 +14,7 @@ export async function validateDoc(
13
14
  externalRefResolver: new BaseResolver(),
14
15
  document,
15
16
  config: new LintConfig({
17
+ plugins: [defaultPlugin],
16
18
  extends: [],
17
19
  rules,
18
20
  }),
@@ -1,5 +1,4 @@
1
- import { Oas3RuleSet } from '../../lint';
2
-
1
+ import { Oas3RuleSet } from '../../oas-types';
3
2
  import { OasSpec } from '../common/spec';
4
3
  import { Operation2xxResponse } from '../common/operation-2xx-response';
5
4
  import { OperationIdUnique } from '../common/operation-operationId-unique';
@@ -42,6 +41,7 @@ import { NoEmptyEnumServers } from './no-servers-empty-enum';
42
41
  import { Oas3Decorator } from '../../visitors';
43
42
 
44
43
  export const rules = {
44
+ spec: OasSpec,
45
45
  'info-description': InfoDescription,
46
46
  'info-contact': InfoContact,
47
47
  'info-license': InfoLicense,
@@ -78,8 +78,7 @@ export const rules = {
78
78
  'no-identical-paths': NoIdenticalPaths,
79
79
  'no-ambiguous-paths': NoAmbiguousPaths,
80
80
  'no-undefined-server-variable': NoUndefinedServerVariable,
81
- 'no-servers-empty-enum': NoEmptyEnumServers,
82
- spec: OasSpec,
81
+ 'no-servers-empty-enum': NoEmptyEnumServers
83
82
  } as Oas3RuleSet;
84
83
 
85
84
  export const preprocessors = {};
@@ -29,26 +29,34 @@ export const ValidContentExamples: Oas3Rule = (opts) => {
29
29
  }
30
30
 
31
31
  function validateExample(example: any, dataLoc: Location) {
32
- const { valid, errors } = validateJsonSchema(
33
- example,
34
- mediaType.schema!,
35
- location.child('schema'),
36
- dataLoc.pointer,
37
- resolve,
38
- disallowAdditionalProperties,
39
- );
40
- if (!valid) {
41
- for (let error of errors) {
42
- report({
43
- message: `Example value must conform to the schema: ${error.message}.`,
44
- location: {
45
- ...new Location(dataLoc.source, error.dataPath),
46
- reportOnKey: error.keyword === 'additionalProperties',
47
- },
48
- from: location,
49
- suggest: error.suggest,
50
- });
32
+ try {
33
+ const { valid, errors } = validateJsonSchema(
34
+ example,
35
+ mediaType.schema!,
36
+ location.child('schema'),
37
+ dataLoc.pointer,
38
+ resolve,
39
+ disallowAdditionalProperties,
40
+ );
41
+ if (!valid) {
42
+ for (let error of errors) {
43
+ report({
44
+ message: `Example value must conform to the schema: ${error.message}.`,
45
+ location: {
46
+ ...new Location(dataLoc.source, error.instancePath),
47
+ reportOnKey: error.keyword === 'additionalProperties',
48
+ },
49
+ from: location,
50
+ suggest: error.suggest,
51
+ });
52
+ }
51
53
  }
54
+ } catch(e) {
55
+ report({
56
+ message: `Example validation errored: ${e.message}.`,
57
+ location: location.child('schema'),
58
+ from: location
59
+ });
52
60
  }
53
61
  }
54
62
  },
package/src/visitors.ts CHANGED
@@ -49,7 +49,12 @@ import { NormalizedNodeType } from './types';
49
49
  import { Stack } from './utils';
50
50
  import { UserContext, ResolveResult, ProblemSeverity } from './walk';
51
51
  import { Location } from './ref-utils';
52
- export type VisitFunction<T> = (node: T, ctx: UserContext, parents?: any) => void;
52
+ export type VisitFunction<T> = (
53
+ node: T,
54
+ ctx: UserContext & { ignoreNextVisitorsOnNode: () => void },
55
+ parents?: any,
56
+ context?: any,
57
+ ) => void;
53
58
 
54
59
  type VisitRefFunction = (node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) => void;
55
60
 
@@ -86,7 +91,6 @@ export type VisitorLevelContext = {
86
91
  isSkippedLevel: false;
87
92
  type: NormalizedNodeType;
88
93
  parent: VisitorLevelContext | null;
89
-
90
94
  activatedOn: Stack<{
91
95
  node?: any;
92
96
  withParentNode?: any;
@@ -264,6 +268,11 @@ export function normalizeVisitors<T extends BaseVisitor>(
264
268
  ): NormalizedOasVisitors<T> {
265
269
  const normalizedVisitors: NormalizedOasVisitors<T> = {} as any;
266
270
 
271
+ normalizedVisitors.any = {
272
+ enter: [],
273
+ leave: [],
274
+ };
275
+
267
276
  for (const typeName of Object.keys(types) as Array<keyof T>) {
268
277
  normalizedVisitors[typeName] = {
269
278
  enter: [],
@@ -271,11 +280,6 @@ export function normalizeVisitors<T extends BaseVisitor>(
271
280
  } as any;
272
281
  }
273
282
 
274
- normalizedVisitors.any = {
275
- enter: [],
276
- leave: [],
277
- };
278
-
279
283
  normalizedVisitors.ref = {
280
284
  enter: [],
281
285
  leave: [],
@@ -376,7 +380,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
376
380
  }
377
381
 
378
382
  for (const typeName of visitorKeys as Array<keyof T>) {
379
- const typeVisitor = (visitor[typeName] as any) as NestedVisitObject<any, T>;
383
+ const typeVisitor = visitor[typeName] as any as NestedVisitObject<any, T>;
380
384
  const normalizedTypeVisitor = normalizedVisitors[typeName]!;
381
385
 
382
386
  if (!typeVisitor) continue;
package/src/walk.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Referenced } from './typings/openapi';
2
2
  import { Location, isRef } from './ref-utils';
3
-
4
3
  import {
5
4
  VisitorLevelContext,
6
5
  NormalizedOasVisitors,
@@ -10,9 +9,8 @@ import {
10
9
 
11
10
  import { ResolvedRefMap, Document, ResolveError, YamlParseError, Source } from './resolve';
12
11
  import { pushStack, popStack } from './utils';
13
- import { OasVersion } from './lint';
12
+ import { OasVersion } from './oas-types';
14
13
  import { NormalizedNodeType, isNamedType } from './types';
15
-
16
14
  type NonUndefined = string | number | boolean | symbol | bigint | object | Record<string, any>;
17
15
 
18
16
  export type ResolveResult<T extends NonUndefined> =
@@ -109,7 +107,6 @@ export function walkDocument<T>(opts: {
109
107
  ctx: WalkContext;
110
108
  }) {
111
109
  const { document, rootType, normalizedVisitors, resolvedRefMap, ctx } = opts;
112
-
113
110
  const seenNodesPerType: Record<string, Set<any>> = {};
114
111
  const seenRefs = new Set<any>();
115
112
 
@@ -124,7 +121,6 @@ export function walkDocument<T>(opts: {
124
121
  ) {
125
122
  let currentLocation = location;
126
123
  const { node: resolvedNode, location: resolvedLocation, error } = resolve(node);
127
-
128
124
  const enteredContexts: Set<VisitorLevelContext> = new Set();
129
125
 
130
126
  if (isRef(node)) {
@@ -209,7 +205,16 @@ export function walkDocument<T>(opts: {
209
205
  if (!activatedOn.skipped) {
210
206
  visitedBySome = true;
211
207
  enteredContexts.add(context);
212
- visitWithContext(visit, resolvedNode, context, ruleId, severity);
208
+ const ignoreNextVisitorsOnNode = visitWithContext(
209
+ visit,
210
+ resolvedNode,
211
+ context,
212
+ ruleId,
213
+ severity,
214
+ );
215
+ if (ignoreNextVisitorsOnNode) {
216
+ break;
217
+ }
213
218
  }
214
219
  }
215
220
  }
@@ -234,15 +239,16 @@ export function walkDocument<T>(opts: {
234
239
  }
235
240
 
236
241
  if (isRef(node)) {
237
- props.push(...Object.keys(node).filter((k) => k!== '$ref' && !props.includes(k))); // properties on the same level as $ref
242
+ props.push(...Object.keys(node).filter((k) => k !== '$ref' && !props.includes(k))); // properties on the same level as $ref
238
243
  }
239
244
 
240
245
  for (const propName of props) {
241
246
  let value = resolvedNode[propName];
247
+
242
248
  let loc = resolvedLocation;
243
249
 
244
250
  if (value === undefined) {
245
- value = node[propName];
251
+ value = node[propName];
246
252
  loc = location; // properties on the same level as $ref should resolve against original location, not target
247
253
  }
248
254
 
@@ -322,6 +328,7 @@ export function walkDocument<T>(opts: {
322
328
  }
323
329
  }
324
330
 
331
+ // returns true ignores all the next visitors on the specific node
325
332
  function visitWithContext(
326
333
  visit: VisitFunction<any>,
327
334
  node: any,
@@ -330,6 +337,8 @@ export function walkDocument<T>(opts: {
330
337
  severity: ProblemSeverity,
331
338
  ) {
332
339
  const report = reportFn.bind(undefined, ruleId, severity);
340
+ let ignoreNextVisitorsOnNode = false;
341
+
333
342
  visit(
334
343
  node,
335
344
  {
@@ -341,9 +350,15 @@ export function walkDocument<T>(opts: {
341
350
  key,
342
351
  parentLocations: collectParentsLocations(context),
343
352
  oasVersion: ctx.oasVersion,
353
+ ignoreNextVisitorsOnNode: () => {
354
+ ignoreNextVisitorsOnNode = true;
355
+ },
344
356
  },
345
357
  collectParents(context),
358
+ context,
346
359
  );
360
+
361
+ return ignoreNextVisitorsOnNode;
347
362
  }
348
363
 
349
364
  function resolve<T>(
@@ -354,6 +369,7 @@ export function walkDocument<T>(opts: {
354
369
  const refId = from + '::' + ref.$ref;
355
370
 
356
371
  const resolvedRef = resolvedRefMap.get(refId);
372
+
357
373
  if (!resolvedRef) {
358
374
  return {
359
375
  location: undefined,