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 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, key, objects) {
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([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oas-toolkit",
3
- "version": "0.13.1",
3
+ "version": "0.15.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {