@redocly/openapi-core 1.0.0-beta.94 → 1.0.0-beta.97

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.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/__tests__/bundle.test.ts +6 -6
  3. package/__tests__/fixtures/extension.js +1 -1
  4. package/__tests__/login.test.ts +2 -2
  5. package/__tests__/ref-utils.test.ts +1 -1
  6. package/__tests__/utils.ts +30 -18
  7. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
  8. package/lib/benchmark/utils.d.ts +2 -1
  9. package/lib/benchmark/utils.js +10 -7
  10. package/lib/bundle.d.ts +2 -2
  11. package/lib/config/all.d.ts +2 -2
  12. package/lib/config/builtIn.d.ts +1 -1
  13. package/lib/config/config-resolvers.d.ts +16 -0
  14. package/lib/config/config-resolvers.js +242 -0
  15. package/lib/config/config.d.ts +18 -130
  16. package/lib/config/config.js +34 -245
  17. package/lib/config/index.d.ts +7 -0
  18. package/lib/config/index.js +19 -0
  19. package/lib/config/load.d.ts +2 -1
  20. package/lib/config/load.js +20 -13
  21. package/lib/config/minimal.d.ts +2 -2
  22. package/lib/config/recommended.d.ts +2 -2
  23. package/lib/config/types.d.ts +113 -0
  24. package/lib/config/types.js +2 -0
  25. package/lib/config/utils.d.ts +13 -0
  26. package/lib/config/utils.js +160 -0
  27. package/lib/format/format.d.ts +1 -1
  28. package/lib/format/format.js +30 -1
  29. package/lib/index.d.ts +1 -2
  30. package/lib/index.js +5 -6
  31. package/lib/lint.d.ts +1 -1
  32. package/lib/lint.js +5 -7
  33. package/lib/redocly/index.d.ts +1 -1
  34. package/lib/redocly/index.js +10 -26
  35. package/lib/redocly/redocly-client-types.d.ts +1 -1
  36. package/lib/redocly/registry-api-types.d.ts +1 -0
  37. package/lib/redocly/registry-api.d.ts +2 -2
  38. package/lib/redocly/registry-api.js +2 -1
  39. package/lib/resolve.d.ts +1 -1
  40. package/lib/resolve.js +1 -2
  41. package/lib/rules/common/assertions/index.js +1 -1
  42. package/lib/rules/common/assertions/utils.d.ts +1 -1
  43. package/lib/rules/common/assertions/utils.js +6 -2
  44. package/lib/utils.d.ts +4 -2
  45. package/lib/utils.js +20 -3
  46. package/package.json +9 -6
  47. package/src/__tests__/lint.test.ts +1 -1
  48. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
  49. package/src/benchmark/benchmark.js +1 -1
  50. package/src/benchmark/utils.ts +13 -8
  51. package/src/bundle.ts +2 -1
  52. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +157 -0
  53. package/src/config/__tests__/config-resolvers.test.ts +429 -0
  54. package/src/config/__tests__/config.test.ts +17 -29
  55. package/src/config/__tests__/fixtures/plugin.js +1 -1
  56. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +12 -0
  57. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
  58. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
  59. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +19 -0
  60. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
  61. package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
  62. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
  63. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
  64. package/src/config/__tests__/load.test.ts +8 -1
  65. package/src/config/all.ts +3 -2
  66. package/src/config/builtIn.ts +2 -1
  67. package/src/config/config-resolvers.ts +359 -0
  68. package/src/config/config.ts +60 -468
  69. package/src/config/index.ts +7 -0
  70. package/src/config/load.ts +37 -31
  71. package/src/config/minimal.ts +2 -2
  72. package/src/config/recommended.ts +2 -2
  73. package/src/config/types.ts +168 -0
  74. package/src/config/utils.ts +208 -0
  75. package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
  76. package/src/format/format.ts +38 -7
  77. package/src/index.ts +6 -2
  78. package/src/lint.ts +4 -5
  79. package/src/redocly/__tests__/redocly-client.test.ts +7 -0
  80. package/src/redocly/index.ts +14 -41
  81. package/src/redocly/redocly-client-types.ts +1 -1
  82. package/src/redocly/registry-api-types.ts +1 -0
  83. package/src/redocly/registry-api.ts +5 -1
  84. package/src/resolve.ts +2 -4
  85. package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
  86. package/src/rules/common/__tests__/info-description.test.ts +3 -3
  87. package/src/rules/common/__tests__/info-license.test.ts +2 -2
  88. package/src/rules/common/__tests__/license-url.test.ts +2 -2
  89. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
  90. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
  91. package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
  92. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
  93. package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
  94. package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
  95. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
  96. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
  97. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
  98. package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
  99. package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
  100. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
  101. package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
  102. package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
  103. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  104. package/src/rules/common/__tests__/spec.test.ts +1 -1
  105. package/src/rules/common/__tests__/tag-description.test.ts +2 -2
  106. package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
  107. package/src/rules/common/assertions/index.ts +1 -1
  108. package/src/rules/common/assertions/utils.ts +5 -2
  109. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  110. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
  111. package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
  112. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  113. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
  114. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
  115. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
  116. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
  117. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  118. package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
  119. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
  120. package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
  121. package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
  122. package/src/utils.ts +21 -4
  123. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,429 @@
1
+ import { resolveLint, resolveApis, resolveConfig } from '../config-resolvers';
2
+ const path = require('path');
3
+
4
+ import type { LintRawConfig, RawConfig } from '../types';
5
+
6
+ const configPath = path.join(__dirname, 'fixtures/resolve-config/.redocly.yaml');
7
+ const baseLintConfig: LintRawConfig = {
8
+ rules: {
9
+ 'operation-2xx-response': 'warn',
10
+ },
11
+ };
12
+
13
+ const minimalLintPreset = resolveLint({
14
+ lintConfig: { ...baseLintConfig, extends: ['minimal'] },
15
+ });
16
+
17
+ const recommendedLintPreset = resolveLint({
18
+ lintConfig: { ...baseLintConfig, extends: ['recommended'] },
19
+ });
20
+
21
+ const removeAbsolutePath = (item: string) =>
22
+ item.match(/^.*\/packages\/core\/src\/config\/__tests__\/fixtures\/(.*)$/)![1];
23
+
24
+ describe('resolveLint', () => {
25
+ it('should return the config with no recommended', async () => {
26
+ const lint = await resolveLint({ lintConfig: baseLintConfig });
27
+ expect(lint.plugins?.length).toEqual(1);
28
+ expect(lint.plugins?.[0].id).toEqual('');
29
+ expect(lint.rules).toEqual({
30
+ 'operation-2xx-response': 'warn',
31
+ });
32
+ });
33
+
34
+ it('should return the config with correct order by preset', async () => {
35
+ expect(
36
+ await resolveLint({
37
+ lintConfig: { ...baseLintConfig, extends: ['minimal', 'recommended'] },
38
+ }),
39
+ ).toEqual(await recommendedLintPreset);
40
+ expect(
41
+ await resolveLint({
42
+ lintConfig: { ...baseLintConfig, extends: ['recommended', 'minimal'] },
43
+ }),
44
+ ).toEqual(await minimalLintPreset);
45
+ });
46
+
47
+ it('should return the same lintConfig when extends is empty array', async () => {
48
+ const configWithEmptyExtends = await resolveLint({
49
+ lintConfig: { ...baseLintConfig, extends: [] },
50
+ });
51
+ expect(configWithEmptyExtends.plugins?.length).toEqual(1);
52
+ expect(configWithEmptyExtends.plugins?.[0].id).toEqual('');
53
+ expect(configWithEmptyExtends.rules).toEqual({
54
+ 'operation-2xx-response': 'warn',
55
+ });
56
+ });
57
+
58
+ it('should resolve extends with local file config', async () => {
59
+ const config = {
60
+ ...baseLintConfig,
61
+ extends: ['local-config.yaml'],
62
+ };
63
+
64
+ const { plugins, ...lint } = await resolveLint({
65
+ lintConfig: config,
66
+ configPath,
67
+ });
68
+
69
+ expect(lint?.rules?.['operation-2xx-response']).toEqual('warn');
70
+ expect(plugins).toBeDefined();
71
+ expect(plugins?.length).toBe(2);
72
+
73
+ expect(lint.extendPaths!.map(removeAbsolutePath)).toEqual([
74
+ 'resolve-config/.redocly.yaml',
75
+ 'resolve-config/local-config.yaml',
76
+ 'resolve-config/.redocly.yaml',
77
+ ]);
78
+ expect(lint.pluginPaths!.map(removeAbsolutePath)).toEqual(['resolve-config/plugin.js']);
79
+
80
+ expect(lint.rules).toEqual({
81
+ 'boolean-parameter-prefixes': 'error',
82
+ 'local/operation-id-not-test': 'error',
83
+ 'no-invalid-media-type-examples': 'error',
84
+ 'operation-2xx-response': 'warn',
85
+ 'operation-description': 'error',
86
+ 'path-http-verbs-order': 'error',
87
+ });
88
+ });
89
+
90
+ // TODO: fix circular test
91
+ it.skip('should throw circular error', () => {
92
+ const config = {
93
+ ...baseLintConfig,
94
+ extends: ['local-config-with-circular.yaml'],
95
+ };
96
+ expect(() => {
97
+ resolveLint({ lintConfig: config, configPath });
98
+ }).toThrow('Circular dependency in config file');
99
+ });
100
+
101
+ it('should resolve extends with local file config witch contains path to nested config', async () => {
102
+ const lintConfig = {
103
+ extends: ['local-config-with-file.yaml'],
104
+ };
105
+ const { plugins, ...lint } = await resolveLint({
106
+ lintConfig,
107
+ configPath,
108
+ });
109
+
110
+ expect(lint?.rules?.['no-invalid-media-type-examples']).toEqual('warn');
111
+ expect(lint?.rules?.['operation-4xx-response']).toEqual('off');
112
+ expect(lint?.rules?.['operation-2xx-response']).toEqual('error');
113
+ expect(plugins).toBeDefined();
114
+ expect(plugins?.length).toBe(3);
115
+
116
+ expect(lint.extendPaths!.map(removeAbsolutePath)).toEqual([
117
+ 'resolve-config/.redocly.yaml',
118
+ 'resolve-config/local-config-with-file.yaml',
119
+ 'resolve-config/api/nested-config.yaml',
120
+ 'resolve-config/.redocly.yaml',
121
+ ]);
122
+ expect(lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
123
+ 'resolve-config/api/plugin.js',
124
+ 'resolve-config/plugin.js',
125
+ 'resolve-config/api/plugin.js',
126
+ ]);
127
+
128
+ delete lint.extendPaths;
129
+ delete lint.pluginPaths;
130
+ expect(lint).toMatchSnapshot();
131
+ });
132
+
133
+ it('should correctly merge assertions from nested config', async () => {
134
+ const lintConfig = {
135
+ extends: ['local-config-with-file.yaml'],
136
+ };
137
+
138
+ const lint = await resolveLint({
139
+ lintConfig,
140
+ configPath,
141
+ });
142
+
143
+ expect(Array.isArray(lint.rules?.assertions)).toEqual(true);
144
+ expect(lint.rules?.assertions).toMatchObject( [
145
+ {
146
+ subject: 'PathItem',
147
+ property: 'get',
148
+ message: 'Every path item must have a GET operation.',
149
+ defined: true,
150
+ assertionId: 'path-item-get-defined'
151
+ },
152
+ {
153
+ subject: 'Tag',
154
+ property: 'description',
155
+ message: 'Tag description must be at least 13 characters and end with a full stop.',
156
+ severity: 'error',
157
+ minLength: 13,
158
+ pattern: '/\\.$/',
159
+ assertionId: 'tag-description'
160
+ }
161
+ ])
162
+ });
163
+
164
+ it('should resolve extends with url file config witch contains path to nested config', async () => {
165
+ const lintConfig = {
166
+ // This points to ./fixtures/resolve-remote-configs/remote-config.yaml
167
+ extends: [
168
+ 'https://raw.githubusercontent.com/Redocly/openapi-cli/master/packages/core/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml',
169
+ ],
170
+ };
171
+
172
+ const { plugins, ...lint } = await resolveLint({
173
+ lintConfig,
174
+ configPath,
175
+ });
176
+
177
+ expect(lint?.rules?.['operation-4xx-response']).toEqual('error');
178
+ expect(lint?.rules?.['operation-2xx-response']).toEqual('error');
179
+ expect(Object.keys(lint.rules || {}).length).toBe(2);
180
+
181
+ expect(lint.extendPaths!.map(removeAbsolutePath)).toEqual([
182
+ 'resolve-config/.redocly.yaml',
183
+ 'resolve-config/.redocly.yaml',
184
+ ]);
185
+ expect(lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
186
+ });
187
+ });
188
+
189
+ describe('resolveApis', () => {
190
+ it('should resolve apis lintConfig and merge minimal extends', async () => {
191
+ const rawConfig: RawConfig = {
192
+ apis: {
193
+ petstore: {
194
+ root: 'some/path',
195
+ lint: {},
196
+ },
197
+ },
198
+ lint: {
199
+ extends: ['minimal'],
200
+ },
201
+ };
202
+ const apisResult = await resolveApis({ rawConfig });
203
+ expect(apisResult['petstore'].lint).toEqual(await minimalLintPreset);
204
+ });
205
+
206
+ it('should not merge recommended extends by default by every level', async () => {
207
+ const rawConfig: RawConfig = {
208
+ apis: {
209
+ petstore: {
210
+ root: 'some/path',
211
+ lint: {},
212
+ },
213
+ },
214
+ lint: {},
215
+ };
216
+
217
+ const apisResult = await resolveApis({ rawConfig, configPath });
218
+
219
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
220
+ 'resolve-config/.redocly.yaml',
221
+ ]);
222
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
223
+
224
+ expect(apisResult['petstore'].lint.rules).toEqual({});
225
+ //@ts-ignore
226
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(1);
227
+ //@ts-ignore
228
+ expect(apisResult['petstore'].lint.plugins[0].id).toEqual('');
229
+ });
230
+
231
+ it('should resolve apis lintConfig when it contains file and not set recommended', async () => {
232
+ const rawConfig: RawConfig = {
233
+ apis: {
234
+ petstore: {
235
+ root: 'some/path',
236
+ lint: {
237
+ rules: {
238
+ 'operation-4xx-response': 'error',
239
+ },
240
+ },
241
+ },
242
+ },
243
+ lint: {
244
+ rules: {
245
+ 'operation-2xx-response': 'warn',
246
+ },
247
+ },
248
+ };
249
+
250
+ const apisResult = await resolveApis({ rawConfig, configPath });
251
+ expect(apisResult['petstore'].lint.rules).toEqual({
252
+ 'operation-2xx-response': 'warn',
253
+ 'operation-4xx-response': 'error',
254
+ });
255
+ //@ts-ignore
256
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(1);
257
+ //@ts-ignore
258
+ expect(apisResult['petstore'].lint.plugins[0].id).toEqual('');
259
+
260
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
261
+ 'resolve-config/.redocly.yaml',
262
+ ]);
263
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
264
+ });
265
+
266
+ it('should resolve apis lintConfig when it contains file', async () => {
267
+ const rawConfig: RawConfig = {
268
+ apis: {
269
+ petstore: {
270
+ root: 'some/path',
271
+ lint: {
272
+ extends: ['local-config.yaml'],
273
+ rules: {
274
+ 'operation-4xx-response': 'error',
275
+ },
276
+ },
277
+ },
278
+ },
279
+ lint: {
280
+ extends: ['minimal'],
281
+ rules: {
282
+ 'operation-2xx-response': 'warn',
283
+ },
284
+ },
285
+ };
286
+
287
+ const apisResult = await resolveApis({ rawConfig, configPath });
288
+ expect(apisResult['petstore'].lint.rules).toBeDefined();
289
+ expect(apisResult['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn'); // think about prioritize in merge ???
290
+ expect(apisResult['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
291
+ expect(apisResult['petstore'].lint.rules?.['local/operation-id-not-test']).toEqual('error');
292
+ //@ts-ignore
293
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(2);
294
+
295
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
296
+ 'resolve-config/.redocly.yaml',
297
+ 'resolve-config/local-config.yaml',
298
+ 'resolve-config/.redocly.yaml',
299
+ ]);
300
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
301
+ 'resolve-config/plugin.js',
302
+ ]);
303
+ });
304
+ });
305
+
306
+ describe('resolveConfig', () => {
307
+ it('should add recommended to top level by default', async () => {
308
+ const rawConfig: RawConfig = {
309
+ apis: {
310
+ petstore: {
311
+ root: 'some/path',
312
+ lint: {
313
+ rules: {
314
+ 'operation-4xx-response': 'error',
315
+ },
316
+ },
317
+ },
318
+ },
319
+ lint: {
320
+ rules: {
321
+ 'operation-2xx-response': 'warn',
322
+ },
323
+ },
324
+ };
325
+
326
+ const { apis } = await resolveConfig(rawConfig, configPath);
327
+ //@ts-ignore
328
+ expect(apis['petstore'].lint.plugins.length).toEqual(1);
329
+ //@ts-ignore
330
+ expect(apis['petstore'].lint.plugins[0].id).toEqual('');
331
+
332
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
333
+ 'resolve-config/.redocly.yaml',
334
+ ]);
335
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
336
+
337
+ expect(apis['petstore'].lint.rules).toEqual({
338
+ ...(await recommendedLintPreset).rules,
339
+ 'operation-2xx-response': 'warn',
340
+ 'operation-4xx-response': 'error',
341
+ });
342
+ });
343
+
344
+ it('should not add recommended to top level by default when apis have extends file', async () => {
345
+ const rawConfig: RawConfig = {
346
+ apis: {
347
+ petstore: {
348
+ root: 'some/path',
349
+ lint: {
350
+ extends: ['local-config.yaml'],
351
+ rules: {
352
+ 'operation-4xx-response': 'error',
353
+ },
354
+ },
355
+ },
356
+ },
357
+ lint: {
358
+ rules: {
359
+ 'operation-2xx-response': 'warn',
360
+ },
361
+ },
362
+ };
363
+
364
+ const { apis } = await resolveConfig(rawConfig, configPath);
365
+ expect(apis['petstore'].lint.rules).toBeDefined();
366
+ expect(Object.keys(apis['petstore'].lint.rules || {}).length).toEqual(7);
367
+ expect(apis['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn');
368
+ expect(apis['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
369
+ expect(apis['petstore'].lint.rules?.['operation-description']).toEqual('error'); // from extends file config
370
+ //@ts-ignore
371
+ expect(apis['petstore'].lint.plugins.length).toEqual(2);
372
+
373
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
374
+ 'resolve-config/.redocly.yaml',
375
+ 'resolve-config/local-config.yaml',
376
+ 'resolve-config/.redocly.yaml',
377
+ ]);
378
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
379
+ 'resolve-config/plugin.js',
380
+ ]);
381
+
382
+ expect(apis['petstore'].lint.recommendedFallback).toBe(false);
383
+ });
384
+
385
+ it('should ignore minimal from the root and read local file', async () => {
386
+ const rawConfig: RawConfig = {
387
+ apis: {
388
+ petstore: {
389
+ root: 'some/path',
390
+ lint: {
391
+ extends: ['recommended', 'local-config.yaml'],
392
+ rules: {
393
+ 'operation-4xx-response': 'error',
394
+ },
395
+ },
396
+ },
397
+ },
398
+ lint: {
399
+ extends: ['minimal'],
400
+ rules: {
401
+ 'operation-2xx-response': 'warn',
402
+ },
403
+ },
404
+ };
405
+
406
+ const { apis } = await resolveConfig(rawConfig, configPath);
407
+ expect(apis['petstore'].lint.rules).toBeDefined();
408
+ expect(apis['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn');
409
+ expect(apis['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
410
+ expect(apis['petstore'].lint.rules?.['operation-description']).toEqual('error'); // from extends file config
411
+ //@ts-ignore
412
+ expect(apis['petstore'].lint.plugins.length).toEqual(2);
413
+ //@ts-ignore
414
+ delete apis['petstore'].lint.plugins;
415
+
416
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
417
+ 'resolve-config/.redocly.yaml',
418
+ 'resolve-config/local-config.yaml',
419
+ 'resolve-config/.redocly.yaml',
420
+ ]);
421
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
422
+ 'resolve-config/plugin.js',
423
+ ]);
424
+
425
+ delete apis['petstore'].lint.extendPaths;
426
+ delete apis['petstore'].lint.pluginPaths;
427
+ expect(apis['petstore'].lint).toMatchSnapshot();
428
+ });
429
+ });
@@ -1,16 +1,16 @@
1
- import { Config, getMergedConfig } from '../config';
1
+ import { Config } from '../config';
2
+ import { getMergedConfig } from '../utils';
2
3
 
3
4
  const testConfig: Config = {
4
5
  rawConfig: {
5
6
  apis: {
6
7
  'test@v1': {
7
8
  root: 'resources/pets.yaml',
8
- lint: { extends: [], rules: { 'operation-summary': 'warn' } },
9
+ lint: { rules: { 'operation-summary': 'warn' } },
9
10
  },
10
11
  },
11
12
  organization: 'redocly-test',
12
13
  lint: {
13
- extends: [],
14
14
  rules: { 'operation-summary': 'error', 'no-empty-servers': 'error' },
15
15
  plugins: [],
16
16
  },
@@ -19,13 +19,12 @@ const testConfig: Config = {
19
19
  apis: {
20
20
  'test@v1': {
21
21
  root: 'resources/pets.yaml',
22
- lint: { extends: [], rules: { 'operation-summary': 'warn' } },
22
+ lint: { rules: { 'operation-summary': 'warn' } },
23
23
  },
24
24
  },
25
25
  // @ts-ignore
26
26
  lint: {
27
27
  rawConfig: {
28
- extends: [],
29
28
  rules: { 'operation-summary': 'error', 'no-empty-servers': 'error' },
30
29
  plugins: [],
31
30
  },
@@ -51,13 +50,12 @@ const testConfig: Config = {
51
50
  };
52
51
 
53
52
  describe('getMergedConfig', () => {
54
- it('should merge lint defined in "apis" section the root one', () => {
53
+ it('should get lint defined in "apis" section', () => {
55
54
  expect(getMergedConfig(testConfig, 'test@v1')).toMatchInlineSnapshot(`
56
55
  Config {
57
56
  "apis": Object {
58
57
  "test@v1": Object {
59
58
  "lint": Object {
60
- "extends": Array [],
61
59
  "rules": Object {
62
60
  "operation-summary": "warn",
63
61
  },
@@ -78,7 +76,9 @@ describe('getMergedConfig', () => {
78
76
  "oas3_1": Object {},
79
77
  },
80
78
  "doNotResolveExamples": false,
79
+ "extendPaths": Array [],
81
80
  "ignore": Object {},
81
+ "pluginPaths": Array [],
82
82
  "plugins": Array [],
83
83
  "preprocessors": Object {
84
84
  "oas2": Object {},
@@ -86,27 +86,21 @@ describe('getMergedConfig', () => {
86
86
  "oas3_1": Object {},
87
87
  },
88
88
  "rawConfig": Object {
89
- "decorators": Object {},
90
- "extends": Array [],
91
- "plugins": Array [],
92
- "preprocessors": Object {},
89
+ "extendPaths": Array [],
90
+ "pluginPaths": Array [],
93
91
  "rules": Object {
94
- "no-empty-servers": "error",
95
92
  "operation-summary": "warn",
96
93
  },
97
94
  },
98
95
  "recommendedFallback": false,
99
96
  "rules": Object {
100
97
  "oas2": Object {
101
- "no-empty-servers": "error",
102
98
  "operation-summary": "warn",
103
99
  },
104
100
  "oas3_0": Object {
105
- "no-empty-servers": "error",
106
101
  "operation-summary": "warn",
107
102
  },
108
103
  "oas3_1": Object {
109
- "no-empty-servers": "error",
110
104
  "operation-summary": "warn",
111
105
  },
112
106
  },
@@ -116,7 +110,6 @@ describe('getMergedConfig', () => {
116
110
  "apis": Object {
117
111
  "test@v1": Object {
118
112
  "lint": Object {
119
- "extends": Array [],
120
113
  "rules": Object {
121
114
  "operation-summary": "warn",
122
115
  },
@@ -127,12 +120,9 @@ describe('getMergedConfig', () => {
127
120
  "features.mockServer": Object {},
128
121
  "features.openapi": Object {},
129
122
  "lint": Object {
130
- "decorators": Object {},
131
- "extends": Array [],
132
- "plugins": Array [],
133
- "preprocessors": Object {},
123
+ "extendPaths": Array [],
124
+ "pluginPaths": Array [],
134
125
  "rules": Object {
135
- "no-empty-servers": "error",
136
126
  "operation-summary": "warn",
137
127
  },
138
128
  },
@@ -162,7 +152,6 @@ describe('getMergedConfig', () => {
162
152
  "apis": Object {
163
153
  "test@v1": Object {
164
154
  "lint": Object {
165
- "extends": Array [],
166
155
  "rules": Object {
167
156
  "operation-summary": "warn",
168
157
  },
@@ -183,7 +172,9 @@ describe('getMergedConfig', () => {
183
172
  "oas3_1": Object {},
184
173
  },
185
174
  "doNotResolveExamples": false,
175
+ "extendPaths": Array [],
186
176
  "ignore": Object {},
177
+ "pluginPaths": Array [],
187
178
  "plugins": Array [],
188
179
  "preprocessors": Object {
189
180
  "oas2": Object {},
@@ -191,10 +182,9 @@ describe('getMergedConfig', () => {
191
182
  "oas3_1": Object {},
192
183
  },
193
184
  "rawConfig": Object {
194
- "decorators": Object {},
195
- "extends": Array [],
185
+ "extendPaths": Array [],
186
+ "pluginPaths": Array [],
196
187
  "plugins": Array [],
197
- "preprocessors": Object {},
198
188
  "rules": Object {
199
189
  "no-empty-servers": "error",
200
190
  "operation-summary": "error",
@@ -221,7 +211,6 @@ describe('getMergedConfig', () => {
221
211
  "apis": Object {
222
212
  "test@v1": Object {
223
213
  "lint": Object {
224
- "extends": Array [],
225
214
  "rules": Object {
226
215
  "operation-summary": "warn",
227
216
  },
@@ -232,10 +221,9 @@ describe('getMergedConfig', () => {
232
221
  "features.mockServer": Object {},
233
222
  "features.openapi": Object {},
234
223
  "lint": Object {
235
- "decorators": Object {},
236
- "extends": Array [],
224
+ "extendPaths": Array [],
225
+ "pluginPaths": Array [],
237
226
  "plugins": Array [],
238
- "preprocessors": Object {},
239
227
  "rules": Object {
240
228
  "no-empty-servers": "error",
241
229
  "operation-summary": "error",
@@ -21,7 +21,7 @@ const rules = {
21
21
  return {
22
22
  SecurityScheme(scheme, { location, report }) {
23
23
  if (scheme.type === 'openIdConnect') {
24
- if (!scheme.openIdConnectUrl?.endsWith('/.well-known/openid-configuration')) {
24
+ if (!scheme.openIdConnectUrl.endsWith('/.well-known/openid-configuration')) {
25
25
  report({
26
26
  message:
27
27
  'openIdConnectUrl must be a URL that ends with /.well-known/openid-configuration',
@@ -0,0 +1,12 @@
1
+ lint:
2
+ plugins:
3
+ - plugin.js
4
+ rules:
5
+ operation-2xx-response: error
6
+ assert/path-item-get-defined:
7
+ subject: PathItem
8
+ property: get
9
+ message: Every path item must have a GET operation.
10
+ defined: true
11
+ extends:
12
+ - test-plugin-nested/all
@@ -0,0 +1,67 @@
1
+ const id = 'test-plugin-nested';
2
+
3
+ /** @type {import('../../config').PreprocessorsConfig} */
4
+ const preprocessors = {
5
+ oas2: {
6
+ 'description-preprocessor': () => {
7
+ return {
8
+ Info(info) {
9
+ const title = info.title || 'API title';
10
+ info.description = `# ${title}\n\n${info.description || ''}`;
11
+ },
12
+ };
13
+ },
14
+ },
15
+ };
16
+
17
+ /** @type {import('../../config').CustomRulesConfig} */
18
+ const rules = {
19
+ oas3: {
20
+ 'openid-connect-url-well-known': () => {
21
+ return {
22
+ SecurityScheme(scheme, { location, report }) {
23
+ if (scheme.type === 'openIdConnect') {
24
+ if (scheme.openIdConnectUrl && !scheme.openIdConnectUrl.endsWith('/.well-known/openid-configuration')) {
25
+ report({
26
+ message:
27
+ 'openIdConnectUrl must be a URL that ends with /.well-known/openid-configuration',
28
+ location: location.child('openIdConnectUrl'),
29
+ });
30
+ }
31
+ }
32
+ },
33
+ };
34
+ },
35
+ },
36
+ };
37
+
38
+ /** @type {import('../../config').DecoratorsConfig} */
39
+ const decorators = {
40
+ oas3: {
41
+ 'inject-x-stats': () => {
42
+ return {
43
+ Info(info) {
44
+ info['x-stats'] = { test: 1 };
45
+ },
46
+ };
47
+ },
48
+ },
49
+ };
50
+
51
+ const configs = {
52
+ all: {
53
+ rules: {
54
+ 'local/operation-id-not-test': 'error',
55
+ 'boolean-parameter-prefixes': 'error',
56
+ },
57
+ },
58
+ };
59
+
60
+
61
+ module.exports = {
62
+ id,
63
+ preprocessors,
64
+ rules,
65
+ decorators,
66
+ configs,
67
+ };
@@ -0,0 +1,8 @@
1
+ lint:
2
+ extends:
3
+ - local-config-with-circular.yaml
4
+ rules:
5
+ no-invalid-media-type-examples: error
6
+ operation-description: error
7
+ path-http-verbs-order: error
8
+ operation-2xx-response: off