@redocly/openapi-core 1.0.0-beta.108 → 1.0.0-beta.110
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/README.md +2 -2
- package/lib/benchmark/benches/resolve-with-no-external.bench.js +1 -1
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +4 -4
- package/lib/config/all.js +3 -1
- package/lib/config/config-resolvers.js +22 -4
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +1 -0
- package/lib/config/load.d.ts +8 -2
- package/lib/config/load.js +4 -2
- package/lib/config/minimal.js +3 -1
- package/lib/config/recommended.js +3 -1
- package/lib/config/rules.js +1 -1
- package/lib/config/types.d.ts +17 -0
- package/lib/config/utils.d.ts +2 -2
- package/lib/config/utils.js +44 -6
- package/lib/decorators/common/registry-dependencies.js +1 -1
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +22 -1
- package/lib/lint.js +2 -2
- package/lib/redocly/registry-api.d.ts +0 -1
- package/lib/redocly/registry-api.js +5 -4
- package/lib/resolve.js +3 -1
- package/lib/rules/ajv.d.ts +1 -1
- package/lib/rules/ajv.js +5 -5
- package/lib/rules/common/assertions/asserts.d.ts +3 -5
- package/lib/rules/common/assertions/asserts.js +137 -97
- package/lib/rules/common/assertions/index.js +2 -6
- package/lib/rules/common/assertions/utils.d.ts +12 -6
- package/lib/rules/common/assertions/utils.js +33 -20
- package/lib/rules/common/no-ambiguous-paths.js +1 -1
- package/lib/rules/common/no-identical-paths.js +1 -1
- package/lib/rules/common/operation-2xx-response.js +1 -1
- package/lib/rules/common/operation-4xx-response.js +1 -1
- package/lib/rules/common/operation-operationId.js +1 -1
- package/lib/rules/common/operation-tag-defined.js +1 -1
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/security-defined.d.ts +2 -0
- package/lib/rules/common/{operation-security-defined.js → security-defined.js} +18 -4
- package/lib/rules/common/spec.js +12 -1
- package/lib/rules/common/tags-alphabetical.js +1 -1
- package/lib/rules/oas2/index.d.ts +1 -1
- package/lib/rules/oas2/index.js +2 -2
- package/lib/rules/oas2/remove-unused-components.js +1 -1
- package/lib/rules/oas2/request-mime-type.js +1 -1
- package/lib/rules/oas2/response-mime-type.js +1 -1
- package/lib/rules/oas3/index.js +6 -2
- package/lib/rules/oas3/no-empty-servers.js +1 -1
- package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -1
- package/lib/rules/oas3/no-unused-components.js +1 -1
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
- package/lib/rules/oas3/remove-unused-components.js +1 -1
- package/lib/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
- package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
- package/lib/rules/other/stats.d.ts +2 -2
- package/lib/rules/other/stats.js +2 -2
- package/lib/rules/utils.js +1 -1
- package/lib/types/oas2.js +5 -5
- package/lib/types/oas3.js +27 -20
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +60 -54
- package/lib/utils.d.ts +3 -3
- package/lib/utils.js +5 -5
- package/lib/visitors.d.ts +11 -11
- package/lib/visitors.js +13 -1
- package/package.json +3 -5
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
- package/src/__tests__/fixtures/extension.js +3 -3
- package/src/__tests__/format.test.ts +76 -0
- package/src/__tests__/lint.test.ts +184 -121
- package/src/__tests__/resolve-http.test.ts +1 -1
- package/src/__tests__/resolve.test.ts +9 -9
- package/src/__tests__/walk.test.ts +78 -10
- package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
- package/src/bundle.ts +4 -4
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -2
- package/src/config/__tests__/config-resolvers.test.ts +37 -1
- package/src/config/__tests__/config.test.ts +5 -0
- package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -19
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
- package/src/config/__tests__/load.test.ts +13 -16
- package/src/config/__tests__/resolve-plugins.test.ts +3 -3
- package/src/config/__tests__/utils.test.ts +64 -4
- package/src/config/all.ts +3 -1
- package/src/config/config-resolvers.ts +30 -7
- package/src/config/config.ts +2 -0
- package/src/config/load.ts +13 -6
- package/src/config/minimal.ts +3 -1
- package/src/config/recommended.ts +3 -1
- package/src/config/rules.ts +2 -2
- package/src/config/types.ts +24 -0
- package/src/config/utils.ts +103 -13
- package/src/decorators/common/registry-dependencies.ts +1 -1
- package/src/format/format.ts +32 -2
- package/src/lint.ts +2 -2
- package/src/redocly/registry-api.ts +5 -4
- package/src/resolve.ts +3 -1
- package/src/rules/__tests__/utils.test.ts +1 -1
- package/src/rules/ajv.ts +4 -4
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
- package/src/rules/common/__tests__/security-defined.test.ts +175 -0
- package/src/rules/common/__tests__/spec.test.ts +79 -0
- package/src/rules/common/assertions/__tests__/asserts.test.ts +491 -428
- package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
- package/src/rules/common/assertions/asserts.ts +155 -97
- package/src/rules/common/assertions/index.ts +2 -11
- package/src/rules/common/assertions/utils.ts +66 -36
- package/src/rules/common/no-ambiguous-paths.ts +1 -1
- package/src/rules/common/no-identical-paths.ts +1 -1
- package/src/rules/common/operation-2xx-response.ts +1 -1
- package/src/rules/common/operation-4xx-response.ts +1 -1
- package/src/rules/common/operation-operationId.ts +1 -1
- package/src/rules/common/operation-tag-defined.ts +1 -1
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +19 -4
- package/src/rules/common/spec.ts +15 -1
- package/src/rules/common/tags-alphabetical.ts +1 -1
- package/src/rules/oas2/index.ts +2 -2
- package/src/rules/oas2/remove-unused-components.ts +1 -1
- package/src/rules/oas2/request-mime-type.ts +1 -1
- package/src/rules/oas2/response-mime-type.ts +1 -1
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
- package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
- package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
- package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
- package/src/rules/oas3/index.ts +6 -2
- package/src/rules/oas3/no-empty-servers.ts +1 -1
- package/src/rules/oas3/no-server-variables-empty-enum.ts +1 -1
- package/src/rules/oas3/no-unused-components.ts +1 -1
- package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
- package/src/rules/oas3/remove-unused-components.ts +1 -1
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
- package/src/rules/other/stats.ts +2 -2
- package/src/rules/utils.ts +2 -1
- package/src/types/index.ts +2 -2
- package/src/types/oas2.ts +5 -5
- package/src/types/oas3.ts +27 -20
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +66 -38
- package/src/utils.ts +11 -7
- package/src/visitors.ts +29 -13
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/operation-security-defined.d.ts +0 -2
- package/src/rules/common/__tests__/operation-security-defined.test.ts +0 -69
package/src/bundle.ts
CHANGED
|
@@ -122,7 +122,7 @@ export async function bundleDocument(opts: {
|
|
|
122
122
|
|
|
123
123
|
const resolvedRefMap = await resolveDocument({
|
|
124
124
|
rootDocument: document,
|
|
125
|
-
rootType: types.
|
|
125
|
+
rootType: types.Root,
|
|
126
126
|
externalRefResolver,
|
|
127
127
|
});
|
|
128
128
|
|
|
@@ -148,7 +148,7 @@ export async function bundleDocument(opts: {
|
|
|
148
148
|
|
|
149
149
|
walkDocument({
|
|
150
150
|
document,
|
|
151
|
-
rootType: types.
|
|
151
|
+
rootType: types.Root as NormalizedNodeType,
|
|
152
152
|
normalizedVisitors: bundleVisitor,
|
|
153
153
|
resolvedRefMap,
|
|
154
154
|
ctx,
|
|
@@ -158,7 +158,7 @@ export async function bundleDocument(opts: {
|
|
|
158
158
|
bundle: document,
|
|
159
159
|
problems: ctx.problems.map((problem) => config.addProblemToIgnore(problem)),
|
|
160
160
|
fileDependencies: externalRefResolver.getFiles(),
|
|
161
|
-
rootType: types.
|
|
161
|
+
rootType: types.Root,
|
|
162
162
|
refTypes: ctx.refTypes,
|
|
163
163
|
visitorsData: ctx.visitorsData,
|
|
164
164
|
};
|
|
@@ -254,7 +254,7 @@ function makeBundleVisitor(
|
|
|
254
254
|
}
|
|
255
255
|
},
|
|
256
256
|
},
|
|
257
|
-
|
|
257
|
+
Root: {
|
|
258
258
|
enter(root: any) {
|
|
259
259
|
if (version === OasMajorVersion.Version3) {
|
|
260
260
|
components = root.components = root.components || {};
|
|
@@ -18,6 +18,7 @@ Object {
|
|
|
18
18
|
"no-server-variables-empty-enum": "error",
|
|
19
19
|
"no-undefined-server-variable": "error",
|
|
20
20
|
"no-unused-components": "warn",
|
|
21
|
+
"spec-components-invalid-map-name": "error",
|
|
21
22
|
},
|
|
22
23
|
"oas3_1Decorators": Object {},
|
|
23
24
|
"oas3_1Preprocessors": Object {},
|
|
@@ -30,6 +31,7 @@ Object {
|
|
|
30
31
|
"no-server-variables-empty-enum": "error",
|
|
31
32
|
"no-undefined-server-variable": "error",
|
|
32
33
|
"no-unused-components": "warn",
|
|
34
|
+
"spec-components-invalid-map-name": "error",
|
|
33
35
|
},
|
|
34
36
|
"preprocessors": Object {},
|
|
35
37
|
"recommendedFallback": false,
|
|
@@ -54,7 +56,6 @@ Object {
|
|
|
54
56
|
"operation-operationId-unique": "error",
|
|
55
57
|
"operation-operationId-url-safe": "error",
|
|
56
58
|
"operation-parameters-unique": "error",
|
|
57
|
-
"operation-security-defined": "error",
|
|
58
59
|
"operation-singular-tag": "off",
|
|
59
60
|
"operation-summary": "error",
|
|
60
61
|
"operation-tag-defined": "off",
|
|
@@ -64,6 +65,7 @@ Object {
|
|
|
64
65
|
"path-not-include-query": "error",
|
|
65
66
|
"path-parameters-defined": "error",
|
|
66
67
|
"paths-kebab-case": "off",
|
|
68
|
+
"security-defined": "error",
|
|
67
69
|
"spec": "error",
|
|
68
70
|
"tag-description": "warn",
|
|
69
71
|
"tags-alphabetical": "off",
|
|
@@ -89,6 +91,7 @@ Object {
|
|
|
89
91
|
"no-server-variables-empty-enum": "error",
|
|
90
92
|
"no-undefined-server-variable": "error",
|
|
91
93
|
"no-unused-components": "warn",
|
|
94
|
+
"spec-components-invalid-map-name": "error",
|
|
92
95
|
},
|
|
93
96
|
"oas3_1Decorators": Object {},
|
|
94
97
|
"oas3_1Preprocessors": Object {},
|
|
@@ -101,6 +104,7 @@ Object {
|
|
|
101
104
|
"no-server-variables-empty-enum": "error",
|
|
102
105
|
"no-undefined-server-variable": "error",
|
|
103
106
|
"no-unused-components": "warn",
|
|
107
|
+
"spec-components-invalid-map-name": "error",
|
|
104
108
|
},
|
|
105
109
|
"preprocessors": Object {},
|
|
106
110
|
"recommendedFallback": undefined,
|
|
@@ -142,7 +146,6 @@ Object {
|
|
|
142
146
|
"operation-operationId-unique": "error",
|
|
143
147
|
"operation-operationId-url-safe": "error",
|
|
144
148
|
"operation-parameters-unique": "error",
|
|
145
|
-
"operation-security-defined": "error",
|
|
146
149
|
"operation-singular-tag": "off",
|
|
147
150
|
"operation-summary": "error",
|
|
148
151
|
"operation-tag-defined": "off",
|
|
@@ -151,6 +154,7 @@ Object {
|
|
|
151
154
|
"path-not-include-query": "error",
|
|
152
155
|
"path-parameters-defined": "error",
|
|
153
156
|
"paths-kebab-case": "off",
|
|
157
|
+
"security-defined": "error",
|
|
154
158
|
"spec": "error",
|
|
155
159
|
"tag-description": "warn",
|
|
156
160
|
"tags-alphabetical": "off",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { colorize } from '../../logger';
|
|
2
|
+
import { asserts } from '../../rules/common/assertions/asserts';
|
|
1
3
|
import { resolveStyleguideConfig, resolveApis, resolveConfig } from '../config-resolvers';
|
|
2
4
|
const path = require('path');
|
|
3
5
|
|
|
@@ -130,6 +132,40 @@ describe('resolveStyleguideConfig', () => {
|
|
|
130
132
|
expect(styleguide).toMatchSnapshot();
|
|
131
133
|
});
|
|
132
134
|
|
|
135
|
+
it('should resolve custom assertion from plugin', async () => {
|
|
136
|
+
const styleguideConfig = {
|
|
137
|
+
extends: ['local-config-with-custom-function.yaml'],
|
|
138
|
+
};
|
|
139
|
+
const { plugins } = await resolveStyleguideConfig({
|
|
140
|
+
styleguideConfig,
|
|
141
|
+
configPath,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(plugins).toBeDefined();
|
|
145
|
+
expect(plugins?.length).toBe(2);
|
|
146
|
+
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should throw error when custom assertion load not exist plugin', async () => {
|
|
150
|
+
const styleguideConfig = {
|
|
151
|
+
extends: ['local-config-with-wrong-custom-function.yaml'],
|
|
152
|
+
};
|
|
153
|
+
try {
|
|
154
|
+
await resolveStyleguideConfig({
|
|
155
|
+
styleguideConfig,
|
|
156
|
+
configPath,
|
|
157
|
+
});
|
|
158
|
+
} catch (e) {
|
|
159
|
+
expect(e.message.toString()).toContain(
|
|
160
|
+
`Plugin ${colorize.red(
|
|
161
|
+
'test-plugin'
|
|
162
|
+
)} doesn't export assertions function with name ${colorize.red('checkWordsCount2')}.`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
expect(asserts['test-plugin/checkWordsCount']).toBeDefined();
|
|
167
|
+
});
|
|
168
|
+
|
|
133
169
|
it('should correctly merge assertions from nested config', async () => {
|
|
134
170
|
const styleguideConfig = {
|
|
135
171
|
extends: ['local-config-with-file.yaml'],
|
|
@@ -165,7 +201,7 @@ describe('resolveStyleguideConfig', () => {
|
|
|
165
201
|
const styleguideConfig = {
|
|
166
202
|
// This points to ./fixtures/resolve-remote-configs/remote-config.yaml
|
|
167
203
|
extends: [
|
|
168
|
-
'https://raw.githubusercontent.com/Redocly/redocly-cli/
|
|
204
|
+
'https://raw.githubusercontent.com/Redocly/redocly-cli/main/packages/core/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml',
|
|
169
205
|
],
|
|
170
206
|
};
|
|
171
207
|
|
|
@@ -48,6 +48,7 @@ const testConfig: Config = {
|
|
|
48
48
|
'features.mockServer': {},
|
|
49
49
|
resolve: { http: { headers: [] } },
|
|
50
50
|
organization: 'redocly-test',
|
|
51
|
+
files: [],
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
describe('getMergedConfig', () => {
|
|
@@ -67,6 +68,7 @@ describe('getMergedConfig', () => {
|
|
|
67
68
|
"configFile": "redocly.yaml",
|
|
68
69
|
"features.mockServer": Object {},
|
|
69
70
|
"features.openapi": Object {},
|
|
71
|
+
"files": Array [],
|
|
70
72
|
"organization": "redocly-test",
|
|
71
73
|
"rawConfig": Object {
|
|
72
74
|
"apis": Object {
|
|
@@ -81,6 +83,7 @@ describe('getMergedConfig', () => {
|
|
|
81
83
|
},
|
|
82
84
|
"features.mockServer": Object {},
|
|
83
85
|
"features.openapi": Object {},
|
|
86
|
+
"files": Array [],
|
|
84
87
|
"organization": "redocly-test",
|
|
85
88
|
"styleguide": Object {
|
|
86
89
|
"extendPaths": Array [],
|
|
@@ -163,6 +166,7 @@ describe('getMergedConfig', () => {
|
|
|
163
166
|
"configFile": "redocly.yaml",
|
|
164
167
|
"features.mockServer": Object {},
|
|
165
168
|
"features.openapi": Object {},
|
|
169
|
+
"files": Array [],
|
|
166
170
|
"organization": "redocly-test",
|
|
167
171
|
"rawConfig": Object {
|
|
168
172
|
"apis": Object {
|
|
@@ -177,6 +181,7 @@ describe('getMergedConfig', () => {
|
|
|
177
181
|
},
|
|
178
182
|
"features.mockServer": Object {},
|
|
179
183
|
"features.openapi": Object {},
|
|
184
|
+
"files": Array [],
|
|
180
185
|
"organization": "redocly-test",
|
|
181
186
|
"styleguide": Object {
|
|
182
187
|
"extendPaths": Array [],
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
- './plugin.js'
|
|
1
|
+
plugins:
|
|
2
|
+
- './plugin.js'
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- test-plugin-nested/all
|
|
1
|
+
plugins:
|
|
2
|
+
- plugin.js
|
|
3
|
+
rules:
|
|
4
|
+
operation-2xx-response: error
|
|
5
|
+
assert/path-item-get-defined:
|
|
6
|
+
subject: PathItem
|
|
7
|
+
property: get
|
|
8
|
+
message: Every path item must have a GET operation.
|
|
9
|
+
defined: true
|
|
10
|
+
extends:
|
|
11
|
+
- test-plugin-nested/all
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
operation-2xx-response: off
|
|
1
|
+
extends:
|
|
2
|
+
- local-config-with-circular.yaml
|
|
3
|
+
rules:
|
|
4
|
+
no-invalid-media-type-examples: error
|
|
5
|
+
operation-description: error
|
|
6
|
+
path-http-verbs-order: error
|
|
7
|
+
operation-2xx-response: off
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
lint:
|
|
2
|
+
rules:
|
|
3
|
+
no-invalid-media-type-examples: warn
|
|
4
|
+
operation-4xx-response: off
|
|
5
|
+
assert/tag-description:
|
|
6
|
+
subject: Tag
|
|
7
|
+
property: description
|
|
8
|
+
message: Tag description must have at least 3 words.
|
|
9
|
+
severity: error
|
|
10
|
+
test-plugin/checkWordsCount:
|
|
11
|
+
min: 3
|
|
12
|
+
plugins:
|
|
13
|
+
- plugin.js
|
|
14
|
+
extends:
|
|
15
|
+
- recommended
|
|
16
|
+
- test-plugin/all
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- test-plugin/all
|
|
1
|
+
rules:
|
|
2
|
+
no-invalid-media-type-examples: warn
|
|
3
|
+
operation-4xx-response: off
|
|
4
|
+
assert/tag-description:
|
|
5
|
+
subject: Tag
|
|
6
|
+
property: description
|
|
7
|
+
message: Tag description must be at least 13 characters and end with a full stop.
|
|
8
|
+
severity: error
|
|
9
|
+
minLength: 13
|
|
10
|
+
pattern: /\.$/
|
|
11
|
+
plugins:
|
|
12
|
+
- plugin.js
|
|
13
|
+
- api/plugin.js
|
|
14
|
+
extends:
|
|
15
|
+
- recommended
|
|
16
|
+
- api/nested-config.yaml
|
|
17
|
+
- test-plugin-nested/all
|
|
18
|
+
- test-plugin/all
|
package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
lint:
|
|
2
|
+
rules:
|
|
3
|
+
no-invalid-media-type-examples: warn
|
|
4
|
+
operation-4xx-response: off
|
|
5
|
+
assert/tag-description:
|
|
6
|
+
subject: Tag
|
|
7
|
+
property: description
|
|
8
|
+
message: Tag description must have at least 3 words.
|
|
9
|
+
severity: error
|
|
10
|
+
test-plugin/checkWordsCount2:
|
|
11
|
+
min: 3
|
|
12
|
+
plugins:
|
|
13
|
+
- plugin.js
|
|
14
|
+
extends:
|
|
15
|
+
- recommended
|
|
16
|
+
- test-plugin/all
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
operation-2xx-response: off
|
|
1
|
+
plugins:
|
|
2
|
+
- plugin.js
|
|
3
|
+
extends:
|
|
4
|
+
- test-plugin/all
|
|
5
|
+
rules:
|
|
6
|
+
no-invalid-media-type-examples: error
|
|
7
|
+
operation-description: error
|
|
8
|
+
path-http-verbs-order: error
|
|
9
|
+
operation-2xx-response: off
|
|
@@ -51,6 +51,16 @@ const decorators = {
|
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const assertions = {
|
|
55
|
+
checkWordsCount: (value, opts, location) => {
|
|
56
|
+
const words = value.split(' ');
|
|
57
|
+
if (words.length >= opts.min) {
|
|
58
|
+
return { isValid: true };
|
|
59
|
+
}
|
|
60
|
+
return { isValid: false, location };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
54
64
|
const configs = {
|
|
55
65
|
all: {
|
|
56
66
|
rules: {
|
|
@@ -66,4 +76,5 @@ module.exports = {
|
|
|
66
76
|
rules,
|
|
67
77
|
decorators,
|
|
68
78
|
configs,
|
|
79
|
+
assertions,
|
|
69
80
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
operation-4xx-response: error
|
|
1
|
+
rules:
|
|
2
|
+
operation-2xx-response: off
|
|
3
|
+
operation-4xx-response: error
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
operation-2xx-response: error
|
|
1
|
+
extends:
|
|
2
|
+
- nested-remote-config.yaml
|
|
3
|
+
rules:
|
|
4
|
+
operation-2xx-response: error
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig, findConfig, getConfig, createConfig } from '../load';
|
|
2
2
|
import { RedoclyClient } from '../../redocly';
|
|
3
|
-
import { RuleConfig,
|
|
3
|
+
import { RuleConfig, FlatRawConfig } from './../types';
|
|
4
4
|
import { Config } from '../config';
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -49,7 +49,7 @@ 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({ processRawConfig: mockFn });
|
|
53
53
|
expect(mockFn).toHaveBeenCalled();
|
|
54
54
|
});
|
|
55
55
|
});
|
|
@@ -93,11 +93,10 @@ describe('getConfig', () => {
|
|
|
93
93
|
describe('createConfig', () => {
|
|
94
94
|
it('should create config from string', async () => {
|
|
95
95
|
const config = await createConfig(`
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
info-license: off
|
|
96
|
+
extends:
|
|
97
|
+
- recommended
|
|
98
|
+
rules:
|
|
99
|
+
info-license: off
|
|
101
100
|
`);
|
|
102
101
|
|
|
103
102
|
verifyExtendedConfig(config, {
|
|
@@ -107,21 +106,19 @@ describe('createConfig', () => {
|
|
|
107
106
|
});
|
|
108
107
|
|
|
109
108
|
it('should create config from object', async () => {
|
|
110
|
-
const rawConfig:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'operation-2xx-response': 'off',
|
|
117
|
-
},
|
|
109
|
+
const rawConfig: FlatRawConfig = {
|
|
110
|
+
extends: ['minimal'],
|
|
111
|
+
rules: {
|
|
112
|
+
'info-license': 'off',
|
|
113
|
+
'tag-description': 'off',
|
|
114
|
+
'operation-2xx-response': 'off',
|
|
118
115
|
},
|
|
119
116
|
};
|
|
120
117
|
const config = await createConfig(rawConfig);
|
|
121
118
|
|
|
122
119
|
verifyExtendedConfig(config, {
|
|
123
120
|
extendsRuleSet: 'minimal',
|
|
124
|
-
overridesRules: rawConfig.
|
|
121
|
+
overridesRules: rawConfig.rules as Record<string, RuleConfig>,
|
|
125
122
|
});
|
|
126
123
|
});
|
|
127
124
|
});
|
|
@@ -5,21 +5,21 @@ describe('resolving a plugin', () => {
|
|
|
5
5
|
const configPath = path.join(__dirname, 'fixtures/plugin-config.yaml');
|
|
6
6
|
|
|
7
7
|
it('should prefix rule names with the plugin id', async () => {
|
|
8
|
-
const config = await loadConfig(configPath);
|
|
8
|
+
const config = await loadConfig({ configPath });
|
|
9
9
|
const plugin = config.styleguide.plugins[0];
|
|
10
10
|
|
|
11
11
|
expect(plugin.rules?.oas3).toHaveProperty('test-plugin/openid-connect-url-well-known');
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
it('should prefix preprocessor names with the plugin id', async () => {
|
|
15
|
-
const config = await loadConfig(configPath);
|
|
15
|
+
const config = await loadConfig({ configPath });
|
|
16
16
|
const plugin = config.styleguide.plugins[0];
|
|
17
17
|
|
|
18
18
|
expect(plugin.preprocessors?.oas2).toHaveProperty('test-plugin/description-preprocessor');
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it('should prefix decorator names with the plugin id', async () => {
|
|
22
|
-
const config = await loadConfig(configPath);
|
|
22
|
+
const config = await loadConfig({ configPath });
|
|
23
23
|
const plugin = config.styleguide.plugins[0];
|
|
24
24
|
|
|
25
25
|
expect(plugin.decorators?.oas3).toHaveProperty('test-plugin/inject-x-stats');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeprecatedInRawConfig, RawConfig } from '../types';
|
|
1
|
+
import { DeprecatedInRawConfig, RawConfig, FlatRawConfig } from '../types';
|
|
2
2
|
import * as utils from '../utils';
|
|
3
3
|
|
|
4
4
|
const makeTestRawConfig = (
|
|
@@ -22,8 +22,54 @@ const makeTestRawConfig = (
|
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
const rawTestConfig: RawConfig = {
|
|
26
|
+
apis: {
|
|
27
|
+
'test@v1': {
|
|
28
|
+
root: 'root.yaml',
|
|
29
|
+
styleguide: {
|
|
30
|
+
extends: ['recommended'],
|
|
31
|
+
rules: { 'operation-2xx-response': 'error' },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
styleguide: {
|
|
36
|
+
plugins: ['test-plugin'],
|
|
37
|
+
extends: ['minimal'],
|
|
38
|
+
rules: { 'operation-4xx-response': 'warn' },
|
|
39
|
+
doNotResolveExamples: true,
|
|
40
|
+
},
|
|
41
|
+
resolve: {
|
|
42
|
+
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
|
|
43
|
+
},
|
|
44
|
+
'features.openapi': {
|
|
45
|
+
disableSidebar: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const flatTestConfig: FlatRawConfig = {
|
|
50
|
+
apis: {
|
|
51
|
+
'test@v1': {
|
|
52
|
+
root: 'root.yaml',
|
|
53
|
+
extends: ['recommended'],
|
|
54
|
+
rules: { 'operation-2xx-response': 'error' },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
plugins: ['test-plugin'],
|
|
58
|
+
extends: ['minimal'],
|
|
59
|
+
rules: {
|
|
60
|
+
'operation-4xx-response': 'warn',
|
|
61
|
+
},
|
|
62
|
+
resolve: {
|
|
63
|
+
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
|
|
64
|
+
doNotResolveExamples: true,
|
|
65
|
+
},
|
|
66
|
+
'features.openapi': {
|
|
67
|
+
disableSidebar: true,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
25
71
|
describe('transformConfig', () => {
|
|
26
|
-
it('should work for
|
|
72
|
+
it('should work for the `styleguide` syntax', () => {
|
|
27
73
|
const transformedRawConfig: RawConfig = utils.transformConfig(
|
|
28
74
|
makeTestRawConfig('styleguide', 'styleguide')
|
|
29
75
|
);
|
|
@@ -73,11 +119,25 @@ describe('transformConfig', () => {
|
|
|
73
119
|
}
|
|
74
120
|
`);
|
|
75
121
|
});
|
|
76
|
-
it('should throw an error if both
|
|
122
|
+
it('should throw an error if both `styleguide` and `lint` syntaxes used together', () => {
|
|
77
123
|
const testRawConfig = makeTestRawConfig('styleguide', 'lint');
|
|
78
124
|
testRawConfig.apiDefinitions = { legacyApiDefinition: 'file.yaml' };
|
|
79
125
|
expect(() => utils.transformConfig(testRawConfig)).toThrowError(
|
|
80
|
-
`Do not use 'apiDefinitions' field. Use 'apis' instead
|
|
126
|
+
`Do not use 'apiDefinitions' field. Use 'apis' instead. `
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
it('should transform flatten config into styleguide', () => {
|
|
130
|
+
expect(utils.transformConfig(flatTestConfig)).toEqual({
|
|
131
|
+
...rawTestConfig,
|
|
132
|
+
resolve: { ...rawTestConfig.resolve, doNotResolveExamples: true },
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
it('should transform styleguide config into styleguide identically', () => {
|
|
136
|
+
expect(utils.transformConfig(rawTestConfig)).toEqual(rawTestConfig);
|
|
137
|
+
});
|
|
138
|
+
it('should fail when there is a mixed config', () => {
|
|
139
|
+
expect(() => utils.transformConfig({ ...rawTestConfig, extends: ['recommended'] })).toThrow(
|
|
140
|
+
`Do not use 'lint', 'styleguide' and flat syntax together. \nSee more about the configuration in the docs: https://redocly.com/docs/cli/configuration/ \n`
|
|
81
141
|
);
|
|
82
142
|
});
|
|
83
143
|
});
|
package/src/config/all.ts
CHANGED
|
@@ -26,7 +26,7 @@ export default {
|
|
|
26
26
|
'operation-operationId-url-safe': 'error',
|
|
27
27
|
'operation-parameters-unique': 'error',
|
|
28
28
|
'operation-tag-defined': 'error',
|
|
29
|
-
'
|
|
29
|
+
'security-defined': 'error',
|
|
30
30
|
'operation-singular-tag': 'error',
|
|
31
31
|
'no-unresolved-refs': 'error',
|
|
32
32
|
'no-enum-type-mismatch': 'error',
|
|
@@ -52,6 +52,7 @@ export default {
|
|
|
52
52
|
'no-unused-components': 'error',
|
|
53
53
|
'no-undefined-server-variable': 'error',
|
|
54
54
|
'no-server-variables-empty-enum': 'error',
|
|
55
|
+
'operation-4xx-problem-details-rfc7807': 'error',
|
|
55
56
|
},
|
|
56
57
|
oas3_1Rules: {
|
|
57
58
|
'no-server-example.com': 'error',
|
|
@@ -61,5 +62,6 @@ export default {
|
|
|
61
62
|
'no-unused-components': 'error',
|
|
62
63
|
'no-undefined-server-variable': 'error',
|
|
63
64
|
'no-server-variables-empty-enum': 'error',
|
|
65
|
+
'operation-4xx-problem-details-rfc7807': 'error',
|
|
64
66
|
},
|
|
65
67
|
} as PluginStyleguideConfig;
|
|
@@ -21,9 +21,10 @@ import type {
|
|
|
21
21
|
DeprecatedInRawConfig,
|
|
22
22
|
} from './types';
|
|
23
23
|
import { isBrowser } from '../env';
|
|
24
|
-
import { isNotString, isString,
|
|
24
|
+
import { isNotString, isString, isDefined, parseYaml } from '../utils';
|
|
25
25
|
import { Config } from './config';
|
|
26
26
|
import { colorize, logger } from '../logger';
|
|
27
|
+
import { asserts, buildAssertCustomFunction } from '../rules/common/assertions/asserts';
|
|
27
28
|
|
|
28
29
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
29
30
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
|
@@ -175,9 +176,13 @@ export function resolvePlugins(
|
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
if (pluginModule.assertions) {
|
|
180
|
+
plugin.assertions = pluginModule.assertions;
|
|
181
|
+
}
|
|
182
|
+
|
|
178
183
|
return plugin;
|
|
179
184
|
})
|
|
180
|
-
.filter(
|
|
185
|
+
.filter(isDefined);
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
export async function resolveApis({
|
|
@@ -299,8 +304,7 @@ export async function resolveStyleguideConfig(
|
|
|
299
304
|
return {
|
|
300
305
|
...resolvedStyleguideConfig,
|
|
301
306
|
rules:
|
|
302
|
-
resolvedStyleguideConfig.rules &&
|
|
303
|
-
groupStyleguideAssertionRules(resolvedStyleguideConfig.rules),
|
|
307
|
+
resolvedStyleguideConfig.rules && groupStyleguideAssertionRules(resolvedStyleguideConfig),
|
|
304
308
|
};
|
|
305
309
|
}
|
|
306
310
|
|
|
@@ -392,9 +396,10 @@ function getMergedRawStyleguideConfig(
|
|
|
392
396
|
return resultLint;
|
|
393
397
|
}
|
|
394
398
|
|
|
395
|
-
function groupStyleguideAssertionRules(
|
|
396
|
-
rules
|
|
397
|
-
|
|
399
|
+
function groupStyleguideAssertionRules({
|
|
400
|
+
rules,
|
|
401
|
+
plugins,
|
|
402
|
+
}: ResolvedStyleguideConfig): Record<string, RuleConfig> | undefined {
|
|
398
403
|
if (!rules) {
|
|
399
404
|
return rules;
|
|
400
405
|
}
|
|
@@ -407,6 +412,24 @@ function groupStyleguideAssertionRules(
|
|
|
407
412
|
for (const [ruleKey, rule] of Object.entries(rules)) {
|
|
408
413
|
if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
|
|
409
414
|
const assertion = rule;
|
|
415
|
+
if (plugins) {
|
|
416
|
+
for (const field of Object.keys(assertion)) {
|
|
417
|
+
const [pluginId, fn] = field.split('/');
|
|
418
|
+
if (!pluginId || !fn) continue;
|
|
419
|
+
const plugin = plugins.find((plugin) => plugin.id === pluginId);
|
|
420
|
+
if (!plugin) {
|
|
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]);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
410
433
|
assertions.push({
|
|
411
434
|
...assertion,
|
|
412
435
|
assertionId: ruleKey.replace('assert/', ''),
|