@redocly/openapi-core 1.1.0 → 1.2.0
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 +10 -0
- package/lib/bundle.d.ts +2 -2
- package/lib/bundle.js +24 -22
- 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/rules.d.ts +2 -2
- package/lib/config/types.d.ts +9 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +7 -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/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/types/asyncapi.d.ts +2 -0
- package/lib/types/asyncapi.js +1027 -0
- 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 +2 -1
- package/src/__tests__/lint.test.ts +36 -6
- package/src/bundle.ts +27 -28
- package/src/config/__tests__/__snapshots__/config.test.ts.snap +24 -0
- package/src/config/__tests__/config.test.ts +15 -4
- 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/rules.ts +2 -2
- package/src/config/types.ts +14 -4
- package/src/index.ts +7 -1
- 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/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/index.ts +2 -2
- package/src/types/asyncapi.ts +1136 -0
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { outdent } from 'outdent';
|
|
2
|
+
import { lintDocument } from '../../../lint';
|
|
3
|
+
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
|
|
4
|
+
import { BaseResolver } from '../../../resolve';
|
|
5
|
+
|
|
6
|
+
describe('no-channel-trailing-slash', () => {
|
|
7
|
+
it('should report on trailing slash in a channel path', async () => {
|
|
8
|
+
const document = parseYamlToDocument(
|
|
9
|
+
outdent`
|
|
10
|
+
asyncapi: '2.6.0'
|
|
11
|
+
info:
|
|
12
|
+
title: Excellent API
|
|
13
|
+
version: 1.0.0
|
|
14
|
+
channels:
|
|
15
|
+
/trailing/:
|
|
16
|
+
subscribe:
|
|
17
|
+
message:
|
|
18
|
+
messageId: Message1
|
|
19
|
+
`,
|
|
20
|
+
'asyncapi.yaml'
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const results = await lintDocument({
|
|
24
|
+
externalRefResolver: new BaseResolver(),
|
|
25
|
+
document,
|
|
26
|
+
config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
|
|
30
|
+
Array [
|
|
31
|
+
Object {
|
|
32
|
+
"location": Array [
|
|
33
|
+
Object {
|
|
34
|
+
"pointer": "#/channels/~1trailing~1",
|
|
35
|
+
"reportOnKey": true,
|
|
36
|
+
"source": "asyncapi.yaml",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
"message": "\`/trailing/\` should not have a trailing slash.",
|
|
40
|
+
"ruleId": "no-channel-trailing-slash",
|
|
41
|
+
"severity": "error",
|
|
42
|
+
"suggest": Array [],
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should not report on if no trailing slash in path', async () => {
|
|
49
|
+
const document = parseYamlToDocument(
|
|
50
|
+
outdent`
|
|
51
|
+
asyncapi: '2.6.0'
|
|
52
|
+
info:
|
|
53
|
+
title: Excellent API
|
|
54
|
+
version: 1.0.0
|
|
55
|
+
channels:
|
|
56
|
+
/expected:
|
|
57
|
+
subscribe:
|
|
58
|
+
message:
|
|
59
|
+
messageId: Message1
|
|
60
|
+
`,
|
|
61
|
+
'asyncapi.yaml'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const results = await lintDocument({
|
|
65
|
+
externalRefResolver: new BaseResolver(),
|
|
66
|
+
document,
|
|
67
|
+
config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should not report on trailing slash in path if the path is root', async () => {
|
|
74
|
+
const document = parseYamlToDocument(
|
|
75
|
+
outdent`
|
|
76
|
+
asyncapi: '2.6.0'
|
|
77
|
+
info:
|
|
78
|
+
title: Excellent API
|
|
79
|
+
version: 1.0.0
|
|
80
|
+
channels:
|
|
81
|
+
/:
|
|
82
|
+
subscribe:
|
|
83
|
+
message:
|
|
84
|
+
messageId: Message1
|
|
85
|
+
`,
|
|
86
|
+
'foobar.yaml'
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const results = await lintDocument({
|
|
90
|
+
externalRefResolver: new BaseResolver(),
|
|
91
|
+
document,
|
|
92
|
+
config: await makeConfig({ 'no-channel-trailing-slash': 'error' }),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Async2Rule } from '../../visitors';
|
|
2
|
+
import { UserContext } from '../../walk';
|
|
3
|
+
|
|
4
|
+
export const ChannelsKebabCase: Async2Rule = () => {
|
|
5
|
+
return {
|
|
6
|
+
Channel(_channel: object, { report, key }: UserContext) {
|
|
7
|
+
const segments = (key as string)
|
|
8
|
+
.split(/[/.:]/) // split on / or : as likely channel namespacers
|
|
9
|
+
.filter((s) => s !== ''); // filter out empty segments
|
|
10
|
+
if (!segments.every((segment) => /^{.+}$/.test(segment) || /^[a-z0-9-.]+$/.test(segment))) {
|
|
11
|
+
report({
|
|
12
|
+
message: `\`${key}\` does not use kebab-case.`,
|
|
13
|
+
location: { reportOnKey: true },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Async2Rule } from '../../visitors';
|
|
2
|
+
import { Assertions } from '../common/assertions';
|
|
3
|
+
import { Spec } from '../common/spec';
|
|
4
|
+
import { InfoContact } from '../common/info-contact';
|
|
5
|
+
import { OperationOperationId } from '../common/operation-operationId';
|
|
6
|
+
import { TagDescription } from '../common/tag-description';
|
|
7
|
+
import { TagsAlphabetical } from '../common/tags-alphabetical';
|
|
8
|
+
import { ChannelsKebabCase } from './channels-kebab-case';
|
|
9
|
+
import { NoChannelTrailingSlash } from './no-channel-trailing-slash';
|
|
10
|
+
|
|
11
|
+
export const rules = {
|
|
12
|
+
spec: Spec as Async2Rule,
|
|
13
|
+
assertions: Assertions,
|
|
14
|
+
'info-contact': InfoContact,
|
|
15
|
+
'operation-operationId': OperationOperationId,
|
|
16
|
+
'channels-kebab-case': ChannelsKebabCase,
|
|
17
|
+
'no-channel-trailing-slash': NoChannelTrailingSlash,
|
|
18
|
+
'tag-description': TagDescription,
|
|
19
|
+
'tags-alphabetical': TagsAlphabetical,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const preprocessors = {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Async2Rule } from '../../visitors';
|
|
2
|
+
import { UserContext } from '../../walk';
|
|
3
|
+
|
|
4
|
+
export const NoChannelTrailingSlash: Async2Rule = () => {
|
|
5
|
+
return {
|
|
6
|
+
Channel(_channel: any, { report, key, location }: UserContext) {
|
|
7
|
+
if ((key as string).endsWith('/') && key !== '/') {
|
|
8
|
+
report({
|
|
9
|
+
message: `\`${key}\` should not have a trailing slash.`,
|
|
10
|
+
location: location.key(),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -2,7 +2,7 @@ import type { Oas2Rule, Oas3Rule } from '../../visitors';
|
|
|
2
2
|
import type { UserContext } from '../../walk';
|
|
3
3
|
import type { Oas2Schema } from '../../typings/swagger';
|
|
4
4
|
import type { Oas3Schema, Oas3_1Schema } from '../../typings/openapi';
|
|
5
|
-
import {
|
|
5
|
+
import { SpecVersion } from '../../oas-types';
|
|
6
6
|
|
|
7
7
|
const SCALAR_TYPES = ['string', 'integer', 'number', 'boolean', 'null'];
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ export const ScalarPropertyMissingExample: Oas3Rule | Oas2Rule = () => {
|
|
|
25
25
|
) {
|
|
26
26
|
report({
|
|
27
27
|
message: `Scalar property should have "example"${
|
|
28
|
-
oasVersion ===
|
|
28
|
+
oasVersion === SpecVersion.OAS3_1 ? ' or "examples"' : ''
|
|
29
29
|
} defined.`,
|
|
30
30
|
location: location.child(propName).key(),
|
|
31
31
|
});
|
package/src/rules/common/spec.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Oas3Rule, Oas2Rule } from '../../visitors';
|
|
1
|
+
import type { Oas3Rule, Oas2Rule, Async2Rule } from '../../visitors';
|
|
2
2
|
import { isNamedType, SpecExtension } from '../../types';
|
|
3
3
|
import { oasTypeOf, matchesJsonSchemaType, getSuggest, validateSchemaEnumType } from '../utils';
|
|
4
4
|
import { isRef } from '../../ref-utils';
|
|
5
5
|
import { isPlainObject } from '../../utils';
|
|
6
6
|
import { UserContext } from '../../walk';
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const Spec: Oas3Rule | Oas2Rule | Async2Rule = () => {
|
|
9
9
|
return {
|
|
10
10
|
any(
|
|
11
11
|
node: any,
|
package/src/rules/oas2/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Oas2Rule } from '../../visitors';
|
|
2
|
-
import {
|
|
2
|
+
import { Spec } from '../common/spec';
|
|
3
3
|
import { NoInvalidSchemaExamples } from '../common/no-invalid-schema-examples';
|
|
4
4
|
import { NoInvalidParameterExamples } from '../common/no-invalid-parameter-examples';
|
|
5
5
|
import { InfoContact } from '../common/info-contact';
|
|
@@ -43,7 +43,7 @@ import { RequiredStringPropertyMissingMinLength } from '../common/required-strin
|
|
|
43
43
|
import { SpecStrictRefs } from '../common/spec-strict-refs';
|
|
44
44
|
|
|
45
45
|
export const rules = {
|
|
46
|
-
spec:
|
|
46
|
+
spec: Spec as Oas2Rule,
|
|
47
47
|
'no-invalid-schema-examples': NoInvalidSchemaExamples,
|
|
48
48
|
'no-invalid-parameter-examples': NoInvalidParameterExamples,
|
|
49
49
|
'info-contact': InfoContact as Oas2Rule,
|
package/src/rules/oas3/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Oas3RuleSet } from '../../oas-types';
|
|
2
|
-
import {
|
|
2
|
+
import { Spec } from '../common/spec';
|
|
3
3
|
import { Operation2xxResponse } from '../common/operation-2xx-response';
|
|
4
4
|
import { Operation4xxResponse } from '../common/operation-4xx-response';
|
|
5
5
|
import { Assertions } from '../common/assertions';
|
|
@@ -54,7 +54,7 @@ import { SpecStrictRefs } from '../common/spec-strict-refs';
|
|
|
54
54
|
import { ComponentNameUnique } from './component-name-unique';
|
|
55
55
|
|
|
56
56
|
export const rules = {
|
|
57
|
-
spec:
|
|
57
|
+
spec: Spec,
|
|
58
58
|
'info-contact': InfoContact,
|
|
59
59
|
'info-license': InfoLicense,
|
|
60
60
|
'info-license-url': InfoLicenseUrl,
|