@redocly/openapi-core 1.1.0 → 1.2.1
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/CHANGELOG.md +20 -0
- package/README.md +250 -7
- package/lib/bundle.d.ts +12 -6
- package/lib/bundle.js +34 -24
- package/lib/config/builtIn.js +4 -0
- package/lib/config/config-resolvers.js +19 -6
- package/lib/config/config.d.ts +9 -9
- package/lib/config/config.js +32 -17
- package/lib/config/load.d.ts +3 -2
- package/lib/config/load.js +2 -2
- package/lib/config/rules.d.ts +2 -2
- package/lib/config/types.d.ts +10 -3
- package/lib/index.d.ts +4 -3
- package/lib/index.js +10 -6
- package/lib/lint.js +10 -16
- package/lib/oas-types.d.ts +16 -10
- package/lib/oas-types.js +52 -26
- package/lib/rules/async2/channels-kebab-case.d.ts +2 -0
- package/lib/rules/async2/channels-kebab-case.js +19 -0
- package/lib/rules/async2/index.d.ts +12 -0
- package/lib/rules/async2/index.js +22 -0
- package/lib/rules/async2/no-channel-trailing-slash.d.ts +2 -0
- package/lib/rules/async2/no-channel-trailing-slash.js +16 -0
- package/lib/rules/common/assertions/asserts.js +12 -4
- package/lib/rules/common/scalar-property-missing-example.js +1 -1
- package/lib/rules/common/spec.d.ts +2 -2
- package/lib/rules/common/spec.js +3 -3
- package/lib/rules/oas2/index.js +1 -1
- package/lib/rules/oas3/index.js +1 -1
- package/lib/rules/oas3/no-server-example.com.js +3 -2
- package/lib/types/asyncapi.d.ts +2 -0
- package/lib/types/asyncapi.js +1034 -0
- package/lib/types/oas3_1.js +8 -1
- package/lib/types/redocly-yaml.js +3 -0
- package/lib/typings/asyncapi.d.ts +21 -0
- package/lib/typings/asyncapi.js +2 -0
- package/lib/visitors.d.ts +12 -0
- package/lib/walk.d.ts +3 -3
- package/package.json +4 -2
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +1 -1
- package/src/__tests__/bundle.test.ts +65 -25
- package/src/__tests__/fixtures/lint/openapi.yaml +10 -0
- package/src/__tests__/fixtures/redocly.yaml +2 -0
- package/src/__tests__/lint.test.ts +67 -8
- package/src/bundle.ts +62 -35
- package/src/config/__tests__/__snapshots__/config.test.ts.snap +24 -0
- package/src/config/__tests__/config.test.ts +15 -4
- package/src/config/__tests__/load.test.ts +35 -2
- package/src/config/builtIn.ts +4 -0
- package/src/config/config-resolvers.ts +25 -6
- package/src/config/config.ts +51 -27
- package/src/config/load.ts +11 -4
- package/src/config/rules.ts +2 -2
- package/src/config/types.ts +17 -4
- package/src/index.ts +10 -2
- package/src/lint.ts +13 -22
- package/src/oas-types.ts +59 -21
- package/src/rules/async2/__tests__/channels-kebab-case.test.ts +141 -0
- package/src/rules/async2/__tests__/no-channel-trailing-slash.test.ts +97 -0
- package/src/rules/async2/channels-kebab-case.ts +18 -0
- package/src/rules/async2/index.ts +22 -0
- package/src/rules/async2/no-channel-trailing-slash.ts +15 -0
- package/src/rules/common/assertions/asserts.ts +16 -4
- package/src/rules/common/scalar-property-missing-example.ts +2 -2
- package/src/rules/common/spec.ts +2 -2
- package/src/rules/oas2/index.ts +2 -2
- package/src/rules/oas3/__tests__/no-server-example.com.test.ts +36 -1
- package/src/rules/oas3/__tests__/spec/spec.test.ts +1 -1
- package/src/rules/oas3/index.ts +2 -2
- package/src/rules/oas3/no-server-example.com.ts +3 -2
- package/src/types/asyncapi.ts +1142 -0
- package/src/types/oas3_1.ts +7 -1
- package/src/types/redocly-yaml.ts +3 -0
- package/src/typings/asyncapi.ts +26 -0
- package/src/visitors.ts +22 -0
- package/src/walk.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
package/lib/types/oas3_1.js
CHANGED
|
@@ -135,7 +135,14 @@ const Schema = {
|
|
|
135
135
|
maxContains: { type: 'integer', minimum: 0 },
|
|
136
136
|
patternProperties: { type: 'object' },
|
|
137
137
|
propertyNames: 'Schema',
|
|
138
|
-
unevaluatedItems:
|
|
138
|
+
unevaluatedItems: (value) => {
|
|
139
|
+
if (typeof value === 'boolean') {
|
|
140
|
+
return { type: 'boolean' };
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
return 'Schema';
|
|
144
|
+
}
|
|
145
|
+
},
|
|
139
146
|
unevaluatedProperties: (value) => {
|
|
140
147
|
if (typeof value === 'boolean') {
|
|
141
148
|
return { type: 'boolean' };
|
|
@@ -61,6 +61,8 @@ const builtInRulesList = [
|
|
|
61
61
|
'required-string-property-missing-min-length',
|
|
62
62
|
'spec-strict-refs',
|
|
63
63
|
'component-name-unique',
|
|
64
|
+
'channels-kebab-case',
|
|
65
|
+
'no-channel-trailing-slash',
|
|
64
66
|
];
|
|
65
67
|
const nodeTypesList = [
|
|
66
68
|
'any',
|
|
@@ -122,6 +124,7 @@ const nodeTypesList = [
|
|
|
122
124
|
'XCodeSampleList',
|
|
123
125
|
'WebhooksMap',
|
|
124
126
|
'SpecExtension',
|
|
127
|
+
'Message',
|
|
125
128
|
];
|
|
126
129
|
const ConfigStyleguide = {
|
|
127
130
|
properties: {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Async2Definition {
|
|
2
|
+
asyncapi: string;
|
|
3
|
+
info?: Async2Info;
|
|
4
|
+
}
|
|
5
|
+
export interface Async2Info {
|
|
6
|
+
title: string;
|
|
7
|
+
version: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
termsOfService?: string;
|
|
10
|
+
contact?: Async2Contact;
|
|
11
|
+
license?: Async2License;
|
|
12
|
+
}
|
|
13
|
+
export interface Async2Contact {
|
|
14
|
+
name?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Async2License {
|
|
19
|
+
name: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
}
|
package/lib/visitors.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { NormalizedNodeType } from './types';
|
|
|
4
4
|
import type { Stack } from './utils';
|
|
5
5
|
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
6
6
|
import type { Location } from './ref-utils';
|
|
7
|
+
import { Async2Definition } from './typings/asyncapi';
|
|
7
8
|
export declare type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
|
|
8
9
|
export declare type VisitFunction<T> = (node: T, ctx: UserContext & {
|
|
9
10
|
ignoreNextVisitorsOnNode: () => void;
|
|
@@ -134,16 +135,24 @@ declare type Oas2FlatVisitor = {
|
|
|
134
135
|
SecurityScheme?: VisitFunctionOrObject<Oas2SecurityScheme>;
|
|
135
136
|
SpecExtension?: VisitFunctionOrObject<unknown>;
|
|
136
137
|
};
|
|
138
|
+
declare type Async2FlatVisitor = {
|
|
139
|
+
Root?: VisitFunctionOrObject<Async2Definition>;
|
|
140
|
+
};
|
|
137
141
|
declare type Oas3NestedVisitor = {
|
|
138
142
|
[T in keyof Oas3FlatVisitor]: Oas3FlatVisitor[T] extends Function ? Oas3FlatVisitor[T] : Oas3FlatVisitor[T] & NestedVisitor<Oas3NestedVisitor>;
|
|
139
143
|
};
|
|
140
144
|
declare type Oas2NestedVisitor = {
|
|
141
145
|
[T in keyof Oas2FlatVisitor]: Oas2FlatVisitor[T] extends Function ? Oas2FlatVisitor[T] : Oas2FlatVisitor[T] & NestedVisitor<Oas2NestedVisitor>;
|
|
142
146
|
};
|
|
147
|
+
declare type Async2NestedVisitor = {
|
|
148
|
+
[T in keyof Async2FlatVisitor]: Async2FlatVisitor[T] extends Function ? Async2FlatVisitor[T] : Async2FlatVisitor[T] & NestedVisitor<Async2NestedVisitor>;
|
|
149
|
+
};
|
|
143
150
|
export declare type Oas3Visitor = BaseVisitor & Oas3NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Oas3NestedVisitor>>;
|
|
144
151
|
export declare type Oas2Visitor = BaseVisitor & Oas2NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Oas2NestedVisitor>>;
|
|
152
|
+
export declare type Async2Visitor = BaseVisitor & Async2NestedVisitor & Record<string, VisitFunction<any> | NestedVisitObject<any, Async2NestedVisitor>>;
|
|
145
153
|
export declare type Oas3TransformVisitor = BaseVisitor & Oas3FlatVisitor & Record<string, VisitFunction<any> | VisitObject<any>>;
|
|
146
154
|
export declare type Oas2TransformVisitor = BaseVisitor & Oas2FlatVisitor & Record<string, VisitFunction<any> | VisitObject<any>>;
|
|
155
|
+
export declare type Async2TransformVisitor = BaseVisitor & Async2FlatVisitor & Record<string, VisitFunction<any> | VisitObject<any>>;
|
|
147
156
|
export declare type NestedVisitor<T> = Exclude<T, 'any' | 'ref' | 'Root'>;
|
|
148
157
|
export declare type NormalizedOasVisitors<T extends BaseVisitor> = {
|
|
149
158
|
[V in keyof T]-?: {
|
|
@@ -162,10 +171,13 @@ export declare type NormalizedOasVisitors<T extends BaseVisitor> = {
|
|
|
162
171
|
};
|
|
163
172
|
export declare type Oas3Rule = (options: Record<string, any>) => Oas3Visitor | Oas3Visitor[];
|
|
164
173
|
export declare type Oas2Rule = (options: Record<string, any>) => Oas2Visitor | Oas2Visitor[];
|
|
174
|
+
export declare type Async2Rule = (options: Record<string, any>) => Async2Visitor | Async2Visitor[];
|
|
165
175
|
export declare type Oas3Preprocessor = (options: Record<string, any>) => Oas3TransformVisitor;
|
|
166
176
|
export declare type Oas2Preprocessor = (options: Record<string, any>) => Oas2TransformVisitor;
|
|
177
|
+
export declare type Async2Preprocessor = (options: Record<string, any>) => Async2TransformVisitor;
|
|
167
178
|
export declare type Oas3Decorator = (options: Record<string, any>) => Oas3TransformVisitor;
|
|
168
179
|
export declare type Oas2Decorator = (options: Record<string, any>) => Oas2TransformVisitor;
|
|
180
|
+
export declare type Async2Decorator = (options: Record<string, any>) => Async2TransformVisitor;
|
|
169
181
|
export declare type OasRule = Oas3Rule;
|
|
170
182
|
export declare type OasPreprocessor = Oas3Preprocessor;
|
|
171
183
|
export declare type OasDecorator = Oas3Decorator;
|
package/lib/walk.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { NormalizedNodeType } from './types';
|
|
|
5
5
|
import type { RuleSeverity } from './config';
|
|
6
6
|
import { Location } from './ref-utils';
|
|
7
7
|
import { ResolveError, YamlParseError, Source } from './resolve';
|
|
8
|
-
import {
|
|
8
|
+
import { SpecVersion } from './oas-types';
|
|
9
9
|
declare type NonUndefined = string | number | boolean | symbol | bigint | object | Record<string, any>;
|
|
10
10
|
export declare type ResolveResult<T extends NonUndefined> = {
|
|
11
11
|
node: T;
|
|
@@ -27,7 +27,7 @@ export declare type UserContext = {
|
|
|
27
27
|
type: NormalizedNodeType;
|
|
28
28
|
key: string | number;
|
|
29
29
|
parent: any;
|
|
30
|
-
oasVersion:
|
|
30
|
+
oasVersion: SpecVersion;
|
|
31
31
|
getVisitorData: () => Record<string, unknown>;
|
|
32
32
|
};
|
|
33
33
|
export declare type Loc = {
|
|
@@ -65,7 +65,7 @@ export declare type NormalizedProblem = {
|
|
|
65
65
|
};
|
|
66
66
|
export declare type WalkContext = {
|
|
67
67
|
problems: NormalizedProblem[];
|
|
68
|
-
oasVersion:
|
|
68
|
+
oasVersion: SpecVersion;
|
|
69
69
|
visitorsData: Record<string, Record<string, unknown>>;
|
|
70
70
|
refTypes?: Map<string, NormalizedNodeType>;
|
|
71
71
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=14.
|
|
7
|
+
"node": ">=14.19.0",
|
|
8
|
+
"npm": ">=7.0.0"
|
|
8
9
|
},
|
|
9
10
|
"engineStrict": true,
|
|
10
11
|
"license": "MIT",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"Swagger",
|
|
27
28
|
"OpenAPI linter",
|
|
28
29
|
"Swagger linter",
|
|
30
|
+
"AsyncAPI linter",
|
|
29
31
|
"oas"
|
|
30
32
|
],
|
|
31
33
|
"contributors": [
|
|
@@ -308,7 +308,7 @@ components:
|
|
|
308
308
|
|
|
309
309
|
`;
|
|
310
310
|
|
|
311
|
-
exports[`bundle should not place
|
|
311
|
+
exports[`bundle should not place referenced schema inline when component in question is not of type "schemas" 1`] = `
|
|
312
312
|
openapi: 3.0.0
|
|
313
313
|
paths:
|
|
314
314
|
/pet:
|
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
import outdent from 'outdent';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
|
-
import { bundleDocument, bundle } from '../bundle';
|
|
4
|
+
import { bundleDocument, bundle, bundleFromString } from '../bundle';
|
|
5
5
|
import { parseYamlToDocument, yamlSerializer, makeConfig } from '../../__tests__/utils';
|
|
6
|
-
import { StyleguideConfig, Config, ResolvedConfig } from '../config';
|
|
6
|
+
import { StyleguideConfig, Config, ResolvedConfig, createConfig, loadConfig } from '../config';
|
|
7
7
|
import { BaseResolver } from '../resolve';
|
|
8
8
|
|
|
9
|
+
const stringDocument = outdent`
|
|
10
|
+
openapi: 3.0.0
|
|
11
|
+
paths:
|
|
12
|
+
/pet:
|
|
13
|
+
get:
|
|
14
|
+
operationId: get
|
|
15
|
+
parameters:
|
|
16
|
+
- $ref: '#/components/parameters/shared_a'
|
|
17
|
+
- name: get_b
|
|
18
|
+
post:
|
|
19
|
+
operationId: post
|
|
20
|
+
parameters:
|
|
21
|
+
- $ref: '#/components/parameters/shared_a'
|
|
22
|
+
components:
|
|
23
|
+
parameters:
|
|
24
|
+
shared_a:
|
|
25
|
+
name: shared-a
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const testDocument = parseYamlToDocument(stringDocument, '');
|
|
29
|
+
|
|
9
30
|
describe('bundle', () => {
|
|
10
31
|
const fetchMock = jest.fn(() =>
|
|
11
32
|
Promise.resolve({
|
|
@@ -19,28 +40,6 @@ describe('bundle', () => {
|
|
|
19
40
|
|
|
20
41
|
expect.addSnapshotSerializer(yamlSerializer);
|
|
21
42
|
|
|
22
|
-
const testDocument = parseYamlToDocument(
|
|
23
|
-
outdent`
|
|
24
|
-
openapi: 3.0.0
|
|
25
|
-
paths:
|
|
26
|
-
/pet:
|
|
27
|
-
get:
|
|
28
|
-
operationId: get
|
|
29
|
-
parameters:
|
|
30
|
-
- $ref: '#/components/parameters/shared_a'
|
|
31
|
-
- name: get_b
|
|
32
|
-
post:
|
|
33
|
-
operationId: post
|
|
34
|
-
parameters:
|
|
35
|
-
- $ref: '#/components/parameters/shared_a'
|
|
36
|
-
components:
|
|
37
|
-
parameters:
|
|
38
|
-
shared_a:
|
|
39
|
-
name: shared-a
|
|
40
|
-
`,
|
|
41
|
-
''
|
|
42
|
-
);
|
|
43
|
-
|
|
44
43
|
it('change nothing with only internal refs', async () => {
|
|
45
44
|
const { bundle, problems } = await bundleDocument({
|
|
46
45
|
document: testDocument,
|
|
@@ -97,7 +96,7 @@ describe('bundle', () => {
|
|
|
97
96
|
expect(res.parsed).toMatchSnapshot();
|
|
98
97
|
});
|
|
99
98
|
|
|
100
|
-
it('should not place
|
|
99
|
+
it('should not place referenced schema inline when component in question is not of type "schemas"', async () => {
|
|
101
100
|
const { bundle: res, problems } = await bundle({
|
|
102
101
|
config: new Config({} as ResolvedConfig),
|
|
103
102
|
ref: path.join(__dirname, 'fixtures/refs/external-request-body.yaml'),
|
|
@@ -233,4 +232,45 @@ describe('bundle', () => {
|
|
|
233
232
|
|
|
234
233
|
`);
|
|
235
234
|
});
|
|
235
|
+
|
|
236
|
+
it('should throw an error when there is no document to bundle', () => {
|
|
237
|
+
const wrapper = () =>
|
|
238
|
+
bundle({
|
|
239
|
+
config: new Config({} as ResolvedConfig),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(wrapper()).rejects.toThrowError('Document or reference is required.\n');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should bundle with a doc provided', async () => {
|
|
246
|
+
const {
|
|
247
|
+
bundle: { parsed },
|
|
248
|
+
problems,
|
|
249
|
+
} = await bundle({
|
|
250
|
+
config: await loadConfig({ configPath: path.join(__dirname, 'fixtures/redocly.yaml') }),
|
|
251
|
+
doc: testDocument,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const origCopy = JSON.parse(JSON.stringify(testDocument.parsed));
|
|
255
|
+
|
|
256
|
+
expect(problems).toHaveLength(0);
|
|
257
|
+
expect(parsed).toEqual(origCopy);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('bundleFromString', () => {
|
|
262
|
+
it('should bundle from string using bundleFromString', async () => {
|
|
263
|
+
const {
|
|
264
|
+
bundle: { parsed, ...rest },
|
|
265
|
+
problems,
|
|
266
|
+
} = await bundleFromString({
|
|
267
|
+
config: await createConfig(`
|
|
268
|
+
extends:
|
|
269
|
+
- recommended
|
|
270
|
+
`),
|
|
271
|
+
source: testDocument.source.body,
|
|
272
|
+
});
|
|
273
|
+
expect(problems).toHaveLength(0);
|
|
274
|
+
expect(rest.source.body).toEqual(stringDocument);
|
|
275
|
+
});
|
|
236
276
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
2
|
import { outdent } from 'outdent';
|
|
3
3
|
|
|
4
|
-
import { lintFromString, lintConfig, lintDocument } from '../lint';
|
|
4
|
+
import { lintFromString, lintConfig, lintDocument, lint } from '../lint';
|
|
5
5
|
import { BaseResolver } from '../resolve';
|
|
6
6
|
import { loadConfig } from '../config/load';
|
|
7
7
|
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
|
|
8
|
-
import {
|
|
8
|
+
import { detectSpec } from '../oas-types';
|
|
9
9
|
|
|
10
10
|
describe('lint', () => {
|
|
11
11
|
it('lintFromString should work', async () => {
|
|
@@ -20,7 +20,7 @@ describe('lint', () => {
|
|
|
20
20
|
license: Fail
|
|
21
21
|
|
|
22
22
|
servers:
|
|
23
|
-
- url: http://example.com
|
|
23
|
+
- url: http://redocly-example.com
|
|
24
24
|
paths: {}
|
|
25
25
|
`,
|
|
26
26
|
config: await loadConfig(),
|
|
@@ -46,6 +46,35 @@ describe('lint', () => {
|
|
|
46
46
|
`);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
it('lint should work', async () => {
|
|
50
|
+
const results = await lint({
|
|
51
|
+
ref: path.join(__dirname, 'fixtures/lint/openapi.yaml'),
|
|
52
|
+
config: await loadConfig({
|
|
53
|
+
configPath: path.join(__dirname, 'fixtures/redocly.yaml'),
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(replaceSourceWithRef(results, path.join(__dirname, 'fixtures/lint/')))
|
|
58
|
+
.toMatchInlineSnapshot(`
|
|
59
|
+
Array [
|
|
60
|
+
Object {
|
|
61
|
+
"from": undefined,
|
|
62
|
+
"location": Array [
|
|
63
|
+
Object {
|
|
64
|
+
"pointer": "#/info/license",
|
|
65
|
+
"reportOnKey": false,
|
|
66
|
+
"source": "openapi.yaml",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
"message": "Expected type \`License\` (object) but got \`string\`",
|
|
70
|
+
"ruleId": "spec",
|
|
71
|
+
"severity": "error",
|
|
72
|
+
"suggest": Array [],
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
`);
|
|
76
|
+
});
|
|
77
|
+
|
|
49
78
|
it('lintConfig should work', async () => {
|
|
50
79
|
const document = parseYamlToDocument(
|
|
51
80
|
outdent`
|
|
@@ -61,12 +90,12 @@ describe('lint', () => {
|
|
|
61
90
|
path-http-verbs-order: error
|
|
62
91
|
boolean-parameter-prefixes: off
|
|
63
92
|
rule/operation-summary-length:
|
|
64
|
-
subject:
|
|
93
|
+
subject:
|
|
65
94
|
type: Operation
|
|
66
95
|
property: summary
|
|
67
96
|
message: Operation summary should start with an active verb
|
|
68
97
|
assertions:
|
|
69
|
-
local/checkWordsCount:
|
|
98
|
+
local/checkWordsCount:
|
|
70
99
|
min: 3
|
|
71
100
|
theme:
|
|
72
101
|
openapi:
|
|
@@ -189,8 +218,8 @@ describe('lint', () => {
|
|
|
189
218
|
it('lintConfig should work with legacy fields - referenceDocs', async () => {
|
|
190
219
|
const document = parseYamlToDocument(
|
|
191
220
|
outdent`
|
|
192
|
-
apis:
|
|
193
|
-
entry:
|
|
221
|
+
apis:
|
|
222
|
+
entry:
|
|
194
223
|
root: ./file.yaml
|
|
195
224
|
rules:
|
|
196
225
|
operation-2xx-response: warn
|
|
@@ -304,11 +333,41 @@ describe('lint', () => {
|
|
|
304
333
|
`,
|
|
305
334
|
''
|
|
306
335
|
);
|
|
307
|
-
expect(() =>
|
|
336
|
+
expect(() => detectSpec(testDocument.parsed)).toThrow(
|
|
308
337
|
`Invalid OpenAPI version: should be a string but got "number"`
|
|
309
338
|
);
|
|
310
339
|
});
|
|
311
340
|
|
|
341
|
+
it('detect unsupported OpenAPI version', () => {
|
|
342
|
+
const testDocument = parseYamlToDocument(
|
|
343
|
+
outdent`
|
|
344
|
+
openapi: 1.0.4
|
|
345
|
+
`,
|
|
346
|
+
''
|
|
347
|
+
);
|
|
348
|
+
expect(() => detectSpec(testDocument.parsed)).toThrow(`Unsupported OpenAPI version: 1.0.4`);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('detect unsupported AsyncAPI version', () => {
|
|
352
|
+
const testDocument = parseYamlToDocument(
|
|
353
|
+
outdent`
|
|
354
|
+
asyncapi: 1.0.4
|
|
355
|
+
`,
|
|
356
|
+
''
|
|
357
|
+
);
|
|
358
|
+
expect(() => detectSpec(testDocument.parsed)).toThrow(`Unsupported AsyncAPI version: 1.0.4`);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('detect unsupported spec format', () => {
|
|
362
|
+
const testDocument = parseYamlToDocument(
|
|
363
|
+
outdent`
|
|
364
|
+
notapi: 3.1.0
|
|
365
|
+
`,
|
|
366
|
+
''
|
|
367
|
+
);
|
|
368
|
+
expect(() => detectSpec(testDocument.parsed)).toThrow(`Unsupported specification`);
|
|
369
|
+
});
|
|
370
|
+
|
|
312
371
|
it("spec rule shouldn't throw an error for named callback", async () => {
|
|
313
372
|
const document = parseYamlToDocument(
|
|
314
373
|
outdent`
|
package/src/bundle.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import isEqual = require('lodash.isequal');
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BaseResolver,
|
|
4
|
+
resolveDocument,
|
|
5
|
+
Document,
|
|
6
|
+
ResolvedRefMap,
|
|
7
|
+
makeRefId,
|
|
8
|
+
makeDocumentFromString,
|
|
9
|
+
} from './resolve';
|
|
3
10
|
import { Oas3Rule, normalizeVisitors, Oas3Visitor, Oas2Visitor } from './visitors';
|
|
4
|
-
import { Oas3Types } from './types/oas3';
|
|
5
|
-
import { Oas2Types } from './types/oas2';
|
|
6
|
-
import { Oas3_1Types } from './types/oas3_1';
|
|
7
11
|
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
|
8
12
|
import { WalkContext, walkDocument, UserContext, ResolveResult, NormalizedProblem } from './walk';
|
|
9
|
-
import {
|
|
13
|
+
import { detectSpec, getTypes, getMajorSpecVersion, SpecMajorVersion } from './oas-types';
|
|
10
14
|
import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
|
|
11
15
|
import { initRules } from './config/rules';
|
|
12
16
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
@@ -25,10 +29,7 @@ export enum OasVersion {
|
|
|
25
29
|
Version3_0 = 'oas3_0',
|
|
26
30
|
Version3_1 = 'oas3_1',
|
|
27
31
|
}
|
|
28
|
-
|
|
29
|
-
export async function bundle(opts: {
|
|
30
|
-
ref?: string;
|
|
31
|
-
doc?: Document;
|
|
32
|
+
export type BundleOptions = {
|
|
32
33
|
externalRefResolver?: BaseResolver;
|
|
33
34
|
config: Config;
|
|
34
35
|
dereference?: boolean;
|
|
@@ -36,7 +37,14 @@ export async function bundle(opts: {
|
|
|
36
37
|
skipRedoclyRegistryRefs?: boolean;
|
|
37
38
|
removeUnusedComponents?: boolean;
|
|
38
39
|
keepUrlRefs?: boolean;
|
|
39
|
-
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export async function bundle(
|
|
43
|
+
opts: {
|
|
44
|
+
ref?: string;
|
|
45
|
+
doc?: Document;
|
|
46
|
+
} & BundleOptions
|
|
47
|
+
) {
|
|
40
48
|
const {
|
|
41
49
|
ref,
|
|
42
50
|
doc,
|
|
@@ -48,7 +56,7 @@ export async function bundle(opts: {
|
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
const document =
|
|
51
|
-
doc
|
|
59
|
+
doc === undefined ? await externalRefResolver.resolveDocument(base, ref!, true) : doc;
|
|
52
60
|
|
|
53
61
|
if (document instanceof Error) {
|
|
54
62
|
throw document;
|
|
@@ -62,6 +70,23 @@ export async function bundle(opts: {
|
|
|
62
70
|
});
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
export async function bundleFromString(
|
|
74
|
+
opts: {
|
|
75
|
+
source: string;
|
|
76
|
+
absoluteRef?: string;
|
|
77
|
+
} & BundleOptions
|
|
78
|
+
) {
|
|
79
|
+
const { source, absoluteRef, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts;
|
|
80
|
+
const document = makeDocumentFromString(source, absoluteRef || '/');
|
|
81
|
+
|
|
82
|
+
return bundleDocument({
|
|
83
|
+
document,
|
|
84
|
+
...opts,
|
|
85
|
+
externalRefResolver,
|
|
86
|
+
config: opts.config.styleguide,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
65
90
|
type BundleContext = WalkContext;
|
|
66
91
|
|
|
67
92
|
export type BundleResult = {
|
|
@@ -93,27 +118,20 @@ export async function bundleDocument(opts: {
|
|
|
93
118
|
removeUnusedComponents = false,
|
|
94
119
|
keepUrlRefs = false,
|
|
95
120
|
} = opts;
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const rules = config.getRulesForOasVersion(
|
|
121
|
+
const specVersion = detectSpec(document.parsed);
|
|
122
|
+
const specMajorVersion = getMajorSpecVersion(specVersion);
|
|
123
|
+
const rules = config.getRulesForOasVersion(specMajorVersion);
|
|
99
124
|
const types = normalizeTypes(
|
|
100
|
-
config.extendTypes(
|
|
101
|
-
customTypes ?? oasMajorVersion === OasMajorVersion.Version3
|
|
102
|
-
? oasVersion === OasVersion.Version3_1
|
|
103
|
-
? Oas3_1Types
|
|
104
|
-
: Oas3Types
|
|
105
|
-
: Oas2Types,
|
|
106
|
-
oasVersion
|
|
107
|
-
),
|
|
125
|
+
config.extendTypes(customTypes ?? getTypes(specVersion), specVersion),
|
|
108
126
|
config
|
|
109
127
|
);
|
|
110
128
|
|
|
111
|
-
const preprocessors = initRules(rules as any, config, 'preprocessors',
|
|
112
|
-
const decorators = initRules(rules as any, config, 'decorators',
|
|
129
|
+
const preprocessors = initRules(rules as any, config, 'preprocessors', specVersion);
|
|
130
|
+
const decorators = initRules(rules as any, config, 'decorators', specVersion);
|
|
113
131
|
|
|
114
132
|
const ctx: BundleContext = {
|
|
115
133
|
problems: [],
|
|
116
|
-
oasVersion:
|
|
134
|
+
oasVersion: specVersion,
|
|
117
135
|
refTypes: new Map<string, NormalizedNodeType>(),
|
|
118
136
|
visitorsData: {},
|
|
119
137
|
};
|
|
@@ -123,7 +141,7 @@ export async function bundleDocument(opts: {
|
|
|
123
141
|
severity: 'error',
|
|
124
142
|
ruleId: 'remove-unused-components',
|
|
125
143
|
visitor:
|
|
126
|
-
|
|
144
|
+
specMajorVersion === SpecMajorVersion.OAS2
|
|
127
145
|
? RemoveUnusedComponentsOas2({})
|
|
128
146
|
: RemoveUnusedComponentsOas3({}),
|
|
129
147
|
});
|
|
@@ -157,7 +175,7 @@ export async function bundleDocument(opts: {
|
|
|
157
175
|
severity: 'error',
|
|
158
176
|
ruleId: 'bundler',
|
|
159
177
|
visitor: makeBundleVisitor(
|
|
160
|
-
|
|
178
|
+
specMajorVersion,
|
|
161
179
|
dereference,
|
|
162
180
|
skipRedoclyRegistryRefs,
|
|
163
181
|
document,
|
|
@@ -188,9 +206,9 @@ export async function bundleDocument(opts: {
|
|
|
188
206
|
};
|
|
189
207
|
}
|
|
190
208
|
|
|
191
|
-
export function mapTypeToComponent(typeName: string, version:
|
|
209
|
+
export function mapTypeToComponent(typeName: string, version: SpecMajorVersion) {
|
|
192
210
|
switch (version) {
|
|
193
|
-
case
|
|
211
|
+
case SpecMajorVersion.OAS3:
|
|
194
212
|
switch (typeName) {
|
|
195
213
|
case 'Schema':
|
|
196
214
|
return 'schemas';
|
|
@@ -213,7 +231,7 @@ export function mapTypeToComponent(typeName: string, version: OasMajorVersion) {
|
|
|
213
231
|
default:
|
|
214
232
|
return null;
|
|
215
233
|
}
|
|
216
|
-
case
|
|
234
|
+
case SpecMajorVersion.OAS2:
|
|
217
235
|
switch (typeName) {
|
|
218
236
|
case 'Schema':
|
|
219
237
|
return 'definitions';
|
|
@@ -224,13 +242,22 @@ export function mapTypeToComponent(typeName: string, version: OasMajorVersion) {
|
|
|
224
242
|
default:
|
|
225
243
|
return null;
|
|
226
244
|
}
|
|
245
|
+
case SpecMajorVersion.Async2:
|
|
246
|
+
switch (typeName) {
|
|
247
|
+
case 'Schema':
|
|
248
|
+
return 'schemas';
|
|
249
|
+
case 'Parameter':
|
|
250
|
+
return 'parameters';
|
|
251
|
+
default:
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
// function oas3Move
|
|
231
258
|
|
|
232
259
|
function makeBundleVisitor(
|
|
233
|
-
version:
|
|
260
|
+
version: SpecMajorVersion,
|
|
234
261
|
dereference: boolean,
|
|
235
262
|
skipRedoclyRegistryRefs: boolean,
|
|
236
263
|
rootDocument: Document,
|
|
@@ -282,16 +309,16 @@ function makeBundleVisitor(
|
|
|
282
309
|
Root: {
|
|
283
310
|
enter(root: any, ctx: any) {
|
|
284
311
|
rootLocation = ctx.location;
|
|
285
|
-
if (version ===
|
|
312
|
+
if (version === SpecMajorVersion.OAS3) {
|
|
286
313
|
components = root.components = root.components || {};
|
|
287
|
-
} else if (version ===
|
|
314
|
+
} else if (version === SpecMajorVersion.OAS2) {
|
|
288
315
|
components = root;
|
|
289
316
|
}
|
|
290
317
|
},
|
|
291
318
|
},
|
|
292
319
|
};
|
|
293
320
|
|
|
294
|
-
if (version ===
|
|
321
|
+
if (version === SpecMajorVersion.OAS3) {
|
|
295
322
|
visitor.DiscriminatorMapping = {
|
|
296
323
|
leave(mapping: Record<string, string>, ctx: any) {
|
|
297
324
|
for (const name of Object.keys(mapping)) {
|
|
@@ -345,7 +372,7 @@ function makeBundleVisitor(
|
|
|
345
372
|
components[componentType] = components[componentType] || {};
|
|
346
373
|
const name = getComponentName(target, componentType, ctx);
|
|
347
374
|
components[componentType][name] = target.node;
|
|
348
|
-
if (version ===
|
|
375
|
+
if (version === SpecMajorVersion.OAS3) {
|
|
349
376
|
return `#/components/${componentType}/${name}`;
|
|
350
377
|
} else {
|
|
351
378
|
return `#/${componentType}/${name}`;
|
|
@@ -6,6 +6,11 @@ StyleguideConfig {
|
|
|
6
6
|
"_usedVersions": Set {},
|
|
7
7
|
"configFile": undefined,
|
|
8
8
|
"decorators": Object {
|
|
9
|
+
"async2": Object {
|
|
10
|
+
"oas2": Object {},
|
|
11
|
+
"oas3_0": Object {},
|
|
12
|
+
"oas3_1": Object {},
|
|
13
|
+
},
|
|
9
14
|
"oas2": Object {
|
|
10
15
|
"oas2": Object {},
|
|
11
16
|
"oas3_0": Object {},
|
|
@@ -39,6 +44,11 @@ StyleguideConfig {
|
|
|
39
44
|
"pluginPaths": Array [],
|
|
40
45
|
"plugins": Array [],
|
|
41
46
|
"preprocessors": Object {
|
|
47
|
+
"async2": Object {
|
|
48
|
+
"oas2": Object {},
|
|
49
|
+
"oas3_0": Object {},
|
|
50
|
+
"oas3_1": Object {},
|
|
51
|
+
},
|
|
42
52
|
"oas2": Object {
|
|
43
53
|
"oas2": Object {},
|
|
44
54
|
"oas3_0": Object {},
|
|
@@ -97,6 +107,20 @@ StyleguideConfig {
|
|
|
97
107
|
},
|
|
98
108
|
"recommendedFallback": false,
|
|
99
109
|
"rules": Object {
|
|
110
|
+
"async2": Object {
|
|
111
|
+
"oas2": Object {
|
|
112
|
+
"no-empty-servers": "error",
|
|
113
|
+
"operation-summary": "error",
|
|
114
|
+
},
|
|
115
|
+
"oas3_0": Object {
|
|
116
|
+
"no-empty-servers": "error",
|
|
117
|
+
"operation-summary": "error",
|
|
118
|
+
},
|
|
119
|
+
"oas3_1": Object {
|
|
120
|
+
"no-empty-servers": "error",
|
|
121
|
+
"operation-summary": "error",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
100
124
|
"oas2": Object {
|
|
101
125
|
"oas2": Object {
|
|
102
126
|
"no-empty-servers": "error",
|