swagger-router
Advanced tools
Comparing version 0.2.6 to 0.2.7
@@ -121,16 +121,15 @@ "use strict"; | ||
// First value, then each of the children (one by one) | ||
return fn(self.value, path) | ||
.then(function() { | ||
return P.resolve(Object.keys(self._children)) | ||
.each(function(childKey) { | ||
var segment = childKey.replace(/^\//, ''); | ||
var visitP = fn(self.value, path); | ||
if (Object.keys(self._children).length > 0) { | ||
Object.keys(self._children).forEach(function(childKey) { | ||
var segment = childKey[0] === '/' ? childKey.substr(1) : childKey; | ||
var child = self._children[childKey]; | ||
if (child === self) { | ||
// Don't enter an infinite loop on ** | ||
return; | ||
} else { | ||
return child.visitAsync(fn, path.concat([segment])); | ||
if (child !== self) { | ||
visitP = visitP.then(function() { | ||
return child.visitAsync(fn, path.concat([segment])); | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
return visitP; | ||
}; | ||
@@ -137,0 +136,0 @@ |
@@ -47,6 +47,23 @@ "use strict"; | ||
return object; | ||
}, | ||
_optionalPath: function(element) { | ||
if (element !== undefined) { | ||
return '/' + encodeURIComponent(element); | ||
} else { | ||
// Terminate the path | ||
throw ''; | ||
} | ||
}, | ||
_encodeURIComponent: function(s) { | ||
s = s || ''; | ||
if (/[^\w_-]/.test(s)) { | ||
return encodeURIComponent(s); | ||
} else { | ||
return s; | ||
} | ||
} | ||
}; | ||
function splitAndPrepareTAsseblyTemplate(templateSpec, part) { | ||
function splitAndPrepareTAsseblyTemplate(templateSpec, options) { | ||
options = options || {}; | ||
var result = []; | ||
@@ -68,6 +85,19 @@ var templateNest = 0; | ||
currentTemplate = templateSpec.substring(startIndex, index); | ||
var compiledExpression = expressionCompiler.parse(currentTemplate); | ||
if (options.isURI) { | ||
if (/^\+/.test(currentTemplate)) { | ||
// literal substitution, just strip the prefix. | ||
currentTemplate = currentTemplate.substring(1); | ||
} else if (/^\//.test(currentTemplate)) { | ||
currentTemplate = '$$._optionalPath(' + currentTemplate.substring(1) + ')'; | ||
} else { | ||
currentTemplate = '$$._encodeURIComponent(' + currentTemplate + ')'; | ||
} | ||
} | ||
var compiledExpression = expressionCompiler.parse(currentTemplate.trim()); | ||
// FIXME: Rewrite path prefixes in expressionCompiler! | ||
compiledExpression = compiledExpression.replace(/([,(\[:])m\./g, | ||
'$1rm.' + part + '.'); | ||
if (options.part) { | ||
compiledExpression = compiledExpression.replace(/([,(\[:])m\./g, | ||
'$1rm.' + options.part + '.'); | ||
} | ||
result.push(['raw', compiledExpression]); | ||
@@ -92,3 +122,3 @@ startIndex = index + 1; | ||
function compileTAssembly(template, reqPart) { | ||
function compileTAssembly(template, reqPart, globals) { | ||
var res; | ||
@@ -107,3 +137,3 @@ var callback = function(bit) { | ||
cb: callback, | ||
globals: globalMethods, | ||
globals: globals || globalMethods, | ||
}; | ||
@@ -116,5 +146,6 @@ var resolveTemplate = TAssembly.compile(template, options); | ||
rm: context.rm, | ||
g: options.globals, | ||
options: context.options || options, | ||
cb: options.cb, | ||
m: context.rm.request[reqPart], | ||
cb: options.cb, | ||
options: context.options || options, | ||
}; | ||
@@ -134,32 +165,17 @@ | ||
*/ | ||
function createURIResolver(spec) { | ||
if (/^\{[^\{}]+}$/.test(spec.uri) || /\{\$\$?\..+}/.test(spec.uri)) { | ||
var tassemblyTemplate = splitAndPrepareTAsseblyTemplate(spec.uri); | ||
var resolver = compileTAssembly(tassemblyTemplate, 'params'); | ||
function createURIResolver(uri, globals) { | ||
// Check if this is a simple path template | ||
if (/^\/(?:[^{]*\{[\/\+]?[a-zA-Z_-]+\}[^{]*)*$/.test(uri)) { | ||
var pathTemplate = new URI(uri, {}, true); | ||
return function(context) { | ||
var value = resolver(context); | ||
if (value.constructor !== URI) { | ||
value = new URI(value, {}, false); | ||
} | ||
return value; | ||
return pathTemplate.expand(context.rm.request.params); | ||
}; | ||
} else if (/^(?:https?:\/\/)?\{[^\/]+}\//.test(spec.uri)) { | ||
// The host is templated - replace it with TAssembly and use URI.expand for path templates | ||
var hostTemplate = /^((?:https?:\/\/)?\{[^\/]+}\/)/.exec(spec.uri)[1]; | ||
var hostTassembly = splitAndPrepareTAsseblyTemplate(hostTemplate); | ||
var hostResolver = compileTAssembly(hostTassembly, 'params'); | ||
var path = spec.uri.substr(hostTemplate.length); | ||
var pathTemplate = new URI('/' + path, {}, true); | ||
return function(context) { | ||
var newHost = hostResolver(context); | ||
// FIXME: Support references to other parts of the request. | ||
// params['$'] = context.rm; | ||
var newUri = pathTemplate.expand(context.rm.request.params); | ||
newUri.urlObj = url.parse(newHost + path); | ||
return newUri; | ||
}; | ||
} else if (/\{/.test(uri)) { | ||
var tassemblyTemplate = splitAndPrepareTAsseblyTemplate(uri, { isURI: true }); | ||
// console.log('tass', spec.uri, tassemblyTemplate); | ||
return compileTAssembly(tassemblyTemplate, 'params', globals); | ||
} else { | ||
var uriTemplate = new URI(spec.uri, {}, true); | ||
return function(context) { | ||
return uriTemplate.expand(context.rm.request.params); | ||
return uri; | ||
}; | ||
@@ -205,7 +221,7 @@ } | ||
} else { | ||
var tAssemblyTemplates = splitAndPrepareTAsseblyTemplate(subSpec, part); | ||
var tAssemblyTemplates = splitAndPrepareTAsseblyTemplate(subSpec, { part: part }); | ||
if (tAssemblyTemplates.length > 1) { | ||
// This is a string with partial templates | ||
// Compile a function | ||
var resolver = compileTAssembly(tAssemblyTemplates, part); | ||
var resolver = compileTAssembly(tAssemblyTemplates, part, globals); | ||
// Replace the complex template with a function call | ||
@@ -248,6 +264,8 @@ var fnName = 'fn_' + globals._i++; | ||
* fields that couldn't be resolved from original request would be ignored. | ||
* @param {object} globalsInit, an object to merge into the global namespace | ||
* available in the template. | ||
*/ | ||
function Template(spec) { | ||
function Template(spec, globalsInit) { | ||
var self = this; | ||
var globals = Object.assign({}, globalMethods); | ||
var globals = Object.assign({}, globalMethods, globalsInit); | ||
spec = _cloneSpec(spec); | ||
@@ -261,4 +279,4 @@ globals._i = 0; | ||
if (part === 'uri') { | ||
globals.uri = createURIResolver(spec); | ||
spec.uri = '$$.uri($context)'; | ||
globals._uri = createURIResolver(spec.uri, globals); | ||
spec.uri = '$$._uri($context)'; | ||
} else if (part === 'method') { | ||
@@ -292,5 +310,7 @@ spec.method = "'" + (spec.method || 'get') + "'"; | ||
rc: null, | ||
rm: null, | ||
g: globals, | ||
options: options, | ||
cb: callback, | ||
m: null, | ||
}; | ||
@@ -297,0 +317,0 @@ c.rc = c; |
"use strict"; | ||
var url = require('url'); | ||
var utils = require('./utils'); | ||
@@ -19,22 +18,20 @@ /** | ||
this.params = params || {}; | ||
this.urlObj = null; | ||
this.path = []; | ||
this.protoHost = null; | ||
this.path = null; | ||
this._pathMetadata = {}; | ||
if (uri && uri.constructor === URI) { | ||
this.urlObj = uri.urlObj; | ||
if (typeof uri === 'string') { | ||
var protoHostMatch = /^[^\/]+:(?:\/\/)?[^\/]+/.exec(uri); | ||
if (protoHostMatch) { | ||
this.protoHost = protoHostMatch[0]; | ||
uri = uri.substring(this.protoHost.length); | ||
} | ||
this.path = utils.parsePath(uri, asPattern); | ||
} else if (Array.isArray(uri)) { | ||
this.path = uri; | ||
} else if (uri && uri.constructor === URI) { | ||
this.protoHost = uri.protoHost; | ||
// this.path is considered immutable, so can be shared with other URI | ||
// instances | ||
this.path = uri.path; | ||
} else if (uri && (uri.constructor === String || Array.isArray(uri))) { | ||
if (uri.constructor === String) { | ||
if (/^[^\/]+:/.test(uri)) { | ||
this.urlObj = url.parse(uri); | ||
// Work around encoding difference for {} between node 0.10 & | ||
// 0.12 / iojs. 0.10 leaves those chars as they are in .path, | ||
// newer node versions percent-encode them. | ||
uri = uri.substr(this.urlObj.resolve('/').length - 1); | ||
} | ||
} | ||
this.path = utils.parsePath(uri, asPattern); | ||
} else if (uri !== '') { | ||
@@ -61,4 +58,3 @@ throw new Error('Invalid path passed into URI constructor: ' + uri); | ||
var params = options.params || this.params; | ||
var uriStr = this.urlObj && this.urlObj.resolve('/').replace(/\/$/, '') | ||
|| ''; | ||
var uriStr = this.protoHost || ''; | ||
for (var i = 0; i < this.path.length; i++) { | ||
@@ -65,0 +61,0 @@ var segment = this.path[i]; |
"use strict"; | ||
var url = require('url'); | ||
var utils = {}; | ||
@@ -8,3 +10,6 @@ | ||
return uri; | ||
} else { | ||
} | ||
try { | ||
return decodeURIComponent(uri); | ||
} catch (e) { | ||
return uri.replace(/(%[0-9a-fA-F][0-9a-fA-F])+/g, function(m) { | ||
@@ -65,11 +70,14 @@ try { | ||
} else if (!isPattern) { | ||
var bits = path.replace(/^\//, '').split(/\//); | ||
if (!/%/.test(path)) { | ||
// fast path | ||
return bits; | ||
} else { | ||
return bits.map(function(bit) { | ||
return robustDecodeURIComponent(bit); | ||
}); | ||
if (path.charCodeAt(0) === 47 /* "/" */) { | ||
path = path.substring(1); | ||
} | ||
var bits = path.split('/'); | ||
if (/%/.test(path)) { | ||
for (var i = 0; i < bits.length; i++) { | ||
if (/%/.test(bits[i])) { | ||
bits[i] = robustDecodeURIComponent(bits[i]); | ||
} | ||
} | ||
} | ||
return bits; | ||
} else { | ||
@@ -76,0 +84,0 @@ return parsePattern(path); |
{ | ||
"name": "swagger-router", | ||
"version": "0.2.6", | ||
"version": "0.2.7", | ||
"description": "An efficient swagger 2 based router with support for multiple APIs. For use in RESTBase.", | ||
@@ -9,3 +9,4 @@ "main": "index.js", | ||
"coverage": "istanbul cover _mocha -- -R spec", | ||
"coveralls": "cat ./coverage/lcov.info | coveralls" | ||
"coveralls": "cat ./coverage/lcov.info | coveralls", | ||
"bench": "node test/bench.js" | ||
}, | ||
@@ -29,3 +30,3 @@ "repository": { | ||
"bluebird": "2.8.2", | ||
"core-js": "^1.2.2", | ||
"core-js": "^1.2.6", | ||
"js-yaml": "^3.4.2", | ||
@@ -32,0 +33,0 @@ "tassembly": "^0.2.0", |
@@ -106,3 +106,3 @@ "use strict"; | ||
}); | ||
assert.deepEqual(result, expectedTemplatedRequest); | ||
assert.deepEqual(result + '', expectedTemplatedRequest + ''); | ||
}); | ||
@@ -122,6 +122,6 @@ | ||
}); | ||
assert.deepEqual(result.uri, | ||
assert.deepEqual(result.uri.toString(), | ||
new URI('http://en.wikipedia.org/path1/{path2}', {}, true).expand({ | ||
path2: 'test1/test2/test3' | ||
})); | ||
}).toString()); | ||
}); | ||
@@ -140,3 +140,4 @@ | ||
}); | ||
assert.deepEqual(resultNoOptional.uri, new URI('/en.wikipedia.org/path1{/optional}', {}, true).expand()); | ||
assert.deepEqual(resultNoOptional.uri.toString(), | ||
new URI('/en.wikipedia.org/path1{/optional}', {}, true).expand().toString()); | ||
var resultWithOptional = new Template(requestTemplate).expand({ | ||
@@ -150,7 +151,32 @@ request: { | ||
}); | ||
assert.deepEqual(resultWithOptional.uri, new URI('/en.wikipedia.org/path1{/optional}', {}, true).expand({ | ||
assert.deepEqual(resultWithOptional.uri.toString(), new URI('/en.wikipedia.org/path1{/optional}', {}, true).expand({ | ||
optional: 'value' | ||
})); | ||
}).toString()); | ||
}); | ||
it('should terminate when an optional path segment is missing', function() { | ||
var requestTemplate = { | ||
uri: '/{domain}{/a}{/b}{+path}' | ||
}; | ||
var resultNoOptional = new Template(requestTemplate).expand({ | ||
request: { | ||
params: { | ||
domain: 'en.wikipedia.org', | ||
b: 'b', | ||
} | ||
} | ||
}).uri.toString(); | ||
assert.deepEqual(resultNoOptional, '/en.wikipedia.org'); | ||
var resultWithOptional = new Template(requestTemplate).expand({ | ||
request: { | ||
params: { | ||
domain: 'en.wikipedia.org', | ||
a: 'a', | ||
path: 'path' | ||
} | ||
} | ||
}).uri.toString(); | ||
assert.deepEqual(resultWithOptional, '/en.wikipedia.org/a'); | ||
}); | ||
it('should support + templates in path', function() { | ||
@@ -164,18 +190,12 @@ var requestTemplate = { | ||
domain: 'en.wikipedia.org', | ||
path: [ | ||
'test1', | ||
'test2', | ||
'test3' | ||
] | ||
path: 'test1/test2/test3' | ||
} | ||
} | ||
}); | ||
assert.deepEqual(result.uri, | ||
assert.deepEqual(result.uri.toString(), | ||
new URI('http://en.wikipedia.org/path1/{+path}', {}, true).expand({ | ||
path: [ | ||
'test1', | ||
'test2', | ||
'test3' | ||
'test1/test2/test3' | ||
] | ||
})); | ||
}).toString()); | ||
}); | ||
@@ -185,3 +205,3 @@ | ||
var requestTemplate = { | ||
uri: '{uri}' | ||
uri: '{+uri}' | ||
}; | ||
@@ -195,3 +215,3 @@ var result = new Template(requestTemplate).expand({ | ||
}); | ||
assert.deepEqual(result.uri, new URI('en.wikipedia.org/path1/test1/test2/test3', {}, false)); | ||
assert.deepEqual(result.uri.toString(), 'en.wikipedia.org/path1/test1/test2/test3'); | ||
}); | ||
@@ -215,3 +235,3 @@ | ||
var template = new Template({ | ||
uri: '/path/{$$.default($.request.body.test, "default")}', | ||
uri: '/path/{$$.default($.request.body.test, "foo/bar")}', | ||
body: { | ||
@@ -241,3 +261,3 @@ complete: '{$$.default($.request.body.test, "default")}', | ||
}); | ||
assert.deepEqual(evaluatedDefaults.uri, '/path/default'); | ||
assert.deepEqual(evaluatedDefaults.uri, '/path/foo%2Fbar'); | ||
assert.deepEqual(evaluatedDefaults.body.complete, 'default'); | ||
@@ -354,2 +374,2 @@ assert.deepEqual(evaluatedDefaults.body.partial, '/test/default'); | ||
}); | ||
}); | ||
}); |
@@ -121,3 +121,2 @@ "use strict"; | ||
var uri = new URI('https://test.com/v1/page/title'); | ||
deepEqual(uri.urlObj.protocol, 'https:'); | ||
deepEqual(uri.path[0], 'v1'); | ||
@@ -124,0 +123,0 @@ deepEqual(uri.toString(), 'https://test.com/v1/page/title'); |
82339
25
1825
Updatedcore-js@^1.2.6