Skip to content

Commit 06d298a

Browse files
authored
fix(bundle): handle external URLs better (#30)
1 parent 64e22bb commit 06d298a

File tree

3 files changed

+165
-5
lines changed

3 files changed

+165
-5
lines changed

lib/bundle/index.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const $Ref = require("../ref");
44
const Pointer = require("../pointer");
55
const url = require("../util/url");
66
const { safePathToPointer, safePointerToPath } = require("../util/url");
7-
const { get, set } = require("./util/object");
7+
const { get, set, unset } = require("./util/object");
88

99
module.exports = bundle;
1010

@@ -291,14 +291,26 @@ function remap (schema, inventory, $refs, options, customRoots) {
291291
let file, hash, pathFromRoot;
292292
for (let entry of inventory) {
293293
// console.log('Re-mapping $ref pointer "%s" at %s', entry.$ref.$ref, entry.pathFromRoot);
294-
// if entry is not dinlineable and has a custom root linked, we need to remap the properties of the object
294+
// if entry is not inlineable and has a custom root linked, we need to remap the properties of the object
295295
if (customRoots[entry.file] && customRoots[entry.file][entry.hash] !== null) {
296-
if (entry.hash === "#" || !("#" in customRoots[entry.file])) {
296+
if (entry.hash === "#" || !customRoots[entry.file]["#"]) {
297+
entry.$ref.$ref = customRoots[entry.file][entry.hash];
298+
297299
// the whole file is referenced for the first time
298300
// we need to inject its entire content here
299301
// or only certain fragment of the whole file has a custom root and we need to inject that portion
302+
// assuming we haven't already set it earlier
303+
if (get(schema, customRoots[entry.file][entry.hash])) continue;
300304
set(schema, customRoots[entry.file][entry.hash], $Ref.dereference(entry.$ref, entry.value));
301-
entry.$ref.$ref = customRoots[entry.file][entry.hash];
305+
306+
// let's try to remove a shared definition in the $reffed schema that won't be used
307+
let parentEntry = inventory.find(({ file, hash }) => file === entry.file && hash === "#");
308+
// parentEntry will be undefined if we never reference the entire document
309+
if (!parentEntry) continue;
310+
pathFromRoot = parentEntry.pathFromRoot;
311+
if (entry.hash !== "#" && pathFromRoot) {
312+
unset(schema, Pointer.join(pathFromRoot, Pointer.parse(entry.hash.replace(hash, "#"))));
313+
}
302314
}
303315
// entry is not supposed to be moved anywhere, it's not placed under root
304316
else {
@@ -339,7 +351,7 @@ function remap (schema, inventory, $refs, options, customRoots) {
339351
hash = entry.hash;
340352

341353
// This is the first $ref to point to this value, so dereference the value.
342-
// Any other $refs that point to the sam e value will point to this $ref instead
354+
// Any other $refs that point to the same value will point to this $ref instead
343355
if (entry.file in customRoots && customRoots[entry.file]["#"]) {
344356
entry.$ref.$ref = entry.mappedPathFromRoot;
345357
pathFromRoot = entry.mappedPathFromRoot;

lib/bundle/util/object.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,25 @@ module.exports.set = function (obj, pointer, value) {
5656
}
5757
};
5858

59+
/**
60+
* @param {object} obj
61+
* @param {string} pointer
62+
*/
63+
module.exports.unset = function (obj, pointer) {
64+
let tokens = Pointer.parse(pointer);
65+
66+
if (tokens.length === 0) {
67+
throw new TypeError("Path cannot point at root");
68+
}
69+
70+
let curObj = obj;
71+
for (let i = 0; i < tokens.length - 1; i++) {
72+
let segment = tokens[i];
73+
curObj = curObj[segment];
74+
}
75+
76+
if (curObj && (tokens[tokens.length - 1] in curObj)) {
77+
delete curObj[tokens[tokens.length - 1]];
78+
}
79+
};
80+

test/specs/custom-bundling-roots-new-stoplight/custom-bundling-roots-new-stoplight.spec.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,130 @@ describe("Stoplight-specific defaults", () => {
136136
}
137137
});
138138
});
139+
140+
it("should handle external URLs", async () => {
141+
setupHttpMocks({
142+
"https://api.stoplight.io/v1/schema/user.json": {
143+
definitions: {
144+
status: {
145+
type: "string",
146+
}
147+
},
148+
type: "object",
149+
properties: {
150+
orders: {
151+
type: "object",
152+
properties: {
153+
new: { $ref: "#/definitions/status" },
154+
cancelled: { $ref: "#/definitions/status" },
155+
error: { $ref: "#/definitions/status" }
156+
},
157+
}
158+
},
159+
},
160+
});
161+
162+
let parser = new $RefParser();
163+
let defaults = createStoplightDefaults({
164+
cwd: __dirname,
165+
endpointUrl: "http://jakub.stoplight-local.com:8080/api/v1/projects/jakub/my-project/nodes",
166+
});
167+
168+
const document = {
169+
swagger: "2.0",
170+
paths: {
171+
"/": {
172+
get: {
173+
responses: {
174+
200: {
175+
$ref: "#/responses/Address"
176+
}
177+
}
178+
},
179+
post: {
180+
responses: {
181+
200: {
182+
$ref: "#/responses/Address"
183+
}
184+
},
185+
parameters: [
186+
{
187+
in: "body",
188+
schema: {
189+
$ref: "https://api.stoplight.io/v1/schema/user.json"
190+
}
191+
}
192+
]
193+
}
194+
}
195+
},
196+
responses: {
197+
Address: {
198+
title: "Address",
199+
}
200+
}
201+
};
202+
203+
const schema = await parser.bundle(document, {
204+
bundle: defaults.oas2,
205+
});
206+
207+
expect(schema).to.equal(parser.schema);
208+
expect(schema).to.deep.equal({
209+
swagger: "2.0",
210+
definitions: {
211+
User_Status: {
212+
type: "string"
213+
}
214+
},
215+
paths: {
216+
"/": {
217+
get: {
218+
responses: {
219+
200: {
220+
$ref: "#/responses/Address"
221+
}
222+
}
223+
},
224+
post: {
225+
parameters: [
226+
{
227+
in: "body",
228+
schema: {
229+
type: "object",
230+
definitions: {},
231+
properties: {
232+
orders: {
233+
type: "object",
234+
properties: {
235+
cancelled: {
236+
$ref: "#/definitions/User_Status"
237+
},
238+
error: {
239+
$ref: "#/definitions/User_Status"
240+
},
241+
new: {
242+
$ref: "#/definitions/User_Status"
243+
}
244+
},
245+
}
246+
}
247+
}
248+
}
249+
],
250+
responses: {
251+
200: {
252+
$ref: "#/responses/Address"
253+
}
254+
}
255+
}
256+
}
257+
},
258+
responses: {
259+
Address: {
260+
title: "Address"
261+
}
262+
}
263+
});
264+
});
139265
});

0 commit comments

Comments
 (0)