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

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 (115) hide show
  1. package/__tests__/bundle.test.ts +6 -6
  2. package/__tests__/fixtures/extension.js +1 -1
  3. package/__tests__/login.test.ts +2 -2
  4. package/__tests__/ref-utils.test.ts +1 -1
  5. package/__tests__/utils.ts +30 -18
  6. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
  7. package/lib/benchmark/utils.d.ts +2 -1
  8. package/lib/benchmark/utils.js +10 -7
  9. package/lib/bundle.d.ts +2 -2
  10. package/lib/config/all.d.ts +2 -2
  11. package/lib/config/builtIn.d.ts +1 -1
  12. package/lib/config/config-resolvers.d.ts +16 -0
  13. package/lib/config/config-resolvers.js +213 -0
  14. package/lib/config/config.d.ts +14 -129
  15. package/lib/config/config.js +17 -234
  16. package/lib/config/index.d.ts +7 -0
  17. package/lib/config/index.js +19 -0
  18. package/lib/config/load.d.ts +2 -1
  19. package/lib/config/load.js +20 -13
  20. package/lib/config/minimal.d.ts +2 -2
  21. package/lib/config/recommended.d.ts +2 -2
  22. package/lib/config/types.d.ts +113 -0
  23. package/lib/config/types.js +2 -0
  24. package/lib/config/utils.d.ts +13 -0
  25. package/lib/config/utils.js +160 -0
  26. package/lib/format/format.d.ts +1 -1
  27. package/lib/format/format.js +28 -0
  28. package/lib/index.d.ts +1 -2
  29. package/lib/index.js +5 -6
  30. package/lib/lint.d.ts +1 -1
  31. package/lib/lint.js +5 -7
  32. package/lib/redocly/index.d.ts +1 -1
  33. package/lib/redocly/redocly-client-types.d.ts +1 -1
  34. package/lib/redocly/registry-api.d.ts +1 -1
  35. package/lib/resolve.d.ts +1 -1
  36. package/lib/resolve.js +1 -2
  37. package/lib/rules/common/assertions/utils.d.ts +1 -1
  38. package/lib/rules/common/assertions/utils.js +6 -2
  39. package/lib/utils.d.ts +4 -1
  40. package/lib/utils.js +18 -1
  41. package/package.json +5 -2
  42. package/src/__tests__/lint.test.ts +1 -1
  43. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
  44. package/src/benchmark/utils.ts +13 -8
  45. package/src/bundle.ts +2 -1
  46. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +140 -0
  47. package/src/config/__tests__/config-resolvers.test.ts +398 -0
  48. package/src/config/__tests__/config.test.ts +17 -29
  49. package/src/config/__tests__/fixtures/plugin.js +1 -1
  50. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +7 -0
  51. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
  52. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
  53. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +12 -0
  54. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
  55. package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
  56. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
  57. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
  58. package/src/config/__tests__/load.test.ts +8 -1
  59. package/src/config/all.ts +3 -2
  60. package/src/config/builtIn.ts +2 -1
  61. package/src/config/config-resolvers.ts +304 -0
  62. package/src/config/config.ts +40 -457
  63. package/src/config/index.ts +7 -0
  64. package/src/config/load.ts +37 -31
  65. package/src/config/minimal.ts +2 -2
  66. package/src/config/recommended.ts +2 -2
  67. package/src/config/types.ts +168 -0
  68. package/src/config/utils.ts +208 -0
  69. package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
  70. package/src/format/format.ts +36 -6
  71. package/src/index.ts +6 -2
  72. package/src/lint.ts +4 -5
  73. package/src/redocly/__tests__/redocly-client.test.ts +7 -0
  74. package/src/redocly/index.ts +3 -1
  75. package/src/redocly/redocly-client-types.ts +1 -1
  76. package/src/redocly/registry-api.ts +3 -1
  77. package/src/resolve.ts +2 -4
  78. package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
  79. package/src/rules/common/__tests__/info-description.test.ts +3 -3
  80. package/src/rules/common/__tests__/info-license.test.ts +2 -2
  81. package/src/rules/common/__tests__/license-url.test.ts +2 -2
  82. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
  83. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
  84. package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
  85. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
  86. package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
  87. package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
  88. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
  89. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
  90. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
  91. package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
  92. package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
  93. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
  94. package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
  95. package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
  96. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  97. package/src/rules/common/__tests__/spec.test.ts +1 -1
  98. package/src/rules/common/__tests__/tag-description.test.ts +2 -2
  99. package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
  100. package/src/rules/common/assertions/utils.ts +5 -2
  101. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  102. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
  103. package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
  104. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  105. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
  106. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
  107. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
  108. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
  109. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  110. package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
  111. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
  112. package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
  113. package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
  114. package/src/utils.ts +18 -2
  115. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,9 @@
1
1
  import { parseYaml } from '../js-yaml';
2
2
  import { Document, Source } from '../resolve';
3
3
  import { Oas3RuleSet } from '../oas-types';
4
- import { RuleConfig, LintConfig, Plugin } from '../config/config';
4
+ import { LintConfig, mergeExtends, resolvePlugins } from '../config';
5
+
6
+ import type { RuleConfig, Plugin, ResolvedLintConfig } from '../config/types';
5
7
 
6
8
  export function parseYamlToDocument(body: string, absoluteRef: string = ''): Document {
7
9
  return {
@@ -16,16 +18,19 @@ export function makeConfigForRuleset(rules: Oas3RuleSet, plugin?: Partial<Plugin
16
18
  Object.keys(rules).forEach((name) => {
17
19
  rulesConf[`${ruleId}/${name}`] = 'error';
18
20
  });
19
-
20
- return new LintConfig({
21
- plugins: [
21
+ const extendConfigs = [
22
+ resolvePlugins([
22
23
  {
23
24
  ...plugin,
24
25
  id: ruleId,
25
26
  rules: { oas3: rules },
26
27
  },
27
- ],
28
- extends: [],
29
- rules: rulesConf,
30
- });
28
+ ]) as ResolvedLintConfig,
29
+ ];
30
+ if (rules) {
31
+ extendConfigs.push({ rules });
32
+ }
33
+ const lint = mergeExtends(extendConfigs);
34
+
35
+ return new LintConfig(lint);
31
36
  }
package/src/bundle.ts CHANGED
@@ -8,7 +8,6 @@ import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
8
8
  import { WalkContext, walkDocument, UserContext, ResolveResult } from './walk';
9
9
  import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
10
10
  import { isRef, Location, refBaseName } from './ref-utils';
11
- import type { Config, LintConfig } from './config/config';
12
11
  import { initRules } from './config/rules';
13
12
  import { reportUnresolvedRef } from './rules/no-unresolved-refs';
14
13
  import { isPlainObject } from './utils';
@@ -17,6 +16,8 @@ import { isRedoclyRegistryURL } from './redocly';
17
16
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components';
18
17
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './rules/oas3/remove-unused-components';
19
18
 
19
+ import type { Config, LintConfig } from './config';
20
+
20
21
  export type Oas3RuleSet = Record<string, Oas3Rule>;
21
22
 
22
23
  export enum OasVersion {
@@ -0,0 +1,140 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`resolveConfig should ignore minimal from the root and read local file 1`] = `
4
+ Object {
5
+ "decorators": Object {},
6
+ "doNotResolveExamples": undefined,
7
+ "oas2Decorators": Object {},
8
+ "oas2Preprocessors": Object {},
9
+ "oas2Rules": Object {},
10
+ "oas3_0Decorators": Object {},
11
+ "oas3_0Preprocessors": Object {},
12
+ "oas3_0Rules": Object {
13
+ "no-empty-servers": "error",
14
+ "no-example-value-and-externalValue": "error",
15
+ "no-invalid-media-type-examples": "error",
16
+ "no-server-example.com": "warn",
17
+ "no-server-trailing-slash": "error",
18
+ "no-servers-empty-enum": "error",
19
+ "no-undefined-server-variable": "error",
20
+ "no-unused-components": "warn",
21
+ },
22
+ "oas3_1Decorators": Object {},
23
+ "oas3_1Preprocessors": Object {},
24
+ "oas3_1Rules": Object {
25
+ "no-empty-servers": "error",
26
+ "no-example-value-and-externalValue": "error",
27
+ "no-server-example.com": "warn",
28
+ "no-server-trailing-slash": "error",
29
+ "no-servers-empty-enum": "error",
30
+ "no-undefined-server-variable": "error",
31
+ "no-unused-components": "warn",
32
+ },
33
+ "preprocessors": Object {},
34
+ "recommendedFallback": false,
35
+ "rules": Object {
36
+ "assertions": "warn",
37
+ "boolean-parameter-prefixes": "error",
38
+ "info-contact": "off",
39
+ "info-description": "warn",
40
+ "info-license": "warn",
41
+ "info-license-url": "warn",
42
+ "local/operation-id-not-test": "error",
43
+ "no-ambiguous-paths": "warn",
44
+ "no-enum-type-mismatch": "error",
45
+ "no-identical-paths": "error",
46
+ "no-invalid-media-type-examples": "error",
47
+ "no-path-trailing-slash": "error",
48
+ "no-unresolved-refs": "error",
49
+ "operation-2xx-response": "warn",
50
+ "operation-4xx-response": "error",
51
+ "operation-description": "error",
52
+ "operation-operationId": "warn",
53
+ "operation-operationId-unique": "error",
54
+ "operation-operationId-url-safe": "error",
55
+ "operation-parameters-unique": "error",
56
+ "operation-security-defined": "error",
57
+ "operation-singular-tag": "off",
58
+ "operation-summary": "error",
59
+ "operation-tag-defined": "off",
60
+ "parameter-description": "off",
61
+ "path-declaration-must-exist": "error",
62
+ "path-http-verbs-order": "error",
63
+ "path-not-include-query": "error",
64
+ "path-parameters-defined": "error",
65
+ "paths-kebab-case": "off",
66
+ "spec": "error",
67
+ "tag-description": "warn",
68
+ "tags-alphabetical": "off",
69
+ },
70
+ }
71
+ `;
72
+
73
+ exports[`resolveLint should resolve extends with local file config witch contains path to nested config 1`] = `
74
+ Object {
75
+ "decorators": Object {},
76
+ "doNotResolveExamples": undefined,
77
+ "oas2Decorators": Object {},
78
+ "oas2Preprocessors": Object {},
79
+ "oas2Rules": Object {},
80
+ "oas3_0Decorators": Object {},
81
+ "oas3_0Preprocessors": Object {},
82
+ "oas3_0Rules": Object {
83
+ "no-empty-servers": "error",
84
+ "no-example-value-and-externalValue": "error",
85
+ "no-invalid-media-type-examples": "warn",
86
+ "no-server-example.com": "warn",
87
+ "no-server-trailing-slash": "error",
88
+ "no-servers-empty-enum": "error",
89
+ "no-undefined-server-variable": "error",
90
+ "no-unused-components": "warn",
91
+ },
92
+ "oas3_1Decorators": Object {},
93
+ "oas3_1Preprocessors": Object {},
94
+ "oas3_1Rules": Object {
95
+ "no-empty-servers": "error",
96
+ "no-example-value-and-externalValue": "error",
97
+ "no-server-example.com": "warn",
98
+ "no-server-trailing-slash": "error",
99
+ "no-servers-empty-enum": "error",
100
+ "no-undefined-server-variable": "error",
101
+ "no-unused-components": "warn",
102
+ },
103
+ "preprocessors": Object {},
104
+ "recommendedFallback": undefined,
105
+ "rules": Object {
106
+ "assertions": "warn",
107
+ "boolean-parameter-prefixes": "error",
108
+ "info-contact": "off",
109
+ "info-description": "warn",
110
+ "info-license": "warn",
111
+ "info-license-url": "warn",
112
+ "local/operation-id-not-test": "error",
113
+ "no-ambiguous-paths": "warn",
114
+ "no-enum-type-mismatch": "error",
115
+ "no-identical-paths": "error",
116
+ "no-invalid-media-type-examples": "warn",
117
+ "no-path-trailing-slash": "error",
118
+ "no-unresolved-refs": "error",
119
+ "operation-2xx-response": "error",
120
+ "operation-4xx-response": "off",
121
+ "operation-description": "off",
122
+ "operation-operationId": "warn",
123
+ "operation-operationId-unique": "error",
124
+ "operation-operationId-url-safe": "error",
125
+ "operation-parameters-unique": "error",
126
+ "operation-security-defined": "error",
127
+ "operation-singular-tag": "off",
128
+ "operation-summary": "error",
129
+ "operation-tag-defined": "off",
130
+ "parameter-description": "off",
131
+ "path-declaration-must-exist": "error",
132
+ "path-not-include-query": "error",
133
+ "path-parameters-defined": "error",
134
+ "paths-kebab-case": "off",
135
+ "spec": "error",
136
+ "tag-description": "warn",
137
+ "tags-alphabetical": "off",
138
+ },
139
+ }
140
+ `;
@@ -0,0 +1,398 @@
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 resolve extends with url file config witch contains path to nested config', async () => {
134
+ const lintConfig = {
135
+ // This points to ./fixtures/resolve-remote-configs/remote-config.yaml
136
+ extends: [
137
+ 'https://raw.githubusercontent.com/Redocly/openapi-cli/master/packages/core/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml',
138
+ ],
139
+ };
140
+
141
+ const { plugins, ...lint } = await resolveLint({
142
+ lintConfig,
143
+ configPath,
144
+ });
145
+
146
+ expect(lint?.rules?.['operation-4xx-response']).toEqual('error');
147
+ expect(lint?.rules?.['operation-2xx-response']).toEqual('error');
148
+ expect(Object.keys(lint.rules || {}).length).toBe(2);
149
+
150
+ expect(lint.extendPaths!.map(removeAbsolutePath)).toEqual([
151
+ 'resolve-config/.redocly.yaml',
152
+ 'resolve-config/.redocly.yaml',
153
+ ]);
154
+ expect(lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
155
+ });
156
+ });
157
+
158
+ describe('resolveApis', () => {
159
+ it('should resolve apis lintConfig and merge minimal extends', async () => {
160
+ const rawConfig: RawConfig = {
161
+ apis: {
162
+ petstore: {
163
+ root: 'some/path',
164
+ lint: {},
165
+ },
166
+ },
167
+ lint: {
168
+ extends: ['minimal'],
169
+ },
170
+ };
171
+ const apisResult = await resolveApis({ rawConfig });
172
+ expect(apisResult['petstore'].lint).toEqual(await minimalLintPreset);
173
+ });
174
+
175
+ it('should not merge recommended extends by default by every level', async () => {
176
+ const rawConfig: RawConfig = {
177
+ apis: {
178
+ petstore: {
179
+ root: 'some/path',
180
+ lint: {},
181
+ },
182
+ },
183
+ lint: {},
184
+ };
185
+
186
+ const apisResult = await resolveApis({ rawConfig, configPath });
187
+
188
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
189
+ 'resolve-config/.redocly.yaml',
190
+ ]);
191
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
192
+
193
+ expect(apisResult['petstore'].lint.rules).toEqual({});
194
+ //@ts-ignore
195
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(1);
196
+ //@ts-ignore
197
+ expect(apisResult['petstore'].lint.plugins[0].id).toEqual('');
198
+ });
199
+
200
+ it('should resolve apis lintConfig when it contains file and not set recommended', async () => {
201
+ const rawConfig: RawConfig = {
202
+ apis: {
203
+ petstore: {
204
+ root: 'some/path',
205
+ lint: {
206
+ rules: {
207
+ 'operation-4xx-response': 'error',
208
+ },
209
+ },
210
+ },
211
+ },
212
+ lint: {
213
+ rules: {
214
+ 'operation-2xx-response': 'warn',
215
+ },
216
+ },
217
+ };
218
+
219
+ const apisResult = await resolveApis({ rawConfig, configPath });
220
+ expect(apisResult['petstore'].lint.rules).toEqual({
221
+ 'operation-2xx-response': 'warn',
222
+ 'operation-4xx-response': 'error',
223
+ });
224
+ //@ts-ignore
225
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(1);
226
+ //@ts-ignore
227
+ expect(apisResult['petstore'].lint.plugins[0].id).toEqual('');
228
+
229
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
230
+ 'resolve-config/.redocly.yaml',
231
+ ]);
232
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
233
+ });
234
+
235
+ it('should resolve apis lintConfig when it contains file', async () => {
236
+ const rawConfig: RawConfig = {
237
+ apis: {
238
+ petstore: {
239
+ root: 'some/path',
240
+ lint: {
241
+ extends: ['local-config.yaml'],
242
+ rules: {
243
+ 'operation-4xx-response': 'error',
244
+ },
245
+ },
246
+ },
247
+ },
248
+ lint: {
249
+ extends: ['minimal'],
250
+ rules: {
251
+ 'operation-2xx-response': 'warn',
252
+ },
253
+ },
254
+ };
255
+
256
+ const apisResult = await resolveApis({ rawConfig, configPath });
257
+ expect(apisResult['petstore'].lint.rules).toBeDefined();
258
+ expect(apisResult['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn'); // think about prioritize in merge ???
259
+ expect(apisResult['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
260
+ expect(apisResult['petstore'].lint.rules?.['local/operation-id-not-test']).toEqual('error');
261
+ //@ts-ignore
262
+ expect(apisResult['petstore'].lint.plugins.length).toEqual(2);
263
+
264
+ expect(apisResult['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
265
+ 'resolve-config/.redocly.yaml',
266
+ 'resolve-config/local-config.yaml',
267
+ 'resolve-config/.redocly.yaml',
268
+ ]);
269
+ expect(apisResult['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
270
+ 'resolve-config/plugin.js',
271
+ ]);
272
+ });
273
+ });
274
+
275
+ describe('resolveConfig', () => {
276
+ it('should add recommended to top level by default', async () => {
277
+ const rawConfig: RawConfig = {
278
+ apis: {
279
+ petstore: {
280
+ root: 'some/path',
281
+ lint: {
282
+ rules: {
283
+ 'operation-4xx-response': 'error',
284
+ },
285
+ },
286
+ },
287
+ },
288
+ lint: {
289
+ rules: {
290
+ 'operation-2xx-response': 'warn',
291
+ },
292
+ },
293
+ };
294
+
295
+ const { apis } = await resolveConfig(rawConfig, configPath);
296
+ //@ts-ignore
297
+ expect(apis['petstore'].lint.plugins.length).toEqual(1);
298
+ //@ts-ignore
299
+ expect(apis['petstore'].lint.plugins[0].id).toEqual('');
300
+
301
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
302
+ 'resolve-config/.redocly.yaml',
303
+ ]);
304
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([]);
305
+
306
+ expect(apis['petstore'].lint.rules).toEqual({
307
+ ...(await recommendedLintPreset).rules,
308
+ 'operation-2xx-response': 'warn',
309
+ 'operation-4xx-response': 'error',
310
+ });
311
+ });
312
+
313
+ it('should not add recommended to top level by default when apis have extends file', async () => {
314
+ const rawConfig: RawConfig = {
315
+ apis: {
316
+ petstore: {
317
+ root: 'some/path',
318
+ lint: {
319
+ extends: ['local-config.yaml'],
320
+ rules: {
321
+ 'operation-4xx-response': 'error',
322
+ },
323
+ },
324
+ },
325
+ },
326
+ lint: {
327
+ rules: {
328
+ 'operation-2xx-response': 'warn',
329
+ },
330
+ },
331
+ };
332
+
333
+ const { apis } = await resolveConfig(rawConfig, configPath);
334
+ expect(apis['petstore'].lint.rules).toBeDefined();
335
+ expect(Object.keys(apis['petstore'].lint.rules || {}).length).toEqual(7);
336
+ expect(apis['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn');
337
+ expect(apis['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
338
+ expect(apis['petstore'].lint.rules?.['operation-description']).toEqual('error'); // from extends file config
339
+ //@ts-ignore
340
+ expect(apis['petstore'].lint.plugins.length).toEqual(2);
341
+
342
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
343
+ 'resolve-config/.redocly.yaml',
344
+ 'resolve-config/local-config.yaml',
345
+ 'resolve-config/.redocly.yaml',
346
+ ]);
347
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
348
+ 'resolve-config/plugin.js',
349
+ ]);
350
+
351
+ expect(apis['petstore'].lint.recommendedFallback).toBe(false);
352
+ });
353
+
354
+ it('should ignore minimal from the root and read local file', async () => {
355
+ const rawConfig: RawConfig = {
356
+ apis: {
357
+ petstore: {
358
+ root: 'some/path',
359
+ lint: {
360
+ extends: ['recommended', 'local-config.yaml'],
361
+ rules: {
362
+ 'operation-4xx-response': 'error',
363
+ },
364
+ },
365
+ },
366
+ },
367
+ lint: {
368
+ extends: ['minimal'],
369
+ rules: {
370
+ 'operation-2xx-response': 'warn',
371
+ },
372
+ },
373
+ };
374
+
375
+ const { apis } = await resolveConfig(rawConfig, configPath);
376
+ expect(apis['petstore'].lint.rules).toBeDefined();
377
+ expect(apis['petstore'].lint.rules?.['operation-2xx-response']).toEqual('warn');
378
+ expect(apis['petstore'].lint.rules?.['operation-4xx-response']).toEqual('error');
379
+ expect(apis['petstore'].lint.rules?.['operation-description']).toEqual('error'); // from extends file config
380
+ //@ts-ignore
381
+ expect(apis['petstore'].lint.plugins.length).toEqual(2);
382
+ //@ts-ignore
383
+ delete apis['petstore'].lint.plugins;
384
+
385
+ expect(apis['petstore'].lint.extendPaths!.map(removeAbsolutePath)).toEqual([
386
+ 'resolve-config/.redocly.yaml',
387
+ 'resolve-config/local-config.yaml',
388
+ 'resolve-config/.redocly.yaml',
389
+ ]);
390
+ expect(apis['petstore'].lint.pluginPaths!.map(removeAbsolutePath)).toEqual([
391
+ 'resolve-config/plugin.js',
392
+ ]);
393
+
394
+ delete apis['petstore'].lint.extendPaths;
395
+ delete apis['petstore'].lint.pluginPaths;
396
+ expect(apis['petstore'].lint).toMatchSnapshot();
397
+ });
398
+ });