oas-toolkit 0.6.0 → 0.6.2

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.
@@ -19,6 +19,7 @@ module.exports = async function ({ argv }) {
19
19
  merger.ensureNoPathColissions(oas, argv);
20
20
  merger.ensureNoTagColissions(oas, argv);
21
21
  merger.ensureNoSecurityColissions(oas, argv);
22
+ merger.ensureNoComplexObjectCollisions(oas, argv);
22
23
  } catch (e) {
23
24
  console.error(`ERROR: ${e.message}`);
24
25
  process.exit(1);
package/merger.js CHANGED
@@ -36,19 +36,6 @@ function merge(objects, options) {
36
36
  }
37
37
  });
38
38
 
39
- // Validate that $ref is the only thing at a specific level
40
- combinedSpec = traverse(combinedSpec).map(function () {
41
- if (["oneOf", "allOf", "anyOf", "$ref"].includes(this.key)) {
42
- if (Object.keys(this.parent.node).length > 1) {
43
- throw new Error(
44
- `Cannot have ${
45
- this.key
46
- } and other properties at the same level: ${this.path.join(".")}`
47
- );
48
- }
49
- }
50
- });
51
-
52
39
  return combinedSpec;
53
40
  }
54
41
 
@@ -208,9 +195,62 @@ function ensureNoSecurityColissions(objects) {
208
195
  }
209
196
  }
210
197
 
198
+ function ensureNoComplexObjectCollisions(objects) {
199
+ const allPaths = {};
200
+ for (let object of objects) {
201
+ traverse(object).forEach(function () {
202
+ if (
203
+ ["oneOf", "allOf", "anyOf", "$ref", "properties"].includes(this.key)
204
+ ) {
205
+ const k = this.path.slice(0, -1).join(".");
206
+ allPaths[k] = allPaths[k] || [];
207
+ allPaths[k].push({
208
+ key: this.key,
209
+ file: object.info.title,
210
+ value: this.node,
211
+ });
212
+ }
213
+ });
214
+ }
215
+
216
+ for (let path in allPaths) {
217
+ if (allPaths[path].length > 1) {
218
+ const v = allPaths[path];
219
+
220
+ // If all entries for this path use the same special key
221
+ if (
222
+ uniqWith(
223
+ v.map((f) => f.key),
224
+ isEqual
225
+ ).length == 1
226
+ ) {
227
+ // Check if all the values are the same
228
+ const values = v.map((f) => {
229
+ // If it's a oneOf, order doesn't matter so sort for comparison
230
+ if (f.key == "oneOf") {
231
+ f.value.sort();
232
+ }
233
+ return f.value;
234
+ });
235
+
236
+ if (isEqual(...values)) {
237
+ continue;
238
+ }
239
+ }
240
+
241
+ throw new Error(
242
+ `Conflicting complex object detected: ${path} (${v
243
+ .map((f) => `${f.file}[${f.key}]`)
244
+ .join(", ")})`
245
+ );
246
+ }
247
+ }
248
+ }
249
+
211
250
  module.exports = Object.assign(merge, {
212
251
  ensureNoComponentColissions,
213
252
  ensureNoPathColissions,
214
253
  ensureNoTagColissions,
215
254
  ensureNoSecurityColissions,
255
+ ensureNoComplexObjectCollisions,
216
256
  });
package/merger.test.js CHANGED
@@ -3,6 +3,7 @@ const {
3
3
  ensureNoPathColissions,
4
4
  ensureNoTagColissions,
5
5
  ensureNoSecurityColissions,
6
+ ensureNoComplexObjectCollisions,
6
7
  } = require("./merger");
7
8
  const merger = require("./merger");
8
9
 
@@ -152,6 +153,161 @@ describe("#ensureNoComponentColissions", () => {
152
153
  });
153
154
  });
154
155
 
156
+ describe("#ensureNoComplexObjectCollisions", () => {
157
+ it("throws when $ref is detected alongside other values", () => {
158
+ expect(() => {
159
+ ensureNoComplexObjectCollisions([
160
+ {
161
+ info: { title: "One" },
162
+ components: {
163
+ schemas: {
164
+ DemoError: {
165
+ type: "object",
166
+ properties: {
167
+ code: {
168
+ type: "string",
169
+ example: "ERROR_ABC",
170
+ },
171
+ },
172
+ },
173
+ },
174
+ },
175
+ },
176
+ {
177
+ info: { title: "Two" },
178
+ components: {
179
+ schemas: {
180
+ DemoError: {
181
+ $ref: "#/components/schemas/AnotherError",
182
+ },
183
+ },
184
+ },
185
+ },
186
+ ]);
187
+ }).toThrow(
188
+ "Conflicting complex object detected: components.schemas.DemoError (One[properties], Two[$ref])"
189
+ );
190
+ });
191
+
192
+ it("does not throw when refs point to the same component", () => {
193
+ expect(() => {
194
+ ensureNoComplexObjectCollisions([
195
+ {
196
+ info: { title: "One" },
197
+ components: {
198
+ schemas: {
199
+ DemoError: {
200
+ $ref: "#/components/schemas/AnotherError",
201
+ },
202
+ },
203
+ },
204
+ },
205
+ {
206
+ info: { title: "Two" },
207
+ components: {
208
+ schemas: {
209
+ DemoError: {
210
+ $ref: "#/components/schemas/AnotherError",
211
+ },
212
+ },
213
+ },
214
+ },
215
+ ]);
216
+ }).not.toThrow();
217
+ });
218
+
219
+ it("does not throw when oneOf point to the same component (order independent)", () => {
220
+ expect(() => {
221
+ ensureNoComplexObjectCollisions([
222
+ {
223
+ info: { title: "One" },
224
+ components: {
225
+ schemas: {
226
+ DemoError: {
227
+ oneOf: ["#/components/schemas/One", "#/components/schemas/Two"],
228
+ },
229
+ },
230
+ },
231
+ },
232
+ {
233
+ info: { title: "Two" },
234
+ components: {
235
+ schemas: {
236
+ DemoError: {
237
+ oneOf: ["#/components/schemas/Two", "#/components/schemas/One"],
238
+ },
239
+ },
240
+ },
241
+ },
242
+ ]);
243
+ }).not.toThrow();
244
+ });
245
+
246
+ it("throws when allOf values are provided in different orders", () => {
247
+ expect(() => {
248
+ ensureNoComplexObjectCollisions([
249
+ {
250
+ info: { title: "One" },
251
+ components: {
252
+ schemas: {
253
+ DemoError: {
254
+ allOf: ["#/components/schemas/One", "#/components/schemas/Two"],
255
+ },
256
+ },
257
+ },
258
+ },
259
+ {
260
+ info: { title: "Two" },
261
+ components: {
262
+ schemas: {
263
+ DemoError: {
264
+ allOf: ["#/components/schemas/Two", "#/components/schemas/One"],
265
+ },
266
+ },
267
+ },
268
+ },
269
+ ]);
270
+ }).toThrow(
271
+ "Conflicting complex object detected: components.schemas.DemoError (One[allOf], Two[allOf])"
272
+ );
273
+ });
274
+
275
+ it("throws when oneOf is detected alongside other values", () => {
276
+ expect(() => {
277
+ ensureNoComplexObjectCollisions([
278
+ {
279
+ info: { title: "One" },
280
+ components: {
281
+ schemas: {
282
+ DemoError: {
283
+ type: "object",
284
+ properties: {
285
+ code: {
286
+ type: "string",
287
+ example: "ERROR_ABC",
288
+ },
289
+ },
290
+ },
291
+ },
292
+ },
293
+ },
294
+ {
295
+ info: { title: "Two" },
296
+ components: {
297
+ schemas: {
298
+ DemoError: {
299
+ oneOf: ["#/components/schemas/AnotherError"],
300
+ },
301
+ },
302
+ },
303
+ },
304
+ ]);
305
+ }).toThrow(
306
+ "Conflicting complex object detected: components.schemas.DemoError (One[properties], Two[oneOf])"
307
+ );
308
+ });
309
+ });
310
+
155
311
  describe("path collisions", () => {
156
312
  it("does not throw with overlapping paths and different verbs", () => {
157
313
  expect(
@@ -453,71 +609,3 @@ describe("returns unique items for:", () => {
453
609
  });
454
610
  });
455
611
  });
456
-
457
- describe("validates the final output", () => {
458
- it("throws when $ref is detected alongside other values", () => {
459
- expect(() => {
460
- merger([
461
- {
462
- components: {
463
- schemas: {
464
- DemoError: {
465
- type: "object",
466
- properties: {
467
- code: {
468
- type: "string",
469
- example: "ERROR_ABC",
470
- },
471
- },
472
- },
473
- },
474
- },
475
- },
476
- {
477
- components: {
478
- schemas: {
479
- DemoError: {
480
- $ref: "#/components/schemas/AnotherError",
481
- },
482
- },
483
- },
484
- },
485
- ]);
486
- }).toThrow(
487
- "Cannot have $ref and other properties at the same level: components.schemas.DemoError.$ref"
488
- );
489
- });
490
-
491
- it("throws when $ref is detected alongside other values", () => {
492
- expect(() => {
493
- merger([
494
- {
495
- components: {
496
- schemas: {
497
- DemoError: {
498
- type: "object",
499
- properties: {
500
- code: {
501
- type: "string",
502
- example: "ERROR_ABC",
503
- },
504
- },
505
- },
506
- },
507
- },
508
- },
509
- {
510
- components: {
511
- schemas: {
512
- DemoError: {
513
- oneOf: ["#/components/schemas/AnotherError"],
514
- },
515
- },
516
- },
517
- },
518
- ]);
519
- }).toThrow(
520
- "Cannot have oneOf and other properties at the same level: components.schemas.DemoError.oneOf"
521
- );
522
- });
523
- });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oas-toolkit",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {