oas-toolkit 0.7.3 → 0.7.5

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.
package/components.js CHANGED
@@ -1,11 +1,12 @@
1
1
  const traverse = require("traverse");
2
2
  const isEqual = require("lodash.isequal");
3
3
  const difference = require("lodash.difference");
4
+ const intersection = require("lodash.intersection");
4
5
 
5
6
  function removeUnusedComponents(oas) {
6
7
  const used = getReferencedComponents(oas);
7
8
  const defined = getDefinedComponents(oas);
8
- const unused = getUnusedComponents(defined, used);
9
+ const unused = getUnusedComponents(defined, used, oas);
9
10
 
10
11
  const result = removeSpecifiedComponents(oas, unused);
11
12
 
@@ -18,6 +19,20 @@ function removeUnusedComponents(oas) {
18
19
  return removeUnusedComponents(result);
19
20
  }
20
21
 
22
+ function getReferencesToComponent(oas, component) {
23
+ return traverse(oas).reduce(function (acc, x) {
24
+ if (
25
+ this.isLeaf &&
26
+ this.key == "$ref" &&
27
+ x == `#/${component.replace(/\./g, "/")}`
28
+ ) {
29
+ acc.push(this.path.slice(0, 3).join("."));
30
+ }
31
+
32
+ return acc;
33
+ }, []);
34
+ }
35
+
21
36
  function removeSpecifiedComponents(oas, unused) {
22
37
  oas = traverse(oas).clone();
23
38
  return traverse(oas).forEach(function (x) {
@@ -75,8 +90,61 @@ function getDefinedComponents(oas) {
75
90
  }, []);
76
91
  }
77
92
 
78
- function getUnusedComponents(all, referenced) {
79
- return difference(all, referenced);
93
+ // This is a horrible global hack, but it works
94
+ const referencesTo = {};
95
+ function getUnusedComponents(all, referenced, oas) {
96
+ const unused = difference(all, referenced);
97
+
98
+ if (!oas) {
99
+ return unused;
100
+ }
101
+
102
+ // If we have a component that is only referenced by itself, it's unused
103
+ const used = intersection(all, referenced);
104
+ for (let component of used) {
105
+ if (component.startsWith("components.securitySchemes")) {
106
+ continue;
107
+ }
108
+
109
+ const references = getReferencesToComponent(oas, component);
110
+ if (references.length == 1 && references[0] === component) {
111
+ unused.push(component);
112
+ }
113
+
114
+ // If there's a circular dependency and nothing else, it can be removed
115
+ for (let ref of references) {
116
+ referencesTo[ref] = getRecursiveReferencesToComponent(oas, ref);
117
+ }
118
+
119
+ let shouldRemove = true;
120
+ for (let ref of references) {
121
+ if (!isEqual(referencesTo[component], referencesTo[ref])) {
122
+ shouldRemove = false;
123
+ }
124
+ }
125
+ if (shouldRemove) {
126
+ unused.push(component);
127
+ }
128
+ }
129
+ return unused;
130
+ }
131
+
132
+ function getRecursiveReferencesToComponent(oas, component, originalComponents) {
133
+ if (!originalComponents) {
134
+ originalComponents = [];
135
+ }
136
+ originalComponents.push(component);
137
+ let refs = getReferencesToComponent(oas, component);
138
+ for (const ref of refs) {
139
+ if (!originalComponents.includes(ref)) {
140
+ refs = refs.concat(
141
+ getRecursiveReferencesToComponent(oas, ref, originalComponents)
142
+ );
143
+ }
144
+ }
145
+ refs.sort();
146
+
147
+ return refs;
80
148
  }
81
149
 
82
150
  module.exports = {
@@ -253,6 +253,149 @@ describe("#components", () => {
253
253
  });
254
254
  });
255
255
 
256
+ it("Removes schemas that are circular (self reference)", () => {
257
+ expect(
258
+ c.removeUnusedComponents({
259
+ info: { title: "One" },
260
+ components: {
261
+ schemas: {
262
+ MySchema: {
263
+ type: "object",
264
+ properties: {
265
+ subSchema: {
266
+ $ref: "#/components/schemas/MySchema",
267
+ },
268
+ },
269
+ },
270
+ },
271
+ },
272
+ })
273
+ ).toEqual({
274
+ info: { title: "One" },
275
+ components: {},
276
+ });
277
+ });
278
+
279
+ it("Removes schemas that are circular (loop)", () => {
280
+ expect(
281
+ c.removeUnusedComponents({
282
+ info: { title: "One" },
283
+ components: {
284
+ schemas: {
285
+ SchemaA: {
286
+ properties: {
287
+ subSchema: {
288
+ $ref: "#/components/schemas/SchemaC",
289
+ },
290
+ },
291
+ },
292
+ SchemaB: {
293
+ properties: {
294
+ subSchema: {
295
+ $ref: "#/components/schemas/SchemaA",
296
+ },
297
+ },
298
+ },
299
+ SchemaC: {
300
+ properties: {
301
+ subSchema: {
302
+ $ref: "#/components/schemas/SchemaB",
303
+ },
304
+ },
305
+ },
306
+ },
307
+ },
308
+ })
309
+ ).toEqual({
310
+ info: { title: "One" },
311
+ components: {},
312
+ });
313
+ });
314
+
315
+ it("Does not remove circular schemas that are also referenced elsewhere", () => {
316
+ expect(
317
+ c.removeUnusedComponents({
318
+ info: { title: "One" },
319
+ paths: {
320
+ "/foo": {
321
+ get: {
322
+ responses: {
323
+ 200: {
324
+ content: {
325
+ "application/json": {
326
+ schema: {
327
+ $ref: "#/components/schemas/AnotherSchema",
328
+ },
329
+ },
330
+ },
331
+ },
332
+ },
333
+ },
334
+ },
335
+ },
336
+ components: {
337
+ schemas: {
338
+ MySchema: {
339
+ type: "object",
340
+ properties: {
341
+ subSchema: {
342
+ $ref: "#/components/schemas/MySchema",
343
+ },
344
+ },
345
+ },
346
+ AnotherSchema: {
347
+ type: "object",
348
+ properties: {
349
+ Foo: {
350
+ $ref: "#/components/schemas/MySchema",
351
+ },
352
+ },
353
+ },
354
+ },
355
+ },
356
+ })
357
+ ).toEqual({
358
+ info: { title: "One" },
359
+ paths: {
360
+ "/foo": {
361
+ get: {
362
+ responses: {
363
+ 200: {
364
+ content: {
365
+ "application/json": {
366
+ schema: {
367
+ $ref: "#/components/schemas/AnotherSchema",
368
+ },
369
+ },
370
+ },
371
+ },
372
+ },
373
+ },
374
+ },
375
+ },
376
+ components: {
377
+ schemas: {
378
+ MySchema: {
379
+ type: "object",
380
+ properties: {
381
+ subSchema: {
382
+ $ref: "#/components/schemas/MySchema",
383
+ },
384
+ },
385
+ },
386
+ AnotherSchema: {
387
+ type: "object",
388
+ properties: {
389
+ Foo: {
390
+ $ref: "#/components/schemas/MySchema",
391
+ },
392
+ },
393
+ },
394
+ },
395
+ },
396
+ });
397
+ });
398
+
256
399
  it("removes unused security schemes", () => {
257
400
  const securityOas = {
258
401
  info: { title: "One" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oas-toolkit",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "jsonpath-plus": "^7.2.0",
22
22
  "lodash.difference": "^4.5.0",
23
23
  "lodash.get": "^4.4.2",
24
+ "lodash.intersection": "^4.4.0",
24
25
  "lodash.isequal": "^4.5.0",
25
26
  "lodash.uniqwith": "^4.5.0",
26
27
  "mergician": "^1.1.0",