Finished implementing router. Backuping non-class method
This commit is contained in:
parent
b8df546f18
commit
26c9b4a27e
9 changed files with 694 additions and 139 deletions
|
@ -9,7 +9,7 @@ export const dummy = function() { callItem() }
|
|||
export const allRoutes = [
|
||||
'/',
|
||||
'/api/articles',
|
||||
'/api/articles/:articleId/file',
|
||||
'/api/articles/:id/file',
|
||||
'/api/articles/:id',
|
||||
'/api/articles/public',
|
||||
'/api/articles/public/:id',
|
||||
|
@ -19,8 +19,8 @@ export const allRoutes = [
|
|||
'/api/media/:id',
|
||||
'/api/pages',
|
||||
'/api/pages/:id',
|
||||
'/api/pages/:pageId/articles',
|
||||
'/api/pages/:pageId/articles/public',
|
||||
'/api/pages/:id/articles',
|
||||
'/api/pages/:id/articles/public',
|
||||
'/api/staff',
|
||||
'/api/staff/:id',
|
||||
]
|
||||
|
@ -28,7 +28,7 @@ export const allRoutes = [
|
|||
export const allManyRoutes = [
|
||||
'/',
|
||||
'/api/articles',
|
||||
'/api/articles/:articleId/file',
|
||||
'/api/articles/:id/file',
|
||||
'/api/articles/:id',
|
||||
'/api/articles/public',
|
||||
'/api/articles/public/:id',
|
||||
|
@ -36,9 +36,9 @@ export const allManyRoutes = [
|
|||
'/api/categories/:categoryId/products',
|
||||
'/api/categories/:categoryId/properties',
|
||||
'/api/categories/:categoryId/values/:props',
|
||||
'/api/categories/:id',
|
||||
'/api/categories/:id/products/:productId',
|
||||
'/api/categories/:id/products/:productId',
|
||||
'/api/categories/:categoryId',
|
||||
'/api/categories/:categoryId/products/:productId',
|
||||
'/api/categories/:categoryId/products/:productId',
|
||||
'/api/customers',
|
||||
'/api/customers/:id',
|
||||
'/api/customers/kennitala/:kennitala',
|
||||
|
@ -51,10 +51,10 @@ export const allManyRoutes = [
|
|||
'/api/orderitem',
|
||||
'/api/orderitem/:id',
|
||||
'/api/orders',
|
||||
'/api/orders/:id',
|
||||
'/api/orders/:orderId',
|
||||
'/api/orders/:orderId/sell',
|
||||
'/api/pages',
|
||||
'/api/pages/:id',
|
||||
'/api/pages/:pageId',
|
||||
'/api/pages/:pageId/articles',
|
||||
'/api/pages/:pageId/articles/public',
|
||||
'/api/products',
|
||||
|
|
2
benchmark/index.bat
Normal file
2
benchmark/index.bat
Normal file
|
@ -0,0 +1,2 @@
|
|||
start /B /WAIT /REALTIME node index.js
|
||||
pause
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'assert'
|
||||
import Benchmark from 'benchmarkjs-pretty'
|
||||
import { koaRouter1, koaRouter2 } from './router_koa.js'
|
||||
import { flaskaRouter1, flaskaRouter2 } from './router_flaska.js'
|
||||
import { flaskaRouter1, flaskaRouter2, flaskaClassRouter1, flaskaClassRouter2 } from './router_flaska.js'
|
||||
import { expressRouter1, expressRouter2 } from './router_express.js'
|
||||
import * as consts from './const.js'
|
||||
|
||||
|
@ -28,6 +28,10 @@ function TestSmallStaticRoute() {
|
|||
testData = flaskaRouter1.match('/api/staff')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.add('bottle-router-alt', function() {
|
||||
testData = flaskaClassRouter1.match('/api/staff')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.run()
|
||||
.then(function() {}, function(e) {
|
||||
console.error('error:', e)
|
||||
|
@ -53,6 +57,10 @@ function TestSmallParamRoute() {
|
|||
testData = flaskaRouter1.match('/api/staff/justatest')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.add('bottle-router-alt', function() {
|
||||
testData = flaskaClassRouter1.match('/api/staff/justatest')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.run()
|
||||
.then(function() {}, function(e) {
|
||||
console.error('error:', e)
|
||||
|
@ -78,6 +86,10 @@ function TestLargeStaticRoute() {
|
|||
testData = flaskaRouter2.match('/api/staff')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.add('bottle-router-alt', function() {
|
||||
testData = flaskaClassRouter2.match('/api/staff')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.run()
|
||||
.then(function() {}, function(e) {
|
||||
console.error('error:', e)
|
||||
|
@ -103,6 +115,39 @@ function TestLargeParamRoute() {
|
|||
testData = flaskaRouter2.match('/api/staff/justatest')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.add('bottle-router-alt', function() {
|
||||
testData = flaskaClassRouter2.match('/api/staff/justatest')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.run()
|
||||
.then(function() {}, function(e) {
|
||||
console.error('error:', e)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
function TestLargeParamLargeUrlRoute() {
|
||||
return new Benchmark.default('Large router param route long route benchmark: /api/products/:id/sub_products/:productId (58 routes registered)')
|
||||
.add('expressjs', function() {
|
||||
testData = null
|
||||
expressRouter2.handle({
|
||||
url: '/api/products/justatest/sub_products/foobar',
|
||||
method: 'GET',
|
||||
}, {}, function() { })
|
||||
assert.ok(testData)
|
||||
})
|
||||
.add('koa-router', function() {
|
||||
testData = koaRouter2.match('/api/products/justatest/sub_products/foobar', 'GET')
|
||||
assert.ok(testData.route)
|
||||
})
|
||||
.add('bottle-router', function() {
|
||||
testData = flaskaRouter2.match('/api/products/justatest/sub_products/foobar')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.add('bottle-router-alt', function() {
|
||||
testData = flaskaClassRouter2.match('/api/products/justatest/sub_products/foobar')
|
||||
assert.ok(testData.handler)
|
||||
})
|
||||
.run()
|
||||
.then(function() {}, function(e) {
|
||||
console.error('error:', e)
|
||||
|
@ -120,6 +165,9 @@ TestSmallStaticRoute()
|
|||
.then(function() {
|
||||
return TestLargeParamRoute()
|
||||
})
|
||||
.then(function() {
|
||||
return TestLargeParamLargeUrlRoute()
|
||||
})
|
||||
.then(function() {
|
||||
process.exit(0)
|
||||
})
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
import { FlaskaRouter } from '../flaska.mjs'
|
||||
import { FlaskaRouter, FlaskaRouterClass } from '../flaska.mjs'
|
||||
import * as consts from './const.js'
|
||||
|
||||
const router1 = new FlaskaRouter()
|
||||
const router2 = new FlaskaRouter()
|
||||
|
||||
const classRouter1 = new FlaskaRouterClass()
|
||||
const classRouter2 = new FlaskaRouterClass()
|
||||
|
||||
for (let key in consts.allRoutes) {
|
||||
router1.addRoute(consts.allRoutes[key], consts.dummy)
|
||||
classRouter1.addRoute(consts.allRoutes[key], consts.dummy)
|
||||
}
|
||||
|
||||
for (let key in consts.allManyRoutes) {
|
||||
router2.addRoute(consts.allManyRoutes[key], consts.dummy)
|
||||
classRouter2.addRoute(consts.allManyRoutes[key], consts.dummy)
|
||||
}
|
||||
|
||||
export {
|
||||
router1 as flaskaRouter1,
|
||||
router2 as flaskaRouter2,
|
||||
classRouter1 as flaskaClassRouter1,
|
||||
classRouter2 as flaskaClassRouter2,
|
||||
}
|
||||
|
|
372
flaska.mjs
372
flaska.mjs
|
@ -2,123 +2,335 @@
|
|||
* Router
|
||||
*/
|
||||
|
||||
function Branch() {
|
||||
this._map = new Map()
|
||||
this._paramName = null
|
||||
this._paramPrototype = null
|
||||
this._handler = null
|
||||
class Branch {
|
||||
constructor() {
|
||||
this.children = new Map()
|
||||
this.paramName = null
|
||||
this.fullparamName = null
|
||||
this.handler = null
|
||||
this.middlewares = []
|
||||
}
|
||||
}
|
||||
|
||||
const __paramMapName = '__param'
|
||||
const __fullParamMapName = '__fullparam'
|
||||
|
||||
function FlaskaRouter() {
|
||||
this._root = new Branch()
|
||||
}
|
||||
export class FlaskaRouter {
|
||||
constructor() {
|
||||
this.root = new Branch()
|
||||
}
|
||||
|
||||
FlaskaRouter.prototype.addRoute = function(route, handler) {
|
||||
if (route[0] !== '/')
|
||||
throw new Error(`route "${route}" must start with forward slash`);
|
||||
addRoute(route, orgMiddlewares, orgHandler) {
|
||||
if (route[0] !== '/')
|
||||
throw new Error(`route "${route}" must start with forward slash`)
|
||||
|
||||
let start = 1;
|
||||
let end = 1;
|
||||
let name = '';
|
||||
let param = '';
|
||||
let objectPrototype = {};
|
||||
let paramDefined = false;
|
||||
let isParam = false;
|
||||
let branch = this._root;
|
||||
let hashConflict = new Map();
|
||||
for (let i = 1; i <= route.length; i++) {
|
||||
if ((i === route.length || route[i] === '/') && end > start) {
|
||||
let child;
|
||||
let number = 0;
|
||||
name = route.substring(start, end);
|
||||
if (isParam) {
|
||||
param = name;
|
||||
name = __paramMapName;
|
||||
let middlewares = orgMiddlewares
|
||||
let handler = orgHandler
|
||||
if (!orgHandler) {
|
||||
handler = orgMiddlewares
|
||||
middlewares = []
|
||||
}
|
||||
if (middlewares && typeof(middlewares) === 'function') {
|
||||
middlewares = [middlewares]
|
||||
}
|
||||
if (typeof(handler) !== 'function') {
|
||||
throw new Error(`route "${route}" was missing a handler`)
|
||||
}
|
||||
|
||||
let start = 1
|
||||
let end = 1
|
||||
let name = ''
|
||||
let param = ''
|
||||
let isParam = false
|
||||
let isFullParam = false
|
||||
let branch = this.root
|
||||
|
||||
if (route.indexOf(':') < 0) {
|
||||
let name = route
|
||||
if (name.length > 1 && name[name.length - 1] === '/') {
|
||||
name = name.slice(0, -1)
|
||||
}
|
||||
if (branch._map.has(name)) {
|
||||
child = branch._map.get(name);
|
||||
let child = new Branch()
|
||||
branch.children.set(name, child)
|
||||
child.handler = handler
|
||||
child.middlewares = middlewares
|
||||
}
|
||||
|
||||
for (let i = 1; i <= route.length; i++) {
|
||||
if ((i === route.length || route[i] === '/') && end > start) {
|
||||
if (branch.fullparamName) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
||||
}
|
||||
let child
|
||||
name = route.substring(start, end)
|
||||
if (isFullParam) {
|
||||
param = name
|
||||
name = __fullParamMapName
|
||||
} else if (isParam) {
|
||||
param = name
|
||||
name = __paramMapName
|
||||
}
|
||||
if (branch.children.has(name)) {
|
||||
child = branch.children.get(name)
|
||||
}
|
||||
else if (isParam && !isFullParam && branch.children.has(__fullParamMapName)) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
||||
}
|
||||
else if (isFullParam && branch.children.has(__paramMapName)) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a partial param child`)
|
||||
}
|
||||
else {
|
||||
child = new Branch()
|
||||
branch.children.set(name, child)
|
||||
}
|
||||
branch = child
|
||||
end = i
|
||||
start = i
|
||||
if (isParam) {
|
||||
if (branch.paramName && branch.paramName !== param) {
|
||||
throw new Error(`route "${route}" conflicts with pre-existing param name of ${branch.paramName} instead of ${param}`)
|
||||
}
|
||||
if (isFullParam) {
|
||||
branch.fullparamName = param
|
||||
} else {
|
||||
branch.paramName = param
|
||||
}
|
||||
isParam = false
|
||||
}
|
||||
} else if (route[i] === '/' && end === start) {
|
||||
throw new Error(`route "${route}" has missing path name inbetween slashes`)
|
||||
}
|
||||
if (i === route.length) {
|
||||
branch.handler = handler
|
||||
branch.middlewares = middlewares
|
||||
continue
|
||||
}
|
||||
if (route[i] === ':') {
|
||||
if (isParam) {
|
||||
isFullParam = true
|
||||
}
|
||||
isParam = true
|
||||
end = start = i + 1
|
||||
}
|
||||
else if (route[i] === '/') {
|
||||
end = start = i + 1
|
||||
}
|
||||
else {
|
||||
child = new Branch();
|
||||
branch._map.set(name, child);
|
||||
end++
|
||||
}
|
||||
branch = child;
|
||||
end = i;
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
|
||||
match(orgUrl) {
|
||||
let url = orgUrl
|
||||
if (url.length > 1 && url[url.length - 1] === '/') {
|
||||
url = url.slice(0, -1)
|
||||
}
|
||||
let branch = this.root
|
||||
let start = 1
|
||||
let end = 1
|
||||
let output
|
||||
let name
|
||||
let char
|
||||
let params = {}
|
||||
if (output = branch.children.get(url)) {
|
||||
return {
|
||||
handler: output.handler,
|
||||
middlewares: output.middlewares,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
for (let i = 1; i <= url.length; i++) {
|
||||
char = url[i]
|
||||
if ((i === url.length || char === '/') && end > start) {
|
||||
name = url.slice(start, end)
|
||||
if (output = branch.children.get(name)) {
|
||||
branch = output
|
||||
}
|
||||
else if (output = branch.children.get(__paramMapName)) {
|
||||
branch = output
|
||||
params[branch.paramName] = name
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
i++
|
||||
end = start = i
|
||||
char = url[i]
|
||||
}
|
||||
if (i >= url.length) {
|
||||
return {
|
||||
handler: branch.handler,
|
||||
middlewares: branch.middlewares,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
if (char === '/') {
|
||||
end = start = i + 1
|
||||
} else {
|
||||
end++
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function FlaskaRouter() {
|
||||
this.root = new Branch()
|
||||
}
|
||||
|
||||
FlaskaRouter.prototype.addRoute = function(route, orgMiddlewares, orgHandler) {
|
||||
if (route[0] !== '/')
|
||||
throw new Error(`route "${route}" must start with forward slash`)
|
||||
|
||||
let middlewares = orgMiddlewares
|
||||
let handler = orgHandler
|
||||
if (!orgHandler) {
|
||||
handler = orgMiddlewares
|
||||
middlewares = []
|
||||
}
|
||||
if (middlewares && typeof(middlewares) === 'function') {
|
||||
middlewares = [middlewares]
|
||||
}
|
||||
if (typeof(handler) !== 'function') {
|
||||
throw new Error(`route "${route}" was missing a handler`)
|
||||
}
|
||||
|
||||
let start = 1
|
||||
let end = 1
|
||||
let name = ''
|
||||
let param = ''
|
||||
let isParam = false
|
||||
let isFullParam = false
|
||||
let branch = this.root
|
||||
|
||||
if (route.indexOf(':') < 0) {
|
||||
let name = route
|
||||
if (name.length > 1 && name[name.length - 1] === '/') {
|
||||
name = name.slice(0, -1)
|
||||
}
|
||||
let child = new Branch()
|
||||
branch.children.set(name, child)
|
||||
child.handler = handler
|
||||
child.middlewares = middlewares
|
||||
}
|
||||
|
||||
for (let i = 1; i <= route.length; i++) {
|
||||
if ((i === route.length || route[i] === '/') && end > start) {
|
||||
if (branch.fullparamName) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
||||
}
|
||||
let child
|
||||
name = route.substring(start, end)
|
||||
if (isFullParam) {
|
||||
param = name
|
||||
name = __fullParamMapName
|
||||
} else if (isParam) {
|
||||
param = name
|
||||
name = __paramMapName
|
||||
}
|
||||
if (branch.children.has(name)) {
|
||||
child = branch.children.get(name)
|
||||
}
|
||||
else if (isParam && !isFullParam && branch.children.has(__fullParamMapName)) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
||||
}
|
||||
else if (isFullParam && branch.children.has(__paramMapName)) {
|
||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a partial param child`)
|
||||
}
|
||||
else {
|
||||
child = new Branch()
|
||||
branch.children.set(name, child)
|
||||
}
|
||||
branch = child
|
||||
end = i
|
||||
start = i
|
||||
if (isParam) {
|
||||
branch._paramName = param;
|
||||
Object.defineProperty(objectPrototype, param, {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: '',
|
||||
});
|
||||
paramDefined = true;
|
||||
isParam = false;
|
||||
if (branch.paramName && branch.paramName !== param) {
|
||||
throw new Error(`route "${route}" conflicts with pre-existing param name of ${branch.paramName} instead of ${param}`)
|
||||
}
|
||||
if (isFullParam) {
|
||||
branch.fullparamName = param
|
||||
} else {
|
||||
branch.paramName = param
|
||||
}
|
||||
isParam = false
|
||||
}
|
||||
} else if (route[i] === '/' && end === start) {
|
||||
throw new Error(`route "${route}" has missing path name inbetween slashes`)
|
||||
}
|
||||
if (i === route.length) {
|
||||
branch._handler = handler;
|
||||
branch._paramPrototype = objectPrototype;
|
||||
continue;
|
||||
branch.handler = handler
|
||||
branch.middlewares = middlewares
|
||||
continue
|
||||
}
|
||||
if (route[i] === ':') {
|
||||
isParam = true;
|
||||
end = start = i + 1;
|
||||
if (isParam) {
|
||||
isFullParam = true
|
||||
}
|
||||
isParam = true
|
||||
end = start = i + 1
|
||||
}
|
||||
else if (route[i] === '/') {
|
||||
end = start = i + 1;
|
||||
end = start = i + 1
|
||||
}
|
||||
else {
|
||||
end++;
|
||||
end++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlaskaRouter.prototype.match = function(url) {
|
||||
let branch = this._root;
|
||||
let start = 1;
|
||||
let end = 1;
|
||||
let output;
|
||||
let map;
|
||||
let name;
|
||||
let char;
|
||||
let paramMap = new Map();
|
||||
FlaskaRouter.prototype.match = function(orgUrl) {
|
||||
let url = orgUrl
|
||||
if (url.length > 1 && url[url.length - 1] === '/') {
|
||||
url = url.slice(0, -1)
|
||||
}
|
||||
let branch = this.root
|
||||
let start = 1
|
||||
let end = 1
|
||||
let output
|
||||
let name
|
||||
let char
|
||||
let params = {}
|
||||
if (output = branch.children.get(url)) {
|
||||
return {
|
||||
handler: output.handler,
|
||||
middlewares: output.middlewares,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
for (let i = 1; i <= url.length; i++) {
|
||||
char = url[i];
|
||||
char = url[i]
|
||||
if ((i === url.length || char === '/') && end > start) {
|
||||
name = url.slice(start, end);
|
||||
map = branch._map;
|
||||
if (output = map.get(name)) {
|
||||
branch = output;
|
||||
name = url.slice(start, end)
|
||||
if (output = branch.children.get(name)) {
|
||||
branch = output
|
||||
}
|
||||
else if (output = map.get(__paramMapName)) {
|
||||
branch = output;
|
||||
paramMap.set(branch._paramName, name);
|
||||
else if (output = branch.children.get(__paramMapName)) {
|
||||
branch = output
|
||||
params[branch.paramName] = name
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
i++;
|
||||
end = start = i;
|
||||
char = url[i];
|
||||
i++
|
||||
end = start = i
|
||||
char = url[i]
|
||||
}
|
||||
if (i >= url.length) {
|
||||
return {
|
||||
handler: branch._handler,
|
||||
params: paramMap,
|
||||
};
|
||||
handler: branch.handler,
|
||||
middlewares: branch.middlewares,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
if (char === '/') {
|
||||
end = start = i + 1;
|
||||
}
|
||||
else {
|
||||
end++;
|
||||
end = start = i + 1
|
||||
} else {
|
||||
end++
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
export {
|
||||
FlaskaRouter,
|
||||
export function Flaska() {
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,6 @@
|
|||
},
|
||||
"homepage": "https://github.com/nfp-projects/bottle-node#readme",
|
||||
"devDependencies": {
|
||||
"eltro": "^0.9.0"
|
||||
"eltro": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
|
96
test/client.mjs
Normal file
96
test/client.mjs
Normal file
|
@ -0,0 +1,96 @@
|
|||
import http from 'http'
|
||||
import { URL } from 'url'
|
||||
|
||||
// taken from isobject npm library
|
||||
function isObject(val) {
|
||||
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
||||
}
|
||||
|
||||
function defaults(options, def) {
|
||||
let out = { }
|
||||
|
||||
if (options) {
|
||||
Object.keys(options || {}).forEach(key => {
|
||||
out[key] = options[key]
|
||||
|
||||
if (Array.isArray(out[key])) {
|
||||
out[key] = out[key].map(item => {
|
||||
if (isObject(item)) return defaults(item)
|
||||
return item
|
||||
})
|
||||
} else if (out[key] && typeof out[key] === 'object') {
|
||||
out[key] = defaults(options[key], def && def[key])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (def) {
|
||||
Object.keys(def).forEach(function(key) {
|
||||
if (typeof out[key] === 'undefined') {
|
||||
out[key] = def[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
export default function Client(port, opts) {
|
||||
this.options = defaults(opts, {})
|
||||
this.prefix = `http://localhost:${port}`
|
||||
}
|
||||
|
||||
Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||
if (path.slice(0, 4) !== 'http') {
|
||||
path = this.prefix + path
|
||||
}
|
||||
let urlObj = new URL(path)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const opts = defaults(defaults(options, {
|
||||
method: method,
|
||||
timeout: 500,
|
||||
protocol: urlObj.protocol,
|
||||
username: urlObj.username,
|
||||
password: urlObj.password,
|
||||
host: urlObj.hostname,
|
||||
port: Number(urlObj.port),
|
||||
path: urlObj.pathname + urlObj.search,
|
||||
}))
|
||||
|
||||
const req = http.request(opts)
|
||||
if (body) {
|
||||
req.write(body)
|
||||
}
|
||||
|
||||
req.on('error', reject)
|
||||
req.on('timeout', function() { reject(new Error(`Request ${method} ${path} timed out`)) })
|
||||
req.on('response', res => {
|
||||
res.setEncoding('utf8')
|
||||
let output = ''
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
output += chunk.toString()
|
||||
})
|
||||
|
||||
res.on('end', function () {
|
||||
try {
|
||||
output = JSON.parse(output)
|
||||
} catch (e) {
|
||||
return reject(new Error(`${e.message} while decoding: ${output}`))
|
||||
}
|
||||
if (output.status) {
|
||||
let err = new Error(`Request failed [${output.status}]: ${output.message}`)
|
||||
err.body = output
|
||||
return reject(err)
|
||||
}
|
||||
resolve(output)
|
||||
})
|
||||
})
|
||||
req.end()
|
||||
})
|
||||
}
|
||||
|
||||
Client.prototype.get = function(path = '/') {
|
||||
return this.customRequest('GET', path, null)
|
||||
}
|
|
@ -1,48 +1,5 @@
|
|||
import { Eltro as t, assert } from 'eltro'
|
||||
import { FlaskaRouter } from '../flaska.mjs'
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
import { Flaska } from '../flaska.mjs'
|
||||
|
||||
t.describe('FlaskaRouter', function() {
|
||||
t.describe('#match()', function() {
|
||||
t.test('should match basic paths', function() {
|
||||
let assertMatched = false
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test', function() { assertMatched = true })
|
||||
let result = router.match('/test')
|
||||
assert.ok(result.handler)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
})
|
||||
|
||||
t.test('should match variable paths', function() {
|
||||
const assertParameter = 'bla'
|
||||
let assertMatched = false
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { assertMatched = true })
|
||||
let result = router.match('/test/' + assertParameter)
|
||||
assert.ok(result.handler)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.get('id'), assertParameter)
|
||||
})
|
||||
|
||||
t.test('should match paths properly', function() {
|
||||
let assertMatched = true
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { assertMatched = false })
|
||||
router.addRoute('/test/:id/test1', function() { })
|
||||
let result = router.match('/test/asdf/test1')
|
||||
assert.ok(result.handler)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.get('id'), 'asdf')
|
||||
})
|
||||
|
||||
t.test('should return null when no match is found', function() {
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { })
|
||||
router.addRoute('/test/:id/test1', function() { })
|
||||
let result = router.match('/test/asdf/test2')
|
||||
assert.notOk(result)
|
||||
})
|
||||
})
|
||||
t.describe('Flaska', function() {
|
||||
})
|
||||
|
|
233
test/router.test.mjs
Normal file
233
test/router.test.mjs
Normal file
|
@ -0,0 +1,233 @@
|
|||
import { Eltro as t, assert } from 'eltro'
|
||||
import { FlaskaRouter } from '../flaska.mjs'
|
||||
|
||||
t.describe('#addRoute()', function() {
|
||||
t.test('fail if trying to add non-root path', function() {
|
||||
let router = new FlaskaRouter()
|
||||
assert.throws(function() { router.addRoute('', function() { }) }, /forward slash/)
|
||||
assert.throws(function() { router.addRoute('test') }, /forward slash/)
|
||||
assert.throws(function() { router.addRoute(':test') }, /forward slash/)
|
||||
assert.throws(function() { router.addRoute('test/test2') }, /forward slash/)
|
||||
})
|
||||
|
||||
t.test('fail if missing handler', function() {
|
||||
let router = new FlaskaRouter()
|
||||
assert.throws(function() { router.addRoute('/') }, /handler/)
|
||||
assert.throws(function() { router.addRoute('/test') }, /handler/)
|
||||
assert.throws(function() { router.addRoute('/:test') }, /handler/)
|
||||
})
|
||||
|
||||
t.test('fail if missing handler but has middleware', function() {
|
||||
let router = new FlaskaRouter()
|
||||
assert.throws(function() { router.addRoute('/', [function() {}]) }, /handler/)
|
||||
assert.throws(function() { router.addRoute('/test', [function() {}]) }, /handler/)
|
||||
assert.throws(function() { router.addRoute('/:test', [function() {}]) }, /handler/)
|
||||
})
|
||||
|
||||
t.test('fail if adding non-name route', function() {
|
||||
let router = new FlaskaRouter()
|
||||
assert.throws(function() { router.addRoute('//', function() {}) }, /path/)
|
||||
assert.throws(function() { router.addRoute('/test//test2', function() {}) }, /path/)
|
||||
assert.throws(function() { router.addRoute('/test/:test//bla', function() {}) }, /path/)
|
||||
})
|
||||
|
||||
t.test('fail if adding non-name param route', function() {
|
||||
let router = new FlaskaRouter()
|
||||
assert.throws(function() { router.addRoute('/:/', function() {}) }, /path/)
|
||||
assert.throws(function() { router.addRoute('/test/:/test2', function() {}) }, /path/)
|
||||
assert.throws(function() { router.addRoute('/test/:test/:/bla', function() {}) }, /path/)
|
||||
})
|
||||
|
||||
t.test('fail if adding different named param to existing route', function() {
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/:test', function() {})
|
||||
router.addRoute('/:test/asdf/:bla', function() {})
|
||||
router.addRoute('/:test/bla', function() {})
|
||||
router.addRoute('/bla/bla', function() {})
|
||||
router.addRoute('/bla/bla/bla', function() {})
|
||||
assert.throws(function() { router.addRoute('/:asdf/', function() {}) }, /param/)
|
||||
assert.throws(function() { router.addRoute('/:test/asdf/:foobar', function() {}) }, /param/)
|
||||
})
|
||||
|
||||
t.test('fail if adding multiple fullparam', function() {
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/:test', function() {})
|
||||
router.addRoute('/:test/asdf/::bla', function() {})
|
||||
router.addRoute('/:test/bla', function() {})
|
||||
router.addRoute('/:test/::bla', function() {})
|
||||
router.addRoute('/bla/bla/bla', function() {})
|
||||
assert.throws(function() { router.addRoute('/:test/asdf/::bla/fail', function() {}) }, /full.+param/)
|
||||
assert.throws(function() { router.addRoute('/:test/::bla/test', function() {}) }, /full.+param/)
|
||||
assert.throws(function() { router.addRoute('/:test/:bla', function() {}) }, /full.+param/)
|
||||
assert.throws(function() { router.addRoute('/::test', function() {}) }, /partial.+param/)
|
||||
})
|
||||
|
||||
t.test('add route correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a/b/c', assertHandler)
|
||||
let result = router.match('/a/b/c')
|
||||
assert.strictEqual(result.handler, assertHandler)
|
||||
})
|
||||
|
||||
t.test('add param route correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a/:b/c', assertHandler)
|
||||
assert.ok(router.root.children.get('a'))
|
||||
assert.ok(router.root.children.get('a').children.get('__param'))
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__param').paramName, 'b')
|
||||
assert.ok(router.root.children.get('a').children.get('__param').children.get('c'))
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__param').children.get('c').handler, assertHandler)
|
||||
})
|
||||
|
||||
t.test('add full param route correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a/::b', assertHandler)
|
||||
assert.ok(router.root.children.get('a'))
|
||||
assert.ok(router.root.children.get('a').children.get('__fullparam'))
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__fullparam').fullparamName, 'b')
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__fullparam').handler, assertHandler)
|
||||
})
|
||||
|
||||
t.test('add param route correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a/:b/c', assertHandler)
|
||||
assert.ok(router.root.children.get('a'))
|
||||
assert.ok(router.root.children.get('a').children.get('__param'))
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__param').paramName, 'b')
|
||||
assert.ok(router.root.children.get('a').children.get('__param').children.get('c'))
|
||||
assert.strictEqual(router.root.children.get('a').children.get('__param').children.get('c').handler, assertHandler)
|
||||
})
|
||||
|
||||
t.test('support single middlewares correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let assertMiddleware = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a', assertMiddleware, assertHandler)
|
||||
assert.ok(router.root.children.get('a'))
|
||||
assert.strictEqual(router.root.children.get('a').handler, assertHandler)
|
||||
assert.strictEqual(router.root.children.get('a').middlewares.length, 1)
|
||||
assert.strictEqual(router.root.children.get('a').middlewares[0], assertMiddleware)
|
||||
})
|
||||
|
||||
t.test('support multi middlewares correctly', function() {
|
||||
let assertHandler = function() { return 1 }
|
||||
let assertMiddleware = function() { return 1 }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/a', [assertMiddleware], assertHandler)
|
||||
router.addRoute('/b', [assertMiddleware, assertMiddleware], assertHandler)
|
||||
assert.ok(router.root.children.get('a'))
|
||||
assert.strictEqual(router.root.children.get('a').handler, assertHandler)
|
||||
assert.strictEqual(router.root.children.get('a').middlewares.length, 1)
|
||||
assert.strictEqual(router.root.children.get('a').middlewares[0], assertMiddleware)
|
||||
assert.ok(router.root.children.get('b'))
|
||||
assert.strictEqual(router.root.children.get('b').handler, assertHandler)
|
||||
assert.strictEqual(router.root.children.get('b').middlewares.length, 2)
|
||||
assert.strictEqual(router.root.children.get('b').middlewares[0], assertMiddleware)
|
||||
assert.strictEqual(router.root.children.get('b').middlewares[1], assertMiddleware)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#match()', function() {
|
||||
t.test('match basic paths', function() {
|
||||
let assertMatched = false
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test', function() { assertMatched = true })
|
||||
let result = router.match('/test')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
|
||||
// Test with extra slash at the end
|
||||
assertMatched = false
|
||||
result = router.match('/test/')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
})
|
||||
|
||||
|
||||
t.test('return middlewares', function() {
|
||||
let assertMatched = false
|
||||
let assertMiddleware = function() { assertMatched = true }
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test', assertMiddleware, function() { })
|
||||
let result = router.match('/test')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 1)
|
||||
result.middlewares[0]()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
|
||||
// Test with extra slash at the end
|
||||
assertMatched = false
|
||||
result = router.match('/test/')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 1)
|
||||
result.middlewares[0]()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
})
|
||||
|
||||
t.test('match variable paths', function() {
|
||||
const assertParameter = 'bla'
|
||||
let assertMatched = false
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { assertMatched = true })
|
||||
let result = router.match('/test/' + assertParameter)
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.id, assertParameter)
|
||||
|
||||
// Test with extra slash at the end
|
||||
assertMatched = false
|
||||
result = router.match('/test/' + assertParameter + '/')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.id, assertParameter)
|
||||
})
|
||||
|
||||
t.test('match paths properly', function() {
|
||||
let assertMatched = true
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { assertMatched = false })
|
||||
router.addRoute('/test/:id/test1', function() { })
|
||||
let result = router.match('/test/asdf/test1')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.id, 'asdf')
|
||||
|
||||
// Test with extra slash at the end
|
||||
result = router.match('/test/asdf/test1/')
|
||||
assert.ok(result.handler)
|
||||
assert.ok(result.middlewares)
|
||||
assert.strictEqual(result.middlewares.length, 0)
|
||||
result.handler()
|
||||
assert.strictEqual(assertMatched, true)
|
||||
assert.strictEqual(result.params.id, 'asdf')
|
||||
})
|
||||
|
||||
t.test('return null when no match is found', function() {
|
||||
let router = new FlaskaRouter()
|
||||
router.addRoute('/test/:id', function() { })
|
||||
router.addRoute('/test/:id/test1', function() { })
|
||||
let result = router.match('/test/asdf/test2')
|
||||
assert.notOk(result)
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue