oas-toolkit 0.13.1 → 0.15.0
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/cli/bin.js +18 -0
- package/cli/commands/filter-annotation.js +14 -0
- package/filter-annotation.js +38 -0
- package/filter-annotation.test.js +48 -0
- package/index.js +1 -0
- package/merger.js +16 -2
- package/merger.test.js +36 -0
- package/package.json +1 -1
package/cli/bin.js
CHANGED
|
@@ -20,6 +20,24 @@ yargs(hideBin(process.argv))
|
|
|
20
20
|
"check for conflicting components, paths, tags, and security schemes",
|
|
21
21
|
require("./commands/check-conflicts")
|
|
22
22
|
)
|
|
23
|
+
.command(
|
|
24
|
+
"filter-annotation <openapi>",
|
|
25
|
+
"keep/remove all paths with a specific annotation",
|
|
26
|
+
(yargs) => {
|
|
27
|
+
yargs.option("keep", {
|
|
28
|
+
demandOption: false,
|
|
29
|
+
});
|
|
30
|
+
yargs.option("remove", {
|
|
31
|
+
demandOption: false
|
|
32
|
+
});
|
|
33
|
+
yargs.positional("openapi", {
|
|
34
|
+
require: true,
|
|
35
|
+
describe: "the OpenAPI file to rewrite",
|
|
36
|
+
type: "string",
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
require("./commands/filter-annotation")
|
|
40
|
+
)
|
|
23
41
|
.command(
|
|
24
42
|
"remove-unused-components <openapi>",
|
|
25
43
|
"remove unused components from the provided OpenAPI file",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const yaml = require("js-yaml");
|
|
3
|
+
|
|
4
|
+
module.exports = async function (argv) {
|
|
5
|
+
try {
|
|
6
|
+
const p = require("../../filter-annotation");
|
|
7
|
+
let oas = yaml.load(fs.readFileSync(argv.openapi));
|
|
8
|
+
oas = p.run(oas, { keep: argv.keep?.split(","), remove: argv.remove?.split(",") });
|
|
9
|
+
console.log(yaml.dump(oas));
|
|
10
|
+
} catch (e) {
|
|
11
|
+
console.error(`ERROR: ${e.message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const isEqual = require("lodash.isequal");
|
|
2
|
+
const uniqWith = require("lodash.uniqwith");
|
|
3
|
+
const url = require("url");
|
|
4
|
+
function run(oas, opts = {}) {
|
|
5
|
+
oas = JSON.parse(JSON.stringify(oas)); // Prevent modification of original object
|
|
6
|
+
|
|
7
|
+
for (const p of Object.keys(oas.paths)) {
|
|
8
|
+
const path = oas.paths[p];
|
|
9
|
+
for (const verb in path) {
|
|
10
|
+
if (opts.keep?.length > 0) {
|
|
11
|
+
for (const keep of opts.keep) {
|
|
12
|
+
if (path[verb][keep] === undefined) {
|
|
13
|
+
delete path[verb];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (opts.remove?.length > 0) {
|
|
19
|
+
for (const remove of opts.remove) {
|
|
20
|
+
if (path[verb][remove]) {
|
|
21
|
+
delete path[verb];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Remove empty paths
|
|
28
|
+
if (Object.keys(path).length === 0) {
|
|
29
|
+
delete oas.paths[p];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return oas;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
run,
|
|
38
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const c = require("./filter-annotation");
|
|
2
|
+
|
|
3
|
+
const oas = {
|
|
4
|
+
servers: [
|
|
5
|
+
{
|
|
6
|
+
url: "https://api.example.com/v1",
|
|
7
|
+
},
|
|
8
|
+
],
|
|
9
|
+
paths: {
|
|
10
|
+
"/foo/hello": {
|
|
11
|
+
get: {
|
|
12
|
+
"x-internal": true,
|
|
13
|
+
description: "Hello world endpoint",
|
|
14
|
+
},
|
|
15
|
+
post: {
|
|
16
|
+
description: "Create a hello world",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
describe("#run", () => {
|
|
24
|
+
it("with keep", () => {
|
|
25
|
+
expect(c.run(oas, { keep: ["x-internal"] }).paths).toEqual({
|
|
26
|
+
"/foo/hello": {
|
|
27
|
+
get: {
|
|
28
|
+
"x-internal": true,
|
|
29
|
+
description: "Hello world endpoint",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("with remove", () => {
|
|
36
|
+
expect(c.run(oas, { remove: ["x-internal"] }).paths).toEqual({
|
|
37
|
+
"/foo/hello": {
|
|
38
|
+
post: {
|
|
39
|
+
description: "Create a hello world",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("removes empty paths", () => {
|
|
46
|
+
expect(c.run(oas, { keep: ["x-missing"] }).paths).toEqual({});
|
|
47
|
+
});
|
|
48
|
+
});
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@ module.exports = {
|
|
|
2
2
|
canonicalServer: require("./canonical-server"),
|
|
3
3
|
components: require("./components"),
|
|
4
4
|
expandAllOf: require("./expand-allof"),
|
|
5
|
+
filterAnnotation: require("./filter-annotation"),
|
|
5
6
|
merger: require("./merger"),
|
|
6
7
|
removeWithAnnotation: require("./remove-with-annotation"),
|
|
7
8
|
rewritePath: require("./rewrite-path"),
|
package/merger.js
CHANGED
|
@@ -124,12 +124,26 @@ function ensureNoPathColissions(objects) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
function ensureListUniqueness(list,
|
|
127
|
+
function ensureListUniqueness(list, keys, objects) {
|
|
128
128
|
let all = [];
|
|
129
|
+
|
|
129
130
|
for (let object of objects) {
|
|
130
131
|
all = all.concat(object[list] || []);
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
// Update the key based on overrides
|
|
135
|
+
const key = keys[0];
|
|
136
|
+
keys = keys.slice(1);
|
|
137
|
+
all = all.map((s) => {
|
|
138
|
+
if (keys.length) {
|
|
139
|
+
const newKey = keys.find((k) => s[k]);
|
|
140
|
+
if (newKey) {
|
|
141
|
+
s[key] = s[newKey];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return s;
|
|
145
|
+
});
|
|
146
|
+
|
|
133
147
|
for (let c of all) {
|
|
134
148
|
const d = all.filter((t) => {
|
|
135
149
|
return t[key] == c[key] && !isEqual(c, t);
|
|
@@ -157,7 +171,7 @@ function ensureListUniqueness(list, key, objects) {
|
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
function ensureNoTagColissions(objects) {
|
|
160
|
-
ensureListUniqueness("tags", "name", objects);
|
|
174
|
+
ensureListUniqueness("tags", ["name", "x-bundled-name", "x-name-override"], objects);
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
function ensureNoSecurityColissions(objects) {
|
package/merger.test.js
CHANGED
|
@@ -20,6 +20,42 @@ describe("#ensureNoTagColissions", () => {
|
|
|
20
20
|
).toBe(undefined);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
it("throws with different descriptions", () => {
|
|
24
|
+
expect(() => {
|
|
25
|
+
ensureNoTagColissions([
|
|
26
|
+
{ info: { title: "One" }, tags: [{ name: "Demo", description: "Demo in One" }] },
|
|
27
|
+
{ info: { title: "Two" }, tags: [{ name: "Demo", description: "Demo in Two" }] },
|
|
28
|
+
])
|
|
29
|
+
}).toThrow(new Error("Conflicting tags detected: Demo (One, Two)"))
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("respects overrides (x-bundled-name)", () => {
|
|
33
|
+
expect(
|
|
34
|
+
ensureNoTagColissions([
|
|
35
|
+
{ info: { title: "One" }, tags: [{ name: "Demo", description: "Demo in One", "x-bundled-name": "Demo One" }] },
|
|
36
|
+
{ info: { title: "Two" }, tags: [{ name: "Demo", description: "Demo in Two", "x-bundled-name": "Demo Two" }] },
|
|
37
|
+
])
|
|
38
|
+
).toBe(undefined);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("respects overrides (x-name-override)", () => {
|
|
42
|
+
expect(
|
|
43
|
+
ensureNoTagColissions([
|
|
44
|
+
{ info: { title: "One" }, tags: [{ name: "Demo", description: "Demo in One", "x-name-override": "Demo One" }] },
|
|
45
|
+
{ info: { title: "Two" }, tags: [{ name: "Demo", description: "Demo in Two", "x-name-override": "Demo Two" }] },
|
|
46
|
+
])
|
|
47
|
+
).toBe(undefined);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("throws when the override is the same", () => {
|
|
51
|
+
expect(() => {
|
|
52
|
+
ensureNoTagColissions([
|
|
53
|
+
{ info: { title: "One" }, tags: [{ name: "Demo One", description: "Demo in One", "x-bundled-name": "Demo" }] },
|
|
54
|
+
{ info: { title: "Two" }, tags: [{ name: "Demo Two", description: "Demo in Two", "x-bundled-name": "Demo" }] },
|
|
55
|
+
])
|
|
56
|
+
}).toThrow(new Error("Conflicting tags detected: Demo (One, Two)"))
|
|
57
|
+
});
|
|
58
|
+
|
|
23
59
|
it("throws when there is a field in one tag but not in another", () => {
|
|
24
60
|
expect(() => {
|
|
25
61
|
ensureNoTagColissions([
|