@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
|
@@ -3,12 +3,14 @@ lint:
|
|
|
3
3
|
no-invalid-media-type-examples: warn
|
|
4
4
|
operation-4xx-response: off
|
|
5
5
|
assert/tag-description:
|
|
6
|
-
subject:
|
|
7
|
-
|
|
6
|
+
subject:
|
|
7
|
+
type: Tag
|
|
8
|
+
property: description
|
|
8
9
|
message: Tag description must have at least 3 words.
|
|
9
10
|
severity: error
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
assertions:
|
|
12
|
+
test-plugin/checkWordsCount:
|
|
13
|
+
min: 3
|
|
12
14
|
plugins:
|
|
13
15
|
- plugin.js
|
|
14
16
|
extends:
|
|
@@ -49,7 +49,10 @@ describe('loadConfig', () => {
|
|
|
49
49
|
|
|
50
50
|
it('should call callback if such passed', async () => {
|
|
51
51
|
const mockFn = jest.fn();
|
|
52
|
-
await loadConfig({
|
|
52
|
+
await loadConfig({
|
|
53
|
+
configPath: path.join(__dirname, './fixtures/load-redocly.yaml'),
|
|
54
|
+
processRawConfig: mockFn,
|
|
55
|
+
});
|
|
53
56
|
expect(mockFn).toHaveBeenCalled();
|
|
54
57
|
});
|
|
55
58
|
});
|
package/src/config/all.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import { isAbsoluteUrl } from '../ref-utils';
|
|
3
|
+
import { pickDefined } from '../utils';
|
|
3
4
|
import { BaseResolver } from '../resolve';
|
|
4
5
|
import { defaultPlugin } from './builtIn';
|
|
5
6
|
import {
|
|
@@ -21,10 +22,16 @@ import type {
|
|
|
21
22
|
DeprecatedInRawConfig,
|
|
22
23
|
} from './types';
|
|
23
24
|
import { isBrowser } from '../env';
|
|
24
|
-
import { isNotString, isString, isDefined, parseYaml } from '../utils';
|
|
25
|
+
import { isNotString, isString, isDefined, parseYaml, keysOf } from '../utils';
|
|
25
26
|
import { Config } from './config';
|
|
26
27
|
import { colorize, logger } from '../logger';
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
Asserts,
|
|
30
|
+
AssertionFn,
|
|
31
|
+
asserts,
|
|
32
|
+
buildAssertCustomFunction,
|
|
33
|
+
} from '../rules/common/assertions/asserts';
|
|
34
|
+
import type { Assertion, AssertionDefinition, RawAssertion } from '../rules/common/assertions';
|
|
28
35
|
|
|
29
36
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
30
37
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
|
@@ -355,7 +362,7 @@ function getMergedRawStyleguideConfig(
|
|
|
355
362
|
) {
|
|
356
363
|
const resultLint = {
|
|
357
364
|
...rootStyleguideConfig,
|
|
358
|
-
...apiStyleguideConfig,
|
|
365
|
+
...pickDefined(apiStyleguideConfig),
|
|
359
366
|
rules: { ...rootStyleguideConfig?.rules, ...apiStyleguideConfig?.rules },
|
|
360
367
|
oas2Rules: { ...rootStyleguideConfig?.oas2Rules, ...apiStyleguideConfig?.oas2Rules },
|
|
361
368
|
oas3_0Rules: { ...rootStyleguideConfig?.oas3_0Rules, ...apiStyleguideConfig?.oas3_0Rules },
|
|
@@ -408,26 +415,17 @@ function groupStyleguideAssertionRules({
|
|
|
408
415
|
const transformedRules: Record<string, RuleConfig> = {};
|
|
409
416
|
|
|
410
417
|
// Collect assertion rules
|
|
411
|
-
const assertions = [];
|
|
418
|
+
const assertions: Assertion[] = [];
|
|
412
419
|
for (const [ruleKey, rule] of Object.entries(rules)) {
|
|
413
420
|
if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
|
|
414
|
-
const assertion = rule;
|
|
421
|
+
const assertion = rule as RawAssertion;
|
|
422
|
+
|
|
415
423
|
if (plugins) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
throw Error(colorize.red(`Plugin ${colorize.blue(pluginId)} isn't found.`));
|
|
422
|
-
}
|
|
423
|
-
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
424
|
-
throw Error(
|
|
425
|
-
`Plugin ${colorize.red(
|
|
426
|
-
pluginId
|
|
427
|
-
)} doesn't export assertions function with name ${colorize.red(fn)}.`
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
asserts[field] = buildAssertCustomFunction(plugin.assertions[fn]);
|
|
424
|
+
registerCustomAssertions(plugins, assertion);
|
|
425
|
+
|
|
426
|
+
// We may have custom assertion inside where block
|
|
427
|
+
for (const context of assertion.where || []) {
|
|
428
|
+
registerCustomAssertions(plugins, context);
|
|
431
429
|
}
|
|
432
430
|
}
|
|
433
431
|
assertions.push({
|
|
@@ -445,3 +443,29 @@ function groupStyleguideAssertionRules({
|
|
|
445
443
|
|
|
446
444
|
return transformedRules;
|
|
447
445
|
}
|
|
446
|
+
|
|
447
|
+
function registerCustomAssertions(plugins: Plugin[], assertion: AssertionDefinition) {
|
|
448
|
+
for (const field of keysOf(assertion.assertions)) {
|
|
449
|
+
const [pluginId, fn] = field.split('/');
|
|
450
|
+
|
|
451
|
+
if (!pluginId || !fn) continue;
|
|
452
|
+
|
|
453
|
+
const plugin = plugins.find((plugin) => plugin.id === pluginId);
|
|
454
|
+
|
|
455
|
+
if (!plugin) {
|
|
456
|
+
throw Error(colorize.red(`Plugin ${colorize.blue(pluginId)} isn't found.`));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (!plugin.assertions || !plugin.assertions[fn]) {
|
|
460
|
+
throw Error(
|
|
461
|
+
`Plugin ${colorize.red(
|
|
462
|
+
pluginId
|
|
463
|
+
)} doesn't export assertions function with name ${colorize.red(fn)}.`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
(asserts as Asserts & { [name: string]: AssertionFn })[field] = buildAssertCustomFunction(
|
|
468
|
+
plugin.assertions[fn]
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
package/src/config/load.ts
CHANGED
|
@@ -71,11 +71,8 @@ export async function loadConfig(
|
|
|
71
71
|
} = {}
|
|
72
72
|
): Promise<Config> {
|
|
73
73
|
const { configPath = findConfig(), customExtends, processRawConfig, files, region } = options;
|
|
74
|
-
const config = await getConfig(configPath);
|
|
74
|
+
const config = await getConfig(configPath, processRawConfig);
|
|
75
75
|
const rawConfig = { ...config, files: files ?? config.files, region: region ?? config.region };
|
|
76
|
-
if (typeof processRawConfig === 'function') {
|
|
77
|
-
await processRawConfig(rawConfig);
|
|
78
|
-
}
|
|
79
76
|
|
|
80
77
|
const redoclyClient = new RedoclyClient();
|
|
81
78
|
const tokens = await redoclyClient.getTokens();
|
|
@@ -105,11 +102,17 @@ export function findConfig(dir?: string): string | undefined {
|
|
|
105
102
|
return existingConfigFiles[0];
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
export async function getConfig(
|
|
105
|
+
export async function getConfig(
|
|
106
|
+
configPath: string | undefined = findConfig(),
|
|
107
|
+
processRawConfig?: (rawConfig: RawConfig) => void | Promise<void>
|
|
108
|
+
): Promise<RawConfig> {
|
|
109
109
|
if (!configPath || !doesYamlFileExist(configPath)) return {};
|
|
110
110
|
try {
|
|
111
111
|
const rawConfig =
|
|
112
112
|
(await loadYaml<RawConfig & DeprecatedInRawConfig & FlatRawConfig>(configPath)) || {};
|
|
113
|
+
if (typeof processRawConfig === 'function') {
|
|
114
|
+
await processRawConfig(rawConfig);
|
|
115
|
+
}
|
|
113
116
|
return transformConfig(rawConfig);
|
|
114
117
|
} catch (e) {
|
|
115
118
|
throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
|
package/src/config/minimal.ts
CHANGED
|
@@ -88,4 +88,41 @@ describe('Oas3 operation-2xx-response', () => {
|
|
|
88
88
|
|
|
89
89
|
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
it('should report even if the responses are null', async () => {
|
|
93
|
+
const document = parseYamlToDocument(
|
|
94
|
+
outdent`
|
|
95
|
+
openapi: 3.0.0
|
|
96
|
+
paths:
|
|
97
|
+
'/test/':
|
|
98
|
+
put:
|
|
99
|
+
responses: null
|
|
100
|
+
`,
|
|
101
|
+
'foobar.yaml'
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const results = await lintDocument({
|
|
105
|
+
externalRefResolver: new BaseResolver(),
|
|
106
|
+
document,
|
|
107
|
+
config: await makeConfig({ 'operation-2xx-response': 'error' }),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
111
|
+
Array [
|
|
112
|
+
Object {
|
|
113
|
+
"location": Array [
|
|
114
|
+
Object {
|
|
115
|
+
"pointer": "#/paths/~1test~1/put/responses",
|
|
116
|
+
"reportOnKey": true,
|
|
117
|
+
"source": "foobar.yaml",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
"message": "Operation must have at least one \`2XX\` response.",
|
|
121
|
+
"ruleId": "operation-2xx-response",
|
|
122
|
+
"severity": "error",
|
|
123
|
+
"suggest": Array [],
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
`);
|
|
127
|
+
});
|
|
91
128
|
});
|
|
@@ -127,4 +127,41 @@ describe('Oas3 operation-4xx-response', () => {
|
|
|
127
127
|
]
|
|
128
128
|
`);
|
|
129
129
|
});
|
|
130
|
+
|
|
131
|
+
it('should report even if the responses are null', async () => {
|
|
132
|
+
const document = parseYamlToDocument(
|
|
133
|
+
outdent`
|
|
134
|
+
openapi: 3.0.0
|
|
135
|
+
paths:
|
|
136
|
+
'/test/':
|
|
137
|
+
put:
|
|
138
|
+
responses: null
|
|
139
|
+
`,
|
|
140
|
+
'foobar.yaml'
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const results = await lintDocument({
|
|
144
|
+
externalRefResolver: new BaseResolver(),
|
|
145
|
+
document,
|
|
146
|
+
config: await makeConfig({ 'operation-2xx-response': 'error' }),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
150
|
+
Array [
|
|
151
|
+
Object {
|
|
152
|
+
"location": Array [
|
|
153
|
+
Object {
|
|
154
|
+
"pointer": "#/paths/~1test~1/put/responses",
|
|
155
|
+
"reportOnKey": true,
|
|
156
|
+
"source": "foobar.yaml",
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
"message": "Operation must have at least one \`2XX\` response.",
|
|
160
|
+
"ruleId": "operation-2xx-response",
|
|
161
|
+
"severity": "error",
|
|
162
|
+
"suggest": Array [],
|
|
163
|
+
},
|
|
164
|
+
]
|
|
165
|
+
`);
|
|
166
|
+
});
|
|
130
167
|
});
|
|
@@ -130,4 +130,73 @@ describe('Oas3 path-params-defined', () => {
|
|
|
130
130
|
]
|
|
131
131
|
`);
|
|
132
132
|
});
|
|
133
|
+
|
|
134
|
+
it('should fail cause POST has no parameters', async () => {
|
|
135
|
+
const document = parseYamlToDocument(
|
|
136
|
+
outdent`
|
|
137
|
+
openapi: 3.0.0
|
|
138
|
+
paths:
|
|
139
|
+
/pets/{a}:
|
|
140
|
+
get:
|
|
141
|
+
parameters:
|
|
142
|
+
- name: a
|
|
143
|
+
in: path
|
|
144
|
+
post:
|
|
145
|
+
description: without parameters
|
|
146
|
+
`,
|
|
147
|
+
'foobar.yaml'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const results = await lintDocument({
|
|
151
|
+
externalRefResolver: new BaseResolver(),
|
|
152
|
+
document,
|
|
153
|
+
config: await makeConfig({ 'path-params-defined': 'error' }),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
157
|
+
Array [
|
|
158
|
+
Object {
|
|
159
|
+
"location": Array [
|
|
160
|
+
Object {
|
|
161
|
+
"pointer": "#/paths/~1pets~1{a}/post/parameters",
|
|
162
|
+
"reportOnKey": true,
|
|
163
|
+
"source": "foobar.yaml",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
"message": "The operation does not define the path parameter \`{a}\` expected by path \`/pets/{a}\`.",
|
|
167
|
+
"ruleId": "path-params-defined",
|
|
168
|
+
"severity": "error",
|
|
169
|
+
"suggest": Array [],
|
|
170
|
+
},
|
|
171
|
+
]
|
|
172
|
+
`);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should apply parameters for POST operation from path parameters', async () => {
|
|
176
|
+
const document = parseYamlToDocument(
|
|
177
|
+
outdent`
|
|
178
|
+
openapi: 3.0.0
|
|
179
|
+
paths:
|
|
180
|
+
/pets/{a}:
|
|
181
|
+
parameters:
|
|
182
|
+
- name: a
|
|
183
|
+
in: path
|
|
184
|
+
get:
|
|
185
|
+
parameters:
|
|
186
|
+
- name: a
|
|
187
|
+
in: path
|
|
188
|
+
post:
|
|
189
|
+
description: without parameters
|
|
190
|
+
`,
|
|
191
|
+
'foobar.yaml'
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const results = await lintDocument({
|
|
195
|
+
externalRefResolver: new BaseResolver(),
|
|
196
|
+
document,
|
|
197
|
+
config: await makeConfig({ 'path-params-defined': 'error' }),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
201
|
+
});
|
|
133
202
|
});
|
|
@@ -81,12 +81,12 @@ describe('Oas3 security-defined', () => {
|
|
|
81
81
|
Object {
|
|
82
82
|
"location": Array [
|
|
83
83
|
Object {
|
|
84
|
-
"pointer": "#/",
|
|
85
|
-
"reportOnKey":
|
|
84
|
+
"pointer": "#/paths/~1pets/get",
|
|
85
|
+
"reportOnKey": true,
|
|
86
86
|
"source": "foobar.yaml",
|
|
87
87
|
},
|
|
88
88
|
],
|
|
89
|
-
"message": "Every
|
|
89
|
+
"message": "Every operation should have security defined on it or on the root level.",
|
|
90
90
|
"ruleId": "security-defined",
|
|
91
91
|
"severity": "error",
|
|
92
92
|
"suggest": Array [],
|
|
@@ -133,12 +133,12 @@ describe('Oas3 security-defined', () => {
|
|
|
133
133
|
Object {
|
|
134
134
|
"location": Array [
|
|
135
135
|
Object {
|
|
136
|
-
"pointer": "#/",
|
|
137
|
-
"reportOnKey":
|
|
136
|
+
"pointer": "#/paths/~1cats/get",
|
|
137
|
+
"reportOnKey": true,
|
|
138
138
|
"source": "foobar.yaml",
|
|
139
139
|
},
|
|
140
140
|
],
|
|
141
|
-
"message": "Every
|
|
141
|
+
"message": "Every operation should have security defined on it or on the root level.",
|
|
142
142
|
"ruleId": "security-defined",
|
|
143
143
|
"severity": "error",
|
|
144
144
|
"suggest": Array [],
|
|
@@ -139,3 +139,128 @@ describe('Oas3 spec', () => {
|
|
|
139
139
|
`);
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
describe('Oas3.1 spec', () => {
|
|
144
|
+
it('should report with "type can be one of the following only"', async () => {
|
|
145
|
+
const document = parseYamlToDocument(
|
|
146
|
+
outdent`
|
|
147
|
+
openapi: 3.1.0
|
|
148
|
+
info:
|
|
149
|
+
version: 1.0.0
|
|
150
|
+
title: Example.com
|
|
151
|
+
description: info,
|
|
152
|
+
license:
|
|
153
|
+
name: Apache 2.0
|
|
154
|
+
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
|
|
155
|
+
components:
|
|
156
|
+
schemas:
|
|
157
|
+
TestSchema:
|
|
158
|
+
title: TestSchema
|
|
159
|
+
description: Property name's description
|
|
160
|
+
type: test
|
|
161
|
+
`
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const results = await lintDocument({
|
|
165
|
+
externalRefResolver: new BaseResolver(),
|
|
166
|
+
document,
|
|
167
|
+
config: await makeConfig({ spec: 'error' }),
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
171
|
+
Array [
|
|
172
|
+
Object {
|
|
173
|
+
"from": undefined,
|
|
174
|
+
"location": Array [
|
|
175
|
+
Object {
|
|
176
|
+
"pointer": "#/components/schemas/TestSchema/type",
|
|
177
|
+
"reportOnKey": false,
|
|
178
|
+
"source": "",
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
"message": "\`type\` can be one of the following only: \\"object\\", \\"array\\", \\"string\\", \\"number\\", \\"integer\\", \\"boolean\\", \\"null\\".",
|
|
182
|
+
"ruleId": "spec",
|
|
183
|
+
"severity": "error",
|
|
184
|
+
"suggest": Array [],
|
|
185
|
+
},
|
|
186
|
+
]
|
|
187
|
+
`);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should report with unknown type in type`s list', async () => {
|
|
191
|
+
const document = parseYamlToDocument(
|
|
192
|
+
outdent`
|
|
193
|
+
openapi: 3.1.0
|
|
194
|
+
info:
|
|
195
|
+
version: 1.0.0
|
|
196
|
+
title: Example.com
|
|
197
|
+
description: info,
|
|
198
|
+
license:
|
|
199
|
+
name: Apache 2.0
|
|
200
|
+
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
|
|
201
|
+
components:
|
|
202
|
+
schemas:
|
|
203
|
+
TestSchema:
|
|
204
|
+
title: TestSchema
|
|
205
|
+
description: Property name's description
|
|
206
|
+
type:
|
|
207
|
+
- string
|
|
208
|
+
- foo
|
|
209
|
+
`
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const results = await lintDocument({
|
|
213
|
+
externalRefResolver: new BaseResolver(),
|
|
214
|
+
document,
|
|
215
|
+
config: await makeConfig({ spec: 'error' }),
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
219
|
+
Array [
|
|
220
|
+
Object {
|
|
221
|
+
"from": undefined,
|
|
222
|
+
"location": Array [
|
|
223
|
+
Object {
|
|
224
|
+
"pointer": "#/components/schemas/TestSchema/type/1",
|
|
225
|
+
"reportOnKey": false,
|
|
226
|
+
"source": "",
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
"message": "\`type\` can be one of the following only: \\"object\\", \\"array\\", \\"string\\", \\"number\\", \\"integer\\", \\"boolean\\", \\"null\\".",
|
|
230
|
+
"ruleId": "spec",
|
|
231
|
+
"severity": "error",
|
|
232
|
+
"suggest": Array [],
|
|
233
|
+
},
|
|
234
|
+
]
|
|
235
|
+
`);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should not report about unknown type', async () => {
|
|
239
|
+
const document = parseYamlToDocument(
|
|
240
|
+
outdent`
|
|
241
|
+
openapi: 3.1.0
|
|
242
|
+
info:
|
|
243
|
+
version: 1.0.0
|
|
244
|
+
title: Example.com
|
|
245
|
+
description: info,
|
|
246
|
+
license:
|
|
247
|
+
name: Apache 2.0
|
|
248
|
+
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
|
|
249
|
+
components:
|
|
250
|
+
schemas:
|
|
251
|
+
TestSchema:
|
|
252
|
+
title: TestSchema
|
|
253
|
+
description: Property name's description
|
|
254
|
+
type: null
|
|
255
|
+
`
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const results = await lintDocument({
|
|
259
|
+
externalRefResolver: new BaseResolver(),
|
|
260
|
+
document,
|
|
261
|
+
config: await makeConfig({ spec: 'error' }),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Location } from '../../../../ref-utils';
|
|
2
2
|
import { Source } from '../../../../resolve';
|
|
3
|
-
import { asserts, buildAssertCustomFunction } from '../asserts';
|
|
3
|
+
import { Asserts, asserts, buildAssertCustomFunction } from '../asserts';
|
|
4
4
|
|
|
5
5
|
let baseLocation = new Location(jest.fn() as any as Source, 'pointer');
|
|
6
6
|
|
|
@@ -674,9 +674,13 @@ describe('oas3 assertions', () => {
|
|
|
674
674
|
}
|
|
675
675
|
return [];
|
|
676
676
|
});
|
|
677
|
-
asserts['local/customFn'] = buildAssertCustomFunction(customFn);
|
|
677
|
+
asserts['local/customFn' as keyof Asserts] = buildAssertCustomFunction(customFn);
|
|
678
678
|
expect(
|
|
679
|
-
asserts['local/customFn'
|
|
679
|
+
asserts['local/customFn' as keyof Asserts](
|
|
680
|
+
Object.keys(fakeNode),
|
|
681
|
+
{ word: 'foo' },
|
|
682
|
+
baseLocation
|
|
683
|
+
)
|
|
680
684
|
).toEqual([
|
|
681
685
|
{
|
|
682
686
|
message: 'First value should be foo',
|
|
@@ -1,61 +1,82 @@
|
|
|
1
|
-
import { Assertions } from '../.';
|
|
1
|
+
import { Assertion, Assertions } from '../.';
|
|
2
2
|
|
|
3
3
|
const opts = {
|
|
4
4
|
'0': {
|
|
5
|
-
subject:
|
|
6
|
-
|
|
5
|
+
subject: {
|
|
6
|
+
type: 'Operation',
|
|
7
|
+
property: 'summary',
|
|
8
|
+
},
|
|
7
9
|
description: 'example warn text',
|
|
8
10
|
severity: 'warn',
|
|
9
|
-
pattern: '/example/',
|
|
11
|
+
assertions: { pattern: '/example/' },
|
|
10
12
|
},
|
|
11
13
|
'1': {
|
|
12
|
-
subject:
|
|
13
|
-
|
|
14
|
+
subject: {
|
|
15
|
+
type: 'PathItem',
|
|
16
|
+
},
|
|
17
|
+
where: [
|
|
18
|
+
{
|
|
19
|
+
subject: { type: 'Operation', filterInParentKeys: ['post'], property: 'responses' },
|
|
20
|
+
assertions: { defined: true },
|
|
21
|
+
},
|
|
22
|
+
],
|
|
14
23
|
description: 'example warn text',
|
|
15
24
|
severity: 'warn',
|
|
16
|
-
mutuallyExclusive: ['summary', 'security'],
|
|
25
|
+
assertions: { mutuallyExclusive: ['summary', 'security'] },
|
|
17
26
|
},
|
|
18
27
|
'2': {
|
|
19
|
-
subject:
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
subject: { type: 'PathItem', property: 'tags' },
|
|
29
|
+
where: [
|
|
30
|
+
{ subject: { type: 'Operation', property: 'responses' }, assertions: { defined: true } },
|
|
31
|
+
],
|
|
22
32
|
description: 'example warn text',
|
|
23
33
|
severity: 'warn',
|
|
24
|
-
sortOrder: 'desc',
|
|
34
|
+
assertions: { sortOrder: 'desc' },
|
|
25
35
|
},
|
|
26
36
|
'3': {
|
|
27
|
-
subject:
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
subject: { type: 'Foo', property: 'test' },
|
|
38
|
+
where: [
|
|
39
|
+
{ subject: { type: 'Bar' }, assertions: {} },
|
|
40
|
+
{ subject: { type: 'Baz' }, assertions: {} },
|
|
41
|
+
],
|
|
30
42
|
description: 'example warn text',
|
|
31
43
|
severity: 'warn',
|
|
32
|
-
sortOrder: 'desc',
|
|
44
|
+
assertions: { sortOrder: 'desc' },
|
|
33
45
|
},
|
|
34
46
|
};
|
|
35
47
|
|
|
36
48
|
describe('Oas3 assertions', () => {
|
|
37
49
|
it('should return the right visitor structure', () => {
|
|
38
|
-
const visitors = Assertions(opts
|
|
50
|
+
const visitors = Assertions(opts as any);
|
|
39
51
|
expect(visitors).toMatchInlineSnapshot(`
|
|
40
52
|
Array [
|
|
41
53
|
Object {
|
|
42
|
-
"Operation":
|
|
54
|
+
"Operation": Object {
|
|
55
|
+
"enter": [Function],
|
|
56
|
+
},
|
|
43
57
|
},
|
|
44
58
|
Object {
|
|
45
59
|
"Operation": Object {
|
|
46
|
-
"PathItem":
|
|
60
|
+
"PathItem": Object {
|
|
61
|
+
"enter": [Function],
|
|
62
|
+
},
|
|
47
63
|
"skip": [Function],
|
|
48
64
|
},
|
|
49
65
|
},
|
|
50
66
|
Object {
|
|
51
67
|
"Operation": Object {
|
|
52
|
-
"PathItem":
|
|
68
|
+
"PathItem": Object {
|
|
69
|
+
"enter": [Function],
|
|
70
|
+
},
|
|
71
|
+
"skip": [Function],
|
|
53
72
|
},
|
|
54
73
|
},
|
|
55
74
|
Object {
|
|
56
75
|
"Bar": Object {
|
|
57
76
|
"Baz": Object {
|
|
58
|
-
"Foo":
|
|
77
|
+
"Foo": Object {
|
|
78
|
+
"enter": [Function],
|
|
79
|
+
},
|
|
59
80
|
},
|
|
60
81
|
},
|
|
61
82
|
},
|