@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.
- package/__tests__/normalizeVisitors.test.ts +1 -1
- package/__tests__/utils.ts +1 -1
- package/__tests__/walk.test.ts +77 -4
- package/lib/benchmark/benches/recommended-oas3.bench.js +2 -1
- package/lib/benchmark/utils.d.ts +1 -1
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +10 -10
- package/lib/config/builtIn.d.ts +2 -1
- package/lib/config/builtIn.js +9 -1
- package/lib/config/config.d.ts +5 -7
- package/lib/config/config.js +40 -100
- package/lib/config/load.d.ts +2 -0
- package/lib/config/load.js +65 -0
- package/lib/config/rules.d.ts +1 -1
- package/lib/format/codeframes.js +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.js +13 -11
- package/lib/lint.d.ts +6 -19
- package/lib/lint.js +13 -43
- package/lib/oas-types.d.ts +19 -0
- package/lib/oas-types.js +42 -0
- package/lib/resolve.d.ts +6 -2
- package/lib/resolve.js +18 -5
- package/lib/rules/ajv.d.ts +3 -3
- package/lib/rules/ajv.js +21 -18
- package/lib/rules/common/info-contact.js +2 -1
- package/lib/rules/common/info-description.js +2 -1
- package/lib/rules/common/info-license-url.js +2 -1
- package/lib/rules/common/license-url.js +2 -1
- package/lib/rules/common/no-ambiguous-paths.js +2 -1
- package/lib/rules/common/no-enum-type-mismatch.js +2 -1
- package/lib/rules/common/no-identical-paths.js +2 -1
- package/lib/rules/common/no-path-trailing-slash.js +2 -1
- package/lib/rules/common/operation-2xx-response.js +2 -1
- package/lib/rules/common/operation-description.js +2 -1
- package/lib/rules/common/operation-operationId-unique.js +2 -1
- package/lib/rules/common/operation-operationId-url-safe.js +2 -1
- package/lib/rules/common/operation-operationId.js +9 -4
- package/lib/rules/common/operation-parameters-unique.js +2 -1
- package/lib/rules/common/operation-security-defined.js +2 -1
- package/lib/rules/common/operation-singular-tag.js +2 -1
- package/lib/rules/common/operation-summary.js +2 -1
- package/lib/rules/common/operation-tag-defined.js +2 -1
- package/lib/rules/common/parameter-description.js +2 -1
- package/lib/rules/common/path-declaration-must-exist.js +2 -1
- package/lib/rules/common/path-http-verbs-order.js +2 -1
- package/lib/rules/common/path-not-include-query.js +2 -1
- package/lib/rules/common/path-params-defined.js +3 -2
- package/lib/rules/common/paths-kebab-case.js +2 -1
- package/lib/rules/common/registry-dependencies.js +2 -1
- package/lib/rules/common/spec.js +5 -2
- package/lib/rules/common/tag-description.js +2 -1
- package/lib/rules/common/tags-alphabetical.js +2 -1
- package/lib/rules/no-unresolved-refs.js +2 -1
- package/lib/rules/oas2/boolean-parameter-prefixes.js +2 -1
- package/lib/rules/oas2/index.d.ts +1 -1
- package/lib/rules/oas2/index.js +1 -1
- package/lib/rules/oas3/boolean-parameter-prefixes.js +2 -1
- package/lib/rules/oas3/index.d.ts +2 -1
- package/lib/rules/oas3/index.js +2 -2
- package/lib/rules/oas3/no-empty-servers.js +2 -1
- package/lib/rules/oas3/no-example-value-and-externalValue.js +2 -1
- package/lib/rules/oas3/no-invalid-media-type-examples.js +20 -10
- package/lib/rules/oas3/no-server-example.com.js +2 -1
- package/lib/rules/oas3/no-server-trailing-slash.js +2 -1
- package/lib/rules/oas3/no-servers-empty-enum.js +2 -1
- package/lib/rules/oas3/no-undefined-server-variable.js +2 -1
- package/lib/rules/oas3/no-unused-components.js +2 -1
- package/lib/rules/other/stats.js +2 -1
- package/lib/types/oas2.js +1 -1
- package/lib/types/oas3.js +1 -1
- package/lib/types/oas3_1.js +1 -1
- package/lib/utils.d.ts +1 -1
- package/lib/visitors.d.ts +3 -1
- package/lib/visitors.js +4 -4
- package/lib/walk.d.ts +1 -1
- package/lib/walk.js +11 -2
- package/package.json +7 -7
- package/src/__tests__/lint.test.ts +44 -0
- package/src/benchmark/benches/recommended-oas3.bench.ts +2 -1
- package/src/benchmark/benchmark.js +3 -1
- package/src/benchmark/utils.ts +1 -1
- package/src/bundle.ts +2 -2
- package/src/config/__tests__/resolve-plugins.test.ts +1 -1
- package/src/config/builtIn.ts +11 -1
- package/src/config/config.ts +30 -78
- package/src/config/load.ts +60 -0
- package/src/config/rules.ts +1 -1
- package/src/format/codeframes.ts +1 -1
- package/src/index.ts +4 -3
- package/src/lint.ts +19 -56
- package/src/oas-types.ts +58 -0
- package/src/resolve.ts +17 -4
- package/src/rules/__tests__/config.ts +10 -0
- package/src/rules/__tests__/no-unresolved-refs.test.ts +9 -21
- package/src/rules/ajv.ts +27 -23
- package/src/rules/common/__tests__/info-description.test.ts +8 -16
- package/src/rules/common/__tests__/info-license.test.ts +3 -3
- package/src/rules/common/__tests__/license-url.test.ts +3 -3
- package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +2 -2
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +50 -5
- package/src/rules/common/__tests__/no-identical-paths.test.ts +2 -2
- package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +4 -4
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +4 -4
- package/src/rules/common/__tests__/operation-operationId-unique.test.ts +3 -3
- package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +2 -5
- package/src/rules/common/__tests__/operation-parameters-unique.test.ts +5 -17
- package/src/rules/common/__tests__/operation-security-defined.test.ts +3 -3
- package/src/rules/common/__tests__/operation-singular-tag.test.ts +3 -3
- package/src/rules/common/__tests__/path-http-verbs-order.test.ts +3 -3
- package/src/rules/common/__tests__/path-not-include-query.test.ts +3 -3
- package/src/rules/common/__tests__/path-params-defined.test.ts +4 -4
- package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
- package/src/rules/common/__tests__/tag-description.test.ts +3 -3
- package/src/rules/common/__tests__/tags-alphabetical.test.ts +3 -3
- package/src/rules/common/operation-operationId.ts +7 -3
- package/src/rules/common/spec.ts +4 -1
- package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +7 -10
- package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +3 -6
- package/src/rules/oas2/__tests__/spec/utils.ts +2 -0
- package/src/rules/oas2/index.ts +1 -10
- package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +7 -10
- package/src/rules/oas3/__tests__/fixtures/common.yaml +11 -0
- package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +7 -7
- package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +3 -9
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +103 -29
- package/src/rules/oas3/__tests__/no-server-example.com.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-unused-components.test.ts +2 -2
- package/src/rules/oas3/__tests__/spec/spec.test.ts +3 -1
- package/src/rules/oas3/__tests__/spec/utils.ts +2 -0
- package/src/rules/oas3/index.ts +3 -4
- package/src/rules/oas3/no-invalid-media-type-examples.ts +27 -19
- package/src/visitors.ts +12 -8
- package/src/walk.ts +24 -8
- 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:
|
|
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
|
|
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
|
|
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:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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:
|
|
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:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
}),
|
package/src/rules/oas3/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Oas3RuleSet } from '../../
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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> = (
|
|
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 =
|
|
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 './
|
|
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
|
-
|
|
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 =
|
|
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,
|