@redocly/openapi-core 1.0.0-beta.106 → 1.0.0-beta.109

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 (193) hide show
  1. package/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +0 -1
  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 +9 -6
  5. package/lib/config/all.js +5 -3
  6. package/lib/config/config-resolvers.js +32 -14
  7. package/lib/config/config.d.ts +3 -5
  8. package/lib/config/config.js +7 -4
  9. package/lib/config/load.d.ts +7 -0
  10. package/lib/config/load.js +14 -6
  11. package/lib/config/minimal.js +7 -4
  12. package/lib/config/recommended.js +7 -4
  13. package/lib/config/rules.d.ts +1 -1
  14. package/lib/config/rules.js +1 -1
  15. package/lib/config/types.d.ts +7 -0
  16. package/lib/config/utils.d.ts +2 -2
  17. package/lib/config/utils.js +49 -11
  18. package/lib/decorators/common/registry-dependencies.js +2 -2
  19. package/lib/env.d.ts +3 -0
  20. package/lib/env.js +8 -0
  21. package/lib/format/codeframes.js +16 -10
  22. package/lib/format/format.d.ts +1 -1
  23. package/lib/format/format.js +49 -26
  24. package/lib/index.d.ts +5 -5
  25. package/lib/index.js +3 -1
  26. package/lib/js-yaml/index.js +1 -0
  27. package/lib/lint.js +2 -2
  28. package/lib/logger.d.ts +10 -0
  29. package/lib/logger.js +31 -0
  30. package/lib/output.d.ts +3 -0
  31. package/lib/output.js +9 -0
  32. package/lib/redocly/index.js +10 -9
  33. package/lib/redocly/registry-api-types.d.ts +28 -30
  34. package/lib/redocly/registry-api.d.ts +3 -3
  35. package/lib/redocly/registry-api.js +7 -1
  36. package/lib/ref-utils.js +2 -1
  37. package/lib/resolve.d.ts +1 -1
  38. package/lib/resolve.js +4 -2
  39. package/lib/rules/ajv.d.ts +1 -1
  40. package/lib/rules/ajv.js +7 -7
  41. package/lib/rules/common/assertions/asserts.js +4 -4
  42. package/lib/rules/common/assertions/index.js +1 -1
  43. package/lib/rules/common/no-ambiguous-paths.js +1 -1
  44. package/lib/rules/common/no-identical-paths.js +1 -1
  45. package/lib/rules/common/no-invalid-parameter-examples.js +3 -3
  46. package/lib/rules/common/no-invalid-schema-examples.js +3 -3
  47. package/lib/rules/common/operation-2xx-response.js +1 -1
  48. package/lib/rules/common/operation-4xx-response.js +1 -1
  49. package/lib/rules/common/operation-operationId.js +1 -1
  50. package/lib/rules/common/operation-tag-defined.js +1 -1
  51. package/lib/rules/common/path-not-include-query.js +1 -1
  52. package/lib/rules/common/security-defined.d.ts +2 -0
  53. package/lib/rules/common/{operation-security-defined.js → security-defined.js} +19 -5
  54. package/lib/rules/common/spec.js +14 -3
  55. package/lib/rules/common/tags-alphabetical.js +1 -1
  56. package/lib/rules/oas2/index.d.ts +1 -1
  57. package/lib/rules/oas2/index.js +2 -2
  58. package/lib/rules/oas2/remove-unused-components.js +3 -3
  59. package/lib/rules/oas2/request-mime-type.js +1 -1
  60. package/lib/rules/oas2/response-mime-type.js +1 -1
  61. package/lib/rules/oas3/index.js +8 -4
  62. package/lib/rules/oas3/no-empty-servers.js +1 -1
  63. package/lib/rules/oas3/no-invalid-media-type-examples.js +2 -2
  64. package/lib/rules/oas3/no-server-variables-empty-enum.d.ts +2 -0
  65. package/lib/rules/oas3/{no-servers-empty-enum.js → no-server-variables-empty-enum.js} +5 -5
  66. package/lib/rules/oas3/no-unused-components.js +2 -2
  67. package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
  68. package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
  69. package/lib/rules/oas3/remove-unused-components.js +4 -4
  70. package/lib/rules/oas3/request-mime-type.js +1 -1
  71. package/lib/rules/oas3/response-mime-type.js +1 -1
  72. package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
  73. package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
  74. package/lib/rules/other/stats.d.ts +2 -2
  75. package/lib/rules/other/stats.js +2 -2
  76. package/lib/rules/utils.d.ts +3 -2
  77. package/lib/rules/utils.js +16 -4
  78. package/lib/types/oas2.js +5 -5
  79. package/lib/types/oas3.js +27 -20
  80. package/lib/types/oas3_1.js +3 -3
  81. package/lib/types/redocly-yaml.js +47 -56
  82. package/lib/utils.d.ts +6 -1
  83. package/lib/utils.js +24 -7
  84. package/lib/visitors.d.ts +12 -12
  85. package/lib/visitors.js +15 -3
  86. package/lib/walk.d.ts +2 -1
  87. package/lib/walk.js +6 -3
  88. package/package.json +2 -2
  89. package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
  90. package/src/__tests__/fixtures/extension.js +3 -3
  91. package/src/__tests__/format.test.ts +76 -0
  92. package/src/__tests__/lint.test.ts +106 -131
  93. package/src/__tests__/logger-browser.test.ts +53 -0
  94. package/src/__tests__/logger.test.ts +47 -0
  95. package/src/__tests__/output-browser.test.ts +18 -0
  96. package/src/__tests__/output.test.ts +15 -0
  97. package/src/__tests__/resolve-http.test.ts +1 -1
  98. package/src/__tests__/resolve.test.ts +9 -9
  99. package/src/__tests__/utils-browser.test.ts +11 -0
  100. package/src/__tests__/utils.test.ts +7 -0
  101. package/src/__tests__/walk.test.ts +78 -10
  102. package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +0 -1
  103. package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
  104. package/src/bundle.ts +10 -7
  105. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +12 -6
  106. package/src/config/__tests__/config.test.ts +35 -0
  107. package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
  108. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
  109. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
  110. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -19
  111. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
  112. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
  113. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
  114. package/src/config/__tests__/load.test.ts +76 -1
  115. package/src/config/__tests__/utils.test.ts +64 -4
  116. package/src/config/all.ts +5 -3
  117. package/src/config/config-resolvers.ts +45 -19
  118. package/src/config/config.ts +10 -8
  119. package/src/config/load.ts +31 -7
  120. package/src/config/minimal.ts +7 -4
  121. package/src/config/recommended.ts +7 -4
  122. package/src/config/rules.ts +2 -2
  123. package/src/config/types.ts +11 -0
  124. package/src/config/utils.ts +115 -25
  125. package/src/decorators/common/registry-dependencies.ts +2 -2
  126. package/src/env.ts +5 -0
  127. package/src/format/codeframes.ts +15 -9
  128. package/src/format/format.ts +59 -34
  129. package/src/index.ts +6 -4
  130. package/src/js-yaml/index.ts +1 -0
  131. package/src/lint.ts +2 -2
  132. package/src/logger.ts +34 -0
  133. package/src/output.ts +7 -0
  134. package/src/redocly/index.ts +7 -4
  135. package/src/redocly/registry-api-types.ts +27 -29
  136. package/src/redocly/registry-api.ts +18 -7
  137. package/src/ref-utils.ts +2 -1
  138. package/src/resolve.ts +7 -5
  139. package/src/rules/__tests__/utils.test.ts +39 -1
  140. package/src/rules/ajv.ts +7 -7
  141. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
  142. package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
  143. package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
  144. package/src/rules/common/__tests__/security-defined.test.ts +175 -0
  145. package/src/rules/common/__tests__/spec.test.ts +79 -0
  146. package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
  147. package/src/rules/common/assertions/asserts.ts +4 -4
  148. package/src/rules/common/assertions/index.ts +1 -1
  149. package/src/rules/common/no-ambiguous-paths.ts +1 -1
  150. package/src/rules/common/no-identical-paths.ts +1 -1
  151. package/src/rules/common/no-invalid-parameter-examples.ts +4 -4
  152. package/src/rules/common/no-invalid-schema-examples.ts +4 -4
  153. package/src/rules/common/operation-2xx-response.ts +1 -1
  154. package/src/rules/common/operation-4xx-response.ts +1 -1
  155. package/src/rules/common/operation-operationId.ts +1 -1
  156. package/src/rules/common/operation-tag-defined.ts +1 -1
  157. package/src/rules/common/path-not-include-query.ts +1 -1
  158. package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +20 -5
  159. package/src/rules/common/spec.ts +17 -3
  160. package/src/rules/common/tags-alphabetical.ts +1 -1
  161. package/src/rules/oas2/index.ts +2 -2
  162. package/src/rules/oas2/remove-unused-components.ts +3 -3
  163. package/src/rules/oas2/request-mime-type.ts +1 -1
  164. package/src/rules/oas2/response-mime-type.ts +1 -1
  165. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +16 -16
  166. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +5 -5
  167. package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
  168. package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
  169. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
  170. package/src/rules/oas3/index.ts +8 -4
  171. package/src/rules/oas3/no-empty-servers.ts +1 -1
  172. package/src/rules/oas3/no-invalid-media-type-examples.ts +3 -3
  173. package/src/rules/oas3/{no-servers-empty-enum.ts → no-server-variables-empty-enum.ts} +3 -3
  174. package/src/rules/oas3/no-unused-components.ts +2 -2
  175. package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
  176. package/src/rules/oas3/remove-unused-components.ts +5 -5
  177. package/src/rules/oas3/request-mime-type.ts +1 -1
  178. package/src/rules/oas3/response-mime-type.ts +1 -1
  179. package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
  180. package/src/rules/other/stats.ts +2 -2
  181. package/src/rules/utils.ts +17 -3
  182. package/src/types/index.ts +2 -2
  183. package/src/types/oas2.ts +5 -5
  184. package/src/types/oas3.ts +27 -20
  185. package/src/types/oas3_1.ts +3 -3
  186. package/src/types/redocly-yaml.ts +53 -41
  187. package/src/utils.ts +31 -4
  188. package/src/visitors.ts +34 -18
  189. package/src/walk.ts +15 -11
  190. package/tsconfig.tsbuildinfo +1 -1
  191. package/lib/rules/common/operation-security-defined.d.ts +0 -2
  192. package/lib/rules/oas3/no-servers-empty-enum.d.ts +0 -2
  193. package/src/rules/common/__tests__/operation-security-defined.test.ts +0 -69
@@ -16,7 +16,7 @@ import { Oas3RuleSet } from '../oas-types';
16
16
  describe('walk order', () => {
17
17
  it('should run visitors', async () => {
18
18
  const visitors = {
19
- DefinitionRoot: {
19
+ Root: {
20
20
  enter: jest.fn(),
21
21
  leave: jest.fn(),
22
22
  },
@@ -63,6 +63,74 @@ describe('walk order', () => {
63
63
  }
64
64
  });
65
65
 
66
+ it('should run legacy visitors', async () => {
67
+ const visitors = {
68
+ DefinitionRoot: {
69
+ enter: jest.fn(),
70
+ leave: jest.fn(),
71
+ },
72
+ PathMap: {
73
+ enter: jest.fn(),
74
+ leave: jest.fn(),
75
+ },
76
+ ServerVariableMap: {
77
+ enter: jest.fn(),
78
+ leave: jest.fn(),
79
+ },
80
+ MediaTypeMap: {
81
+ enter: jest.fn(),
82
+ leave: jest.fn(),
83
+ },
84
+ ExampleMap: {
85
+ enter: jest.fn(),
86
+ leave: jest.fn(),
87
+ },
88
+ HeaderMap: {
89
+ enter: jest.fn(),
90
+ leave: jest.fn(),
91
+ },
92
+ };
93
+
94
+ const testRuleSet: Oas3RuleSet = {
95
+ test: jest.fn(() => {
96
+ return visitors;
97
+ }),
98
+ };
99
+
100
+ const document = parseYamlToDocument(
101
+ outdent`
102
+ openapi: 3.0.0
103
+ servers:
104
+ - url: http://{test}.url
105
+ variables:
106
+ test: test
107
+ paths:
108
+ /test-path:
109
+ get:
110
+ responses:
111
+ 200:
112
+ headers: {}
113
+ content:
114
+ application/json:
115
+ schema: {}
116
+ examples: {}
117
+ `,
118
+ ''
119
+ );
120
+
121
+ await lintDocument({
122
+ externalRefResolver: new BaseResolver(),
123
+ document,
124
+ config: makeConfigForRuleset(testRuleSet),
125
+ });
126
+
127
+ expect(testRuleSet.test).toBeCalledTimes(1);
128
+ for (const fns of Object.values(visitors)) {
129
+ expect(fns.enter).toBeCalled();
130
+ expect(fns.leave).toBeCalled();
131
+ }
132
+ });
133
+
66
134
  it('should run nested visitors correctly', async () => {
67
135
  const calls: string[] = [];
68
136
 
@@ -1046,8 +1114,8 @@ describe('walk order', () => {
1046
1114
 
1047
1115
  expect(calls).toMatchInlineSnapshot(`
1048
1116
  Array [
1049
- "enter DefinitionRoot",
1050
- "enter PathMap",
1117
+ "enter Root",
1118
+ "enter PathsMap",
1051
1119
  "enter PathItem",
1052
1120
  "enter ParameterList",
1053
1121
  "enter Parameter",
@@ -1066,7 +1134,7 @@ describe('walk order', () => {
1066
1134
  "leave ParameterList",
1067
1135
  "leave Operation",
1068
1136
  "leave PathItem",
1069
- "leave PathMap",
1137
+ "leave PathsMap",
1070
1138
  "enter Components",
1071
1139
  "enter NamedParameters",
1072
1140
  "leave NamedParameters",
@@ -1075,7 +1143,7 @@ describe('walk order', () => {
1075
1143
  "leave Schema",
1076
1144
  "leave NamedSchemas",
1077
1145
  "leave Components",
1078
- "leave DefinitionRoot",
1146
+ "leave Root",
1079
1147
  ]
1080
1148
  `);
1081
1149
  });
@@ -1378,10 +1446,10 @@ describe('type extensions', () => {
1378
1446
  parameters: listOf('Parameter'),
1379
1447
  },
1380
1448
  },
1381
- DefinitionRoot: {
1382
- ...types.DefinitionRoot,
1449
+ Root: {
1450
+ ...types.Root,
1383
1451
  properties: {
1384
- ...types.DefinitionRoot.properties,
1452
+ ...types.Root.properties,
1385
1453
  'x-webhooks': 'XWebHooks',
1386
1454
  },
1387
1455
  },
@@ -1393,7 +1461,7 @@ describe('type extensions', () => {
1393
1461
 
1394
1462
  expect(calls).toMatchInlineSnapshot(`
1395
1463
  Array [
1396
- "enter DefinitionRoot",
1464
+ "enter Root",
1397
1465
  "enter XWebHooks",
1398
1466
  "enter hook test",
1399
1467
  "enter ParameterList",
@@ -1402,7 +1470,7 @@ describe('type extensions', () => {
1402
1470
  "leave ParameterList",
1403
1471
  "leave hook test",
1404
1472
  "leave XWebHooks",
1405
- "leave DefinitionRoot",
1473
+ "leave Root",
1406
1474
  ]
1407
1475
  `);
1408
1476
  });
@@ -15,7 +15,6 @@ const rebillyDocument = parseYamlToDocument(
15
15
  const config = makeConfigForRuleset({
16
16
  test: () => {
17
17
  return {
18
- // @ts-ignore
19
18
  Schema(schema, ctx) {
20
19
  if (schema.type === 'number') {
21
20
  ctx.report({
@@ -18,6 +18,6 @@ export function measureAsync() {
18
18
  return resolveDocument({
19
19
  rootDocument: rebillyDocument,
20
20
  externalRefResolver,
21
- rootType: normalizeTypes(Oas3Types).DefinitionRoot,
21
+ rootType: normalizeTypes(Oas3Types).Root,
22
22
  });
23
23
  }
package/src/bundle.ts CHANGED
@@ -10,7 +10,7 @@ import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
10
10
  import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
11
11
  import { initRules } from './config/rules';
12
12
  import { reportUnresolvedRef } from './rules/no-unresolved-refs';
13
- import { isPlainObject } from './utils';
13
+ import { isPlainObject, isTruthy } from './utils';
14
14
  import { OasRef } from './typings/openapi';
15
15
  import { isRedoclyRegistryURL } from './redocly';
16
16
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components';
@@ -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 || {};
@@ -302,9 +302,12 @@ function makeBundleVisitor(
302
302
  if (!isPlainObject(resolved.node)) {
303
303
  ctx.parent[ctx.key] = resolved.node;
304
304
  } else {
305
+ // TODO: why $ref isn't optional in OasRef?
306
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
305
307
  // @ts-ignore
306
308
  delete ref.$ref;
307
- Object.assign(ref, resolved.node);
309
+ const obj = Object.assign({}, resolved.node, ref);
310
+ Object.assign(ref, obj); // assign ref itself again so ref fields take precedence
308
311
  }
309
312
  }
310
313
 
@@ -348,7 +351,7 @@ function makeBundleVisitor(
348
351
 
349
352
  let name = '';
350
353
 
351
- const refParts = pointer.slice(2).split('/').filter(Boolean); // slice(2) removes "#/"
354
+ const refParts = pointer.slice(2).split('/').filter(isTruthy); // slice(2) removes "#/"
352
355
  while (refParts.length > 0) {
353
356
  name = refParts.pop() + (name ? `-${name}` : '');
354
357
  if (
@@ -15,20 +15,23 @@ Object {
15
15
  "no-invalid-media-type-examples": "error",
16
16
  "no-server-example.com": "warn",
17
17
  "no-server-trailing-slash": "error",
18
- "no-servers-empty-enum": "error",
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 {},
24
25
  "oas3_1Rules": Object {
25
26
  "no-empty-servers": "error",
26
27
  "no-example-value-and-externalValue": "error",
28
+ "no-invalid-media-type-examples": "error",
27
29
  "no-server-example.com": "warn",
28
30
  "no-server-trailing-slash": "error",
29
- "no-servers-empty-enum": "error",
31
+ "no-server-variables-empty-enum": "error",
30
32
  "no-undefined-server-variable": "error",
31
33
  "no-unused-components": "warn",
34
+ "spec-components-invalid-map-name": "error",
32
35
  },
33
36
  "preprocessors": Object {},
34
37
  "recommendedFallback": false,
@@ -53,7 +56,6 @@ Object {
53
56
  "operation-operationId-unique": "error",
54
57
  "operation-operationId-url-safe": "error",
55
58
  "operation-parameters-unique": "error",
56
- "operation-security-defined": "error",
57
59
  "operation-singular-tag": "off",
58
60
  "operation-summary": "error",
59
61
  "operation-tag-defined": "off",
@@ -63,6 +65,7 @@ Object {
63
65
  "path-not-include-query": "error",
64
66
  "path-parameters-defined": "error",
65
67
  "paths-kebab-case": "off",
68
+ "security-defined": "error",
66
69
  "spec": "error",
67
70
  "tag-description": "warn",
68
71
  "tags-alphabetical": "off",
@@ -85,20 +88,23 @@ Object {
85
88
  "no-invalid-media-type-examples": "warn",
86
89
  "no-server-example.com": "warn",
87
90
  "no-server-trailing-slash": "error",
88
- "no-servers-empty-enum": "error",
91
+ "no-server-variables-empty-enum": "error",
89
92
  "no-undefined-server-variable": "error",
90
93
  "no-unused-components": "warn",
94
+ "spec-components-invalid-map-name": "error",
91
95
  },
92
96
  "oas3_1Decorators": Object {},
93
97
  "oas3_1Preprocessors": Object {},
94
98
  "oas3_1Rules": Object {
95
99
  "no-empty-servers": "error",
96
100
  "no-example-value-and-externalValue": "error",
101
+ "no-invalid-media-type-examples": "warn",
97
102
  "no-server-example.com": "warn",
98
103
  "no-server-trailing-slash": "error",
99
- "no-servers-empty-enum": "error",
104
+ "no-server-variables-empty-enum": "error",
100
105
  "no-undefined-server-variable": "error",
101
106
  "no-unused-components": "warn",
107
+ "spec-components-invalid-map-name": "error",
102
108
  },
103
109
  "preprocessors": Object {},
104
110
  "recommendedFallback": undefined,
@@ -140,7 +146,6 @@ Object {
140
146
  "operation-operationId-unique": "error",
141
147
  "operation-operationId-url-safe": "error",
142
148
  "operation-parameters-unique": "error",
143
- "operation-security-defined": "error",
144
149
  "operation-singular-tag": "off",
145
150
  "operation-summary": "error",
146
151
  "operation-tag-defined": "off",
@@ -149,6 +154,7 @@ Object {
149
154
  "path-not-include-query": "error",
150
155
  "path-parameters-defined": "error",
151
156
  "paths-kebab-case": "off",
157
+ "security-defined": "error",
152
158
  "spec": "error",
153
159
  "tag-description": "warn",
154
160
  "tags-alphabetical": "off",
@@ -1,3 +1,4 @@
1
+ import { OasVersion } from '../../oas-types';
1
2
  import { Config, StyleguideConfig } from '../config';
2
3
  import { getMergedConfig } from '../utils';
3
4
 
@@ -242,3 +243,37 @@ describe('getMergedConfig', () => {
242
243
  `);
243
244
  });
244
245
  });
246
+
247
+ describe('StyleguideConfig.extendTypes', () => {
248
+ let oas3 = jest.fn();
249
+ let oas2 = jest.fn();
250
+ let testRawConfigStyleguide = {
251
+ plugins: [
252
+ {
253
+ id: 'test-types-plugin',
254
+ typeExtension: {
255
+ oas3,
256
+ oas2,
257
+ },
258
+ },
259
+ ],
260
+ };
261
+ it('should call only oas3 types extension', () => {
262
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
263
+ styleguideConfig.extendTypes({}, OasVersion.Version3_0);
264
+ expect(oas3).toHaveBeenCalledTimes(1);
265
+ expect(oas2).toHaveBeenCalledTimes(0);
266
+ });
267
+ it('should call only oas2 types extension', () => {
268
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
269
+ styleguideConfig.extendTypes({}, OasVersion.Version2);
270
+ expect(oas3).toHaveBeenCalledTimes(0);
271
+ expect(oas2).toHaveBeenCalledTimes(1);
272
+ });
273
+ it('should throw error if for oas version different from 2 and 3', () => {
274
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
275
+ expect(() => styleguideConfig.extendTypes({}, 'something else' as OasVersion)).toThrowError(
276
+ 'Not implemented'
277
+ );
278
+ });
279
+ });
@@ -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
@@ -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
@@ -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
@@ -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,5 +1,7 @@
1
- import { loadConfig, findConfig, getConfig } from '../load';
1
+ import { loadConfig, findConfig, getConfig, createConfig } from '../load';
2
2
  import { RedoclyClient } from '../../redocly';
3
+ import { RuleConfig, FlatRawConfig } from './../types';
4
+ import { Config } from '../config';
3
5
 
4
6
  const fs = require('fs');
5
7
  const path = require('path');
@@ -87,3 +89,76 @@ describe('getConfig', () => {
87
89
  expect(getConfig()).toEqual(Promise.resolve({}));
88
90
  });
89
91
  });
92
+
93
+ describe('createConfig', () => {
94
+ it('should create config from string', async () => {
95
+ const config = await createConfig(`
96
+ extends:
97
+ - recommended
98
+ rules:
99
+ info-license: off
100
+ `);
101
+
102
+ verifyExtendedConfig(config, {
103
+ extendsRuleSet: 'recommended',
104
+ overridesRules: { 'info-license': 'off' },
105
+ });
106
+ });
107
+
108
+ it('should create config from object', async () => {
109
+ const rawConfig: FlatRawConfig = {
110
+ extends: ['minimal'],
111
+ rules: {
112
+ 'info-license': 'off',
113
+ 'tag-description': 'off',
114
+ 'operation-2xx-response': 'off',
115
+ },
116
+ };
117
+ const config = await createConfig(rawConfig);
118
+
119
+ verifyExtendedConfig(config, {
120
+ extendsRuleSet: 'minimal',
121
+ overridesRules: rawConfig.rules as Record<string, RuleConfig>,
122
+ });
123
+ });
124
+ });
125
+
126
+ function verifyExtendedConfig(
127
+ config: Config,
128
+ {
129
+ extendsRuleSet,
130
+ overridesRules,
131
+ }: { extendsRuleSet: string; overridesRules: Record<string, RuleConfig> }
132
+ ) {
133
+ const defaultPlugin = config.styleguide.plugins.find((plugin) => plugin.id === '');
134
+ expect(defaultPlugin).toBeDefined();
135
+
136
+ const recommendedRules = defaultPlugin?.configs?.[extendsRuleSet];
137
+ expect(recommendedRules).toBeDefined();
138
+
139
+ verifyOasRules(config.styleguide.rules.oas2, overridesRules, recommendedRules?.rules || {});
140
+ verifyOasRules(
141
+ config.styleguide.rules.oas3_0,
142
+ overridesRules,
143
+ Object.assign({}, recommendedRules?.rules, recommendedRules?.oas3_0Rules)
144
+ );
145
+ verifyOasRules(
146
+ config.styleguide.rules.oas3_1,
147
+ overridesRules,
148
+ Object.assign({}, recommendedRules?.rules, recommendedRules?.oas3_1Rules)
149
+ );
150
+ }
151
+
152
+ function verifyOasRules(
153
+ finalRuleset: Record<string, RuleConfig>,
154
+ overridesRules: Record<string, RuleConfig>,
155
+ defaultRuleset: Record<string, RuleConfig>
156
+ ) {
157
+ Object.entries(finalRuleset).forEach(([ruleName, ruleValue]) => {
158
+ if (ruleName in overridesRules) {
159
+ expect(ruleValue).toBe(overridesRules[ruleName]);
160
+ } else {
161
+ expect(ruleValue).toBe(defaultRuleset[ruleName]);
162
+ }
163
+ });
164
+ }
@@ -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
  });