Skip to content

Commit 21d8a89

Browse files
committed
Merge pull request #947 from swagger-api/develop_2.0
updated client for remote ref support
2 parents 89d6bed + ff5e847 commit 21d8a89

File tree

5 files changed

+815
-411
lines changed

5 files changed

+815
-411
lines changed

dist/lib/swagger-client.js

+206-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* swagger-client - swagger.js is a javascript client for use with swaggering APIs.
3-
* @version v2.1.8-M1
3+
* @version v2.1.9-M1
44
* @link http://swagger.io
55
* @license apache 2.0
66
*/
@@ -301,6 +301,206 @@ PrimitiveModel.prototype.getMockSignature = function(modelsToIgnore) {
301301
}
302302
return returnVal;
303303
};
304+
/**
305+
* Resolves a spec's remote references
306+
*/
307+
var Resolver = function (){};
308+
309+
Resolver.prototype.resolve = function(spec, callback, scope) {
310+
this.scope = (scope || this);
311+
var host, name, path, property, propertyName, type;
312+
var processedCalls = 0, resolvedRefs = {}, unresolvedRefs = {};
313+
314+
// store objects for dereferencing
315+
var resolutionTable = {};
316+
317+
// models
318+
for(name in spec.definitions) {
319+
var model = spec.definitions[name];
320+
for(propertyName in model.properties) {
321+
property = model.properties[propertyName];
322+
this.resolveTo(property, resolutionTable);
323+
}
324+
}
325+
// operations
326+
for(name in spec.paths) {
327+
var method, operation, responseCode;
328+
path = spec.paths[name];
329+
for(method in path) {
330+
operation = path[method];
331+
var i, parameters = operation.parameters;
332+
for(i in parameters) {
333+
var parameter = parameters[i];
334+
if(parameter.in === 'body' && parameter.schema) {
335+
this.resolveTo(parameter.schema, resolutionTable);
336+
}
337+
if(parameter.$ref) {
338+
this.resolveInline(spec, parameter, resolutionTable, unresolvedRefs);
339+
}
340+
}
341+
for(responseCode in operation.responses) {
342+
var response = operation.responses[responseCode];
343+
if(response.schema) {
344+
this.resolveTo(response.schema, resolutionTable);
345+
}
346+
}
347+
}
348+
}
349+
// get hosts
350+
var opts = {}, expectedCalls = 0;
351+
for(name in resolutionTable) {
352+
var parts = name.split('#');
353+
if(parts.length == 2) {
354+
host = parts[0]; path = parts[1];
355+
if(!Array.isArray(opts[host])) {
356+
opts[host] = [];
357+
expectedCalls += 1;
358+
}
359+
opts[host].push(path);
360+
}
361+
}
362+
363+
for(name in opts) {
364+
var self = this, opt = opts[name];
365+
host = name;
366+
367+
var obj = {
368+
useJQuery: false, // TODO
369+
url: host,
370+
method: "get",
371+
headers: {
372+
accept: this.scope.swaggerRequestHeaders || 'application/json'
373+
},
374+
on: {
375+
error: function(response) {
376+
processedCalls += 1;
377+
var i;
378+
for(i = 0; i < opt.length; i++) {
379+
// fail all of these
380+
var resolved = host + '#' + opt[i];
381+
unresolvedRefs[resolved] = null;
382+
}
383+
if(processedCalls === expectedCalls)
384+
self.finish(spec, resolutionTable, resolvedRefs, unresolvedRefs, callback);
385+
},
386+
response: function(response) {
387+
var i, j, swagger = response.obj;
388+
processedCalls += 1;
389+
for(i = 0; i < opt.length; i++) {
390+
var location = swagger, path = opt[i], parts = path.split('/');
391+
for(j = 0; j < parts.length; j++) {
392+
var segment = parts[j];
393+
if(typeof location === 'undefined')
394+
break;
395+
if(segment.length > 0)
396+
location = location[segment];
397+
}
398+
var resolved = host + '#' + path, resolvedName = parts[j-1];
399+
if(typeof location !== 'undefined') {
400+
resolvedRefs[resolved] = {
401+
name: resolvedName,
402+
obj: location
403+
};
404+
}
405+
else unresolvedRefs[resolved] = null;
406+
}
407+
if(processedCalls === expectedCalls)
408+
self.finish(spec, resolutionTable, resolvedRefs, unresolvedRefs, callback);
409+
}
410+
}
411+
};
412+
authorizations.apply(obj);
413+
new SwaggerHttp().execute(obj);
414+
}
415+
if(Object.keys(opts).length === 0)
416+
callback.call(this.scope, spec, unresolvedRefs);
417+
};
418+
419+
Resolver.prototype.finish = function(spec, resolutionTable, resolvedRefs, unresolvedRefs, callback) {
420+
// walk resolution table and replace with resolved refs
421+
var ref;
422+
for(ref in resolutionTable) {
423+
var i, locations = resolutionTable[ref];
424+
for(i = 0; i < locations.length; i++) {
425+
var resolvedTo = resolvedRefs[locations[i].obj.$ref];
426+
if(resolvedTo) {
427+
if(!spec.definitions)
428+
spec.definitions = {};
429+
if(locations[i].resolveAs === '$ref') {
430+
spec.definitions[resolvedTo.name] = resolvedTo.obj;
431+
locations[i].obj.$ref = '#/definitions/' + resolvedTo.name;
432+
}
433+
else if (locations[i].resolveAs === 'inline') {
434+
var key;
435+
var targetObj = locations[i].obj;
436+
delete targetObj.$ref;
437+
for(key in resolvedTo.obj) {
438+
targetObj[key] = resolvedTo.obj[key];
439+
}
440+
}
441+
}
442+
}
443+
}
444+
callback.call(this.scope, spec, unresolvedRefs);
445+
};
446+
447+
/**
448+
* immediately in-lines local refs, queues remote refs
449+
* for inline resolution
450+
*/
451+
Resolver.prototype.resolveInline = function (spec, property, objs, unresolvedRefs) {
452+
var ref = property.$ref;
453+
if(ref) {
454+
if(ref.indexOf('http') === 0) {
455+
if(Array.isArray(objs[ref])) {
456+
objs[ref].push({obj: property, resolveAs: 'inline'});
457+
}
458+
else {
459+
objs[ref] = [{obj: property, resolveAs: 'inline'}];
460+
}
461+
}
462+
else if (ref.indexOf('#') === 0) {
463+
// local resolve
464+
var shortenedRef = ref.substring(1);
465+
var i, parts = shortenedRef.split('/'), location = spec;
466+
for(i = 0; i < parts.length; i++) {
467+
var part = parts[i];
468+
if(part.length > 0) {
469+
location = location[part];
470+
}
471+
}
472+
if(location) {
473+
delete property.$ref;
474+
var key;
475+
for(key in location) {
476+
property[key] = location[key];
477+
}
478+
}
479+
else unresolvedRefs[ref] = null;
480+
}
481+
}
482+
else if(property.type === 'array') {
483+
this.resolveTo(property.items, objs);
484+
}
485+
};
486+
487+
Resolver.prototype.resolveTo = function (property, objs) {
488+
var ref = property.$ref;
489+
if(ref) {
490+
if(ref.indexOf('http') === 0) {
491+
if(Array.isArray(objs[ref])) {
492+
objs[ref].push({obj: property, resolveAs: '$ref'});
493+
}
494+
else {
495+
objs[ref] = [{obj: property, resolveAs: '$ref'}];
496+
}
497+
}
498+
}
499+
else if(property.type === 'array') {
500+
var items = property.items;
501+
this.resolveTo(items, objs);
502+
}
503+
};
304504
var addModel = function(name, model) {
305505
models[name] = model;
306506
};
@@ -389,7 +589,7 @@ SwaggerClient.prototype.build = function(mock) {
389589

390590
if(responseObj.swagger && parseInt(responseObj.swagger) === 2) {
391591
self.swaggerVersion = responseObj.swagger;
392-
self.buildFromSpec(responseObj);
592+
new Resolver().resolve(responseObj, self.buildFromSpec, self);
393593
self.isValid = true;
394594
}
395595
else {
@@ -403,7 +603,9 @@ SwaggerClient.prototype.build = function(mock) {
403603
}
404604
};
405605
if(this.spec) {
406-
setTimeout(function() { self.buildFromSpec(self.spec); }, 10);
606+
setTimeout(function() {
607+
new Resolver().resolve(self.spec, self.buildFromSpec, self);
608+
}, 10);
407609
}
408610
else {
409611
authorizations.apply(obj);
@@ -3088,5 +3290,5 @@ e.SwaggerApi = SwaggerClient;
30883290
e.Operation = Operation;
30893291
e.Model = Model;
30903292
e.addModel = addModel;
3091-
3293+
e.Resolver = Resolver;
30923294
})();

0 commit comments

Comments
 (0)