@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.
Files changed (159) hide show
  1. package/README.md +2 -2
  2. package/lib/benchmark/benches/resolve-with-no-external.bench.js +1 -1
  3. package/lib/bundle.d.ts +1 -1
  4. package/lib/bundle.js +4 -4
  5. package/lib/config/all.js +3 -1
  6. package/lib/config/config-resolvers.js +22 -4
  7. package/lib/config/config.d.ts +1 -0
  8. package/lib/config/config.js +1 -0
  9. package/lib/config/load.d.ts +8 -2
  10. package/lib/config/load.js +4 -2
  11. package/lib/config/minimal.js +3 -1
  12. package/lib/config/recommended.js +3 -1
  13. package/lib/config/rules.js +1 -1
  14. package/lib/config/types.d.ts +17 -0
  15. package/lib/config/utils.d.ts +2 -2
  16. package/lib/config/utils.js +44 -6
  17. package/lib/decorators/common/registry-dependencies.js +1 -1
  18. package/lib/format/format.d.ts +1 -1
  19. package/lib/format/format.js +22 -1
  20. package/lib/lint.js +2 -2
  21. package/lib/redocly/registry-api.d.ts +0 -1
  22. package/lib/redocly/registry-api.js +5 -4
  23. package/lib/resolve.js +3 -1
  24. package/lib/rules/ajv.d.ts +1 -1
  25. package/lib/rules/ajv.js +5 -5
  26. package/lib/rules/common/assertions/asserts.d.ts +3 -5
  27. package/lib/rules/common/assertions/asserts.js +137 -97
  28. package/lib/rules/common/assertions/index.js +2 -6
  29. package/lib/rules/common/assertions/utils.d.ts +12 -6
  30. package/lib/rules/common/assertions/utils.js +33 -20
  31. package/lib/rules/common/no-ambiguous-paths.js +1 -1
  32. package/lib/rules/common/no-identical-paths.js +1 -1
  33. package/lib/rules/common/operation-2xx-response.js +1 -1
  34. package/lib/rules/common/operation-4xx-response.js +1 -1
  35. package/lib/rules/common/operation-operationId.js +1 -1
  36. package/lib/rules/common/operation-tag-defined.js +1 -1
  37. package/lib/rules/common/path-not-include-query.js +1 -1
  38. package/lib/rules/common/security-defined.d.ts +2 -0
  39. package/lib/rules/common/{operation-security-defined.js → security-defined.js} +18 -4
  40. package/lib/rules/common/spec.js +12 -1
  41. package/lib/rules/common/tags-alphabetical.js +1 -1
  42. package/lib/rules/oas2/index.d.ts +1 -1
  43. package/lib/rules/oas2/index.js +2 -2
  44. package/lib/rules/oas2/remove-unused-components.js +1 -1
  45. package/lib/rules/oas2/request-mime-type.js +1 -1
  46. package/lib/rules/oas2/response-mime-type.js +1 -1
  47. package/lib/rules/oas3/index.js +6 -2
  48. package/lib/rules/oas3/no-empty-servers.js +1 -1
  49. package/lib/rules/oas3/no-server-variables-empty-enum.js +1 -1
  50. package/lib/rules/oas3/no-unused-components.js +1 -1
  51. package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
  52. package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
  53. package/lib/rules/oas3/remove-unused-components.js +1 -1
  54. package/lib/rules/oas3/request-mime-type.js +1 -1
  55. package/lib/rules/oas3/response-mime-type.js +1 -1
  56. package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
  57. package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
  58. package/lib/rules/other/stats.d.ts +2 -2
  59. package/lib/rules/other/stats.js +2 -2
  60. package/lib/rules/utils.js +1 -1
  61. package/lib/types/oas2.js +5 -5
  62. package/lib/types/oas3.js +27 -20
  63. package/lib/types/oas3_1.js +3 -3
  64. package/lib/types/redocly-yaml.js +60 -54
  65. package/lib/utils.d.ts +3 -3
  66. package/lib/utils.js +5 -5
  67. package/lib/visitors.d.ts +11 -11
  68. package/lib/visitors.js +13 -1
  69. package/package.json +3 -5
  70. package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
  71. package/src/__tests__/fixtures/extension.js +3 -3
  72. package/src/__tests__/format.test.ts +76 -0
  73. package/src/__tests__/lint.test.ts +184 -121
  74. package/src/__tests__/resolve-http.test.ts +1 -1
  75. package/src/__tests__/resolve.test.ts +9 -9
  76. package/src/__tests__/walk.test.ts +78 -10
  77. package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
  78. package/src/bundle.ts +4 -4
  79. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +6 -2
  80. package/src/config/__tests__/config-resolvers.test.ts +37 -1
  81. package/src/config/__tests__/config.test.ts +5 -0
  82. package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
  83. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
  84. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
  85. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +16 -0
  86. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -19
  87. package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +16 -0
  88. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
  89. package/src/config/__tests__/fixtures/resolve-config/plugin.js +11 -0
  90. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
  91. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
  92. package/src/config/__tests__/load.test.ts +13 -16
  93. package/src/config/__tests__/resolve-plugins.test.ts +3 -3
  94. package/src/config/__tests__/utils.test.ts +64 -4
  95. package/src/config/all.ts +3 -1
  96. package/src/config/config-resolvers.ts +30 -7
  97. package/src/config/config.ts +2 -0
  98. package/src/config/load.ts +13 -6
  99. package/src/config/minimal.ts +3 -1
  100. package/src/config/recommended.ts +3 -1
  101. package/src/config/rules.ts +2 -2
  102. package/src/config/types.ts +24 -0
  103. package/src/config/utils.ts +103 -13
  104. package/src/decorators/common/registry-dependencies.ts +1 -1
  105. package/src/format/format.ts +32 -2
  106. package/src/lint.ts +2 -2
  107. package/src/redocly/registry-api.ts +5 -4
  108. package/src/resolve.ts +3 -1
  109. package/src/rules/__tests__/utils.test.ts +1 -1
  110. package/src/rules/ajv.ts +4 -4
  111. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
  112. package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
  113. package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
  114. package/src/rules/common/__tests__/security-defined.test.ts +175 -0
  115. package/src/rules/common/__tests__/spec.test.ts +79 -0
  116. package/src/rules/common/assertions/__tests__/asserts.test.ts +491 -428
  117. package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
  118. package/src/rules/common/assertions/asserts.ts +155 -97
  119. package/src/rules/common/assertions/index.ts +2 -11
  120. package/src/rules/common/assertions/utils.ts +66 -36
  121. package/src/rules/common/no-ambiguous-paths.ts +1 -1
  122. package/src/rules/common/no-identical-paths.ts +1 -1
  123. package/src/rules/common/operation-2xx-response.ts +1 -1
  124. package/src/rules/common/operation-4xx-response.ts +1 -1
  125. package/src/rules/common/operation-operationId.ts +1 -1
  126. package/src/rules/common/operation-tag-defined.ts +1 -1
  127. package/src/rules/common/path-not-include-query.ts +1 -1
  128. package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +19 -4
  129. package/src/rules/common/spec.ts +15 -1
  130. package/src/rules/common/tags-alphabetical.ts +1 -1
  131. package/src/rules/oas2/index.ts +2 -2
  132. package/src/rules/oas2/remove-unused-components.ts +1 -1
  133. package/src/rules/oas2/request-mime-type.ts +1 -1
  134. package/src/rules/oas2/response-mime-type.ts +1 -1
  135. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +51 -2
  136. package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
  137. package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
  138. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
  139. package/src/rules/oas3/index.ts +6 -2
  140. package/src/rules/oas3/no-empty-servers.ts +1 -1
  141. package/src/rules/oas3/no-server-variables-empty-enum.ts +1 -1
  142. package/src/rules/oas3/no-unused-components.ts +1 -1
  143. package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
  144. package/src/rules/oas3/remove-unused-components.ts +1 -1
  145. package/src/rules/oas3/request-mime-type.ts +1 -1
  146. package/src/rules/oas3/response-mime-type.ts +1 -1
  147. package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
  148. package/src/rules/other/stats.ts +2 -2
  149. package/src/rules/utils.ts +2 -1
  150. package/src/types/index.ts +2 -2
  151. package/src/types/oas2.ts +5 -5
  152. package/src/types/oas3.ts +27 -20
  153. package/src/types/oas3_1.ts +3 -3
  154. package/src/types/redocly-yaml.ts +66 -38
  155. package/src/utils.ts +11 -7
  156. package/src/visitors.ts +29 -13
  157. package/tsconfig.tsbuildinfo +1 -1
  158. package/lib/rules/common/operation-security-defined.d.ts +0 -2
  159. 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.DefinitionRoot,
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.DefinitionRoot as NormalizedNodeType,
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.DefinitionRoot,
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
- DefinitionRoot: {
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/master/packages/core/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml',
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
- lint:
2
- plugins:
3
- - './plugin.js'
1
+ plugins:
2
+ - './plugin.js'
@@ -1,12 +1,11 @@
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
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
- 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
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
- 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 be at least 13 characters and end with a full stop.
9
- severity: error
10
- minLength: 13
11
- pattern: /\.$/
12
- plugins:
13
- - plugin.js
14
- - api/plugin.js
15
- extends:
16
- - recommended
17
- - api/nested-config.yaml
18
- - test-plugin-nested/all
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
@@ -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
- lint:
2
- plugins:
3
- - plugin.js
4
- extends:
5
- - test-plugin/all
6
- rules:
7
- no-invalid-media-type-examples: error
8
- operation-description: error
9
- path-http-verbs-order: error
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
- lint:
2
- rules:
3
- operation-2xx-response: off
4
- operation-4xx-response: error
1
+ rules:
2
+ operation-2xx-response: off
3
+ operation-4xx-response: error
@@ -1,5 +1,4 @@
1
- lint:
2
- extends:
3
- - nested-remote-config.yaml
4
- rules:
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, RawConfig } from './../types';
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(undefined, undefined, mockFn);
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
- styleguide:
97
- extends:
98
- - recommended
99
- rules:
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: RawConfig = {
111
- styleguide: {
112
- extends: ['minimal'],
113
- rules: {
114
- 'info-license': 'off',
115
- 'tag-description': 'off',
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.styleguide!.rules as Record<string, RuleConfig>,
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 new syntax', () => {
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 new and old syntax used together', () => {
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
- 'operation-security-defined': 'error',
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, notUndefined, parseYaml } from '../utils';
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(notUndefined);
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: Record<string, RuleConfig> | undefined
397
- ): Record<string, RuleConfig> | undefined {
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/', ''),