furkot-directions
Advanced tools
Comparing version 0.0.11 to 0.0.12
0.0.12 / 2017-01-13 | ||
=================== | ||
* split query after determining that the service is supposed to handle it | ||
* enforce one limiter per service regardless the number of instances of furkot directions | ||
* refactor directions service to streamline aborting on timeout | ||
* debug logging of 'query over limit' condition when using google or google web service | ||
* debug@2.6.0 | ||
* only invoke enabled services | ||
* code42day-vis-why 1.1.4 -> 1.1.6 | ||
0.0.11 / 2017-01-10 | ||
@@ -3,0 +14,0 @@ =================== |
@@ -45,6 +45,7 @@ var _defaults = require('lodash.defaults'); | ||
function disable() { | ||
return false; | ||
} | ||
// default timeout to complete operation | ||
var defaultTimeout = 20 * 1000; | ||
var id = 0; | ||
function furkotDirections(options) { | ||
@@ -58,6 +59,17 @@ | ||
function directions(query, fn) { | ||
var result; | ||
var result, timeoutId, queryId; | ||
if (!query) { | ||
return fn(); | ||
} | ||
id += 1; | ||
queryId = id; | ||
timeoutId = setTimeout(function () { | ||
timeoutId = undefined; | ||
// cancel outstanding requests | ||
options.services.forEach(function (service) { | ||
service.abort(queryId); | ||
}); | ||
}, options.timeout); | ||
result = new Array(query.length); | ||
@@ -67,3 +79,7 @@ if (!query.length) { | ||
} | ||
strategy(options.services, query, result, function (err, query, result) { | ||
strategy(options.services, queryId, query, result, function (err, queryId, query, result) { | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
timeoutId = undefined; | ||
} | ||
if (err) { | ||
@@ -77,10 +93,16 @@ return fn(); | ||
options = options || {}; | ||
options = _defaults(options, { | ||
timeout: defaultTimeout | ||
}); | ||
if (!options.services) { | ||
options.services = services.map(function (service) { | ||
return service.service(_defaults({ | ||
name: service.name, | ||
enable: options[(service.name + '_enable')] || disable, | ||
skip: service.skip | ||
}, options)); | ||
}); | ||
options.services = services.reduce(function (result, service) { | ||
if (options[(service.name + '_enable')]) { | ||
result.push(service.service(_defaults({ | ||
name: service.name, | ||
enable: options[(service.name + '_enable')], | ||
skip: service.skip | ||
}, options))); | ||
} | ||
return result; | ||
}, []); | ||
} | ||
@@ -87,0 +109,0 @@ |
@@ -6,2 +6,3 @@ var _defaults = require('lodash.defaults'); | ||
var util = require('../util'); | ||
var debug = require('debug')('furkot:directions:google'); | ||
@@ -89,6 +90,2 @@ module.exports = init; | ||
function abort() { | ||
// do nothing - request cannot be aborted | ||
} | ||
function getStatus(response) { | ||
@@ -101,2 +98,3 @@ var st; | ||
if (st === gmStatus.OVER_QUERY_LIMIT || st === gmStatus.UNKNOWN_ERROR) { | ||
debug('error', st); | ||
return status.error; | ||
@@ -170,3 +168,2 @@ } | ||
request: request, | ||
abort: abort, | ||
status: getStatus, | ||
@@ -173,0 +170,0 @@ prepareRequest: prepareRequest, |
@@ -6,2 +6,3 @@ var _defaults = require('lodash.defaults'); | ||
var util = require('../util'); | ||
var debug = require('debug')('furkot:directions:googlews'); | ||
@@ -59,5 +60,7 @@ module.exports = init; | ||
// stop asking | ||
debug('failure', st); | ||
return status.failure; | ||
} | ||
if (st === gmStatus.OVER_QUERY_LIMIT || st === gmStatus.UNKNOWN_ERROR) { | ||
debug('error', st); | ||
return status.error; | ||
@@ -64,0 +67,0 @@ } |
@@ -7,8 +7,10 @@ var _defaults = require('lodash.defaults'); | ||
var util = require('./util'); | ||
var debug = require('debug')('furkot:directions:service'); | ||
module.exports = init; | ||
// default timeout to complete operation | ||
var defaultTimeout = 20 * 1000; | ||
var limiters = {}; | ||
var ERROR = 'input error'; | ||
function eachOfSeries(items, task, fn) { | ||
@@ -29,6 +31,2 @@ var tasks = items.map(function(item, i) { | ||
function abort(req) { | ||
req.abort(); | ||
} | ||
function initUrl(url) { | ||
@@ -44,13 +42,44 @@ if (typeof url === 'function') { | ||
function init(options) { | ||
var limiter, holdRequests, simplify; | ||
var limiter, holdRequests, simplify, outstanding = {}; | ||
function directions(query, result, fn) { | ||
var legs, timeoutId, laterTimeoutId, reqInProgress, startTime = Date.now(); | ||
function abort(queryId) { | ||
debug('abort', queryId); | ||
if (!outstanding[queryId]) { | ||
return; | ||
} | ||
if (outstanding[queryId].subquery) { | ||
return options.abort(outstanding[queryId].subquery); | ||
} | ||
if (outstanding[queryId].query) { | ||
return options.abort(outstanding[queryId].query); | ||
} | ||
if (outstanding[queryId]) { | ||
// cancel later request if scheduled | ||
if (outstanding[queryId].laterTimeoutId) { | ||
clearTimeout(outstanding[queryId].laterTimeoutId); | ||
} | ||
// cancel request in progress | ||
if (outstanding[queryId].reqInProgress) { | ||
outstanding[queryId].reqInProgress.abort(); | ||
} | ||
outstanding[queryId].callback('aborted'); | ||
} | ||
} | ||
function directions(queryId, queryArray, result, fn) { | ||
function spliceResults(idx, segments, segResult) { | ||
Array.prototype.splice.apply(queryArray, [idx + queryArray.delta, 1].concat(segments)); | ||
Array.prototype.splice.apply(result, [idx + queryArray.delta, 1].concat(segResult)); | ||
queryArray.delta += segments.length - 1; | ||
} | ||
function queryDirections(query, idx, callback) { | ||
var req; | ||
var req, segments; | ||
function requestLater() { | ||
laterTimeoutId = setTimeout(function () { | ||
laterTimeoutId = undefined; | ||
outstanding[queryId].laterTimeoutId = setTimeout(function () { | ||
if (outstanding[queryId]) { | ||
delete outstanding[queryId].laterTimeoutId; | ||
} | ||
queryDirections(query, idx, callback); | ||
@@ -60,6 +89,9 @@ }, options.penaltyTimeout); | ||
if (!timeoutId) { | ||
return callback(); | ||
if (!outstanding[queryId]) { | ||
// query has been aborted | ||
return; | ||
} | ||
if (options.skip(options, query, legs.result[idx])) { | ||
outstanding[queryId].callback = callback; | ||
if (options.skip(options, query, result[idx + queryArray.delta])) { | ||
return callback(); | ||
@@ -72,2 +104,18 @@ } | ||
segments = util.splitPoints(query, queryArray.maxPoints || options.maxPoints); | ||
if (!segments) { | ||
return callback(ERROR); | ||
} | ||
if (segments !== query) { | ||
outstanding[queryId].query = queryId + 'q'; | ||
return directions(outstanding[queryId].query, segments, | ||
new Array(segments.length), function (err, stop, id, query, result) { | ||
if (!err) { | ||
spliceResults(idx, query, result); | ||
} | ||
callback(err); | ||
}); | ||
} | ||
query.path = query.path || pathType.none; | ||
@@ -80,9 +128,13 @@ req = options.prepareRequest(query); | ||
limiter.trigger(function () { | ||
reqInProgress = options.request(options.url(query), req, function (err, response) { | ||
if (!outstanding[queryId]) { | ||
// query has been aborted | ||
return; | ||
} | ||
outstanding[queryId].reqInProgress = options.request(options.url(query), req, function (err, response) { | ||
var st, res; | ||
reqInProgress = undefined; | ||
if (!timeoutId) { | ||
//abort has not been effective | ||
return callback(); | ||
if (!outstanding[queryId]) { | ||
// query has been aborted | ||
return; | ||
} | ||
delete outstanding[queryId].reqInProgress; | ||
if (err) { | ||
@@ -110,6 +162,8 @@ return callback(err); | ||
query.maxPoints = 2; | ||
return directions(query, new Array(1), function (err, stop, query, result) { | ||
outstanding[queryId].subquery = queryId + 's'; | ||
return directions(outstanding[queryId].subquery, query, | ||
new Array(1), function (err, stop, id, query, result) { | ||
if (!err) { | ||
Array.prototype.splice.apply(legs.query, [idx, 1].concat(query)); | ||
Array.prototype.splice.apply(legs.result, [idx, 1].concat(result)); | ||
spliceResults(idx, query, result); | ||
} | ||
@@ -128,3 +182,3 @@ callback(err); | ||
} | ||
legs.result[idx] = res; | ||
result[idx + queryArray.delta] = res; | ||
} | ||
@@ -136,32 +190,13 @@ callback(); | ||
legs = util.splitPoints(query, result, options.maxPoints); | ||
if (legs.error || !legs.query.length) { | ||
return fn(undefined, true); | ||
} | ||
outstanding[queryId] = {}; | ||
queryArray.delta = 0; | ||
query.timeout = query.timeout || options.timeout; | ||
timeoutId = setTimeout(function () { | ||
// cancel later request if schedule | ||
if (laterTimeoutId) { | ||
clearTimeout(laterTimeoutId); | ||
laterTimeoutId = undefined; | ||
eachOfSeries(queryArray, queryDirections, function (err) { | ||
if (outstanding[queryId]) { | ||
delete outstanding[queryId]; | ||
if (err === ERROR) { | ||
return fn(undefined, true); | ||
} | ||
fn(err, false, queryId, queryArray, result); | ||
} | ||
// cancel imminent request if scheduled | ||
limiter.cancel(); | ||
// cancel request in progress | ||
if (reqInProgress) { | ||
options.abort(reqInProgress); | ||
reqInProgress = undefined; | ||
} | ||
timeoutId = undefined; | ||
fn(undefined, true, query, result); | ||
}, query.timeout); | ||
eachOfSeries(legs.query, queryDirections, function (err) { | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
legs.query.timeout = query.timeout - Date.now() + startTime; | ||
timeoutId = undefined; | ||
fn(err, legs.query.timeout <= 0, legs.query, legs.result); | ||
} | ||
}); | ||
@@ -174,9 +209,10 @@ } | ||
request: request, | ||
abort: abort, | ||
timeout: defaultTimeout | ||
abort: abort | ||
}); | ||
options.url = initUrl(options.url); | ||
limiter = require('./limiter')(options.interval, options.penaltyInterval); | ||
limiters[options.name] = limiters[options.name] || require('./limiter')(options.interval, options.penaltyInterval); | ||
limiter = limiters[options.name]; | ||
simplify = require('./simplify')(options); | ||
directions.abort = options.abort; | ||
return directions; | ||
} |
@@ -65,34 +65,20 @@ var _defaults = require('lodash.defaults'); | ||
function doSplitPoints(result, seg, idx) { | ||
var i, maxPoints = result.maxPoints; | ||
if (!(seg.points && seg.points.length > 1)) { | ||
result.error = true; | ||
return result; | ||
function splitPoints(query, maxPoints) { | ||
var i, segments; | ||
if (!(query.points && query.points.length > 1)) { | ||
return ; | ||
} | ||
if (result.superResult[idx]) { | ||
result.query.push(seg); | ||
result.result.push(result.superResult[idx]); | ||
if (query.points.length <= maxPoints) { | ||
return query; | ||
} | ||
else if (seg.points.length <= maxPoints) { | ||
result.query.push(seg); | ||
result.result.push(undefined); | ||
segments = []; | ||
for (i = 0; i < query.points.length - 1; i += maxPoints - 1) { | ||
segments.push(_defaults({ | ||
points: query.points.slice(i, i + maxPoints) | ||
}, query)); | ||
} | ||
else { | ||
for (i = 0; i < seg.points.length - 1; i += maxPoints - 1) { | ||
result.query.push(_defaults({ | ||
points: seg.points.slice(i, i + maxPoints) | ||
}, seg)); | ||
} | ||
Array.prototype.push.apply(result.result, new Array(Math.ceil((seg.points.length - 1) / (maxPoints - 1)))); | ||
if (last(segments).points.length === 1) { | ||
last(segments).points.unshift(segments[segments.length - 2].points.pop()); | ||
} | ||
return result; | ||
return segments; | ||
} | ||
function splitPoints(query, result, maxPoints) { | ||
return query.reduce(doSplitPoints, { | ||
maxPoints: query.maxPoints || maxPoints, | ||
query: [], | ||
result: [], | ||
superResult: result | ||
}); | ||
} |
{ | ||
"name": "furkot-directions", | ||
"version": "0.0.11", | ||
"version": "0.0.12", | ||
"description": "Directions service for Furkot", | ||
@@ -19,3 +19,4 @@ "author": { | ||
"code42day-google-polyline": "~2", | ||
"code42day-vis-why": "^1.1.4", | ||
"code42day-vis-why": "^1.1.6", | ||
"debug": "^2.6.0", | ||
"fetchagent": "^1.0.0", | ||
@@ -22,0 +23,0 @@ "geodesy": "^1.1.1", |
Sorry, the diff of this file is not supported yet
86016
1356
9
+ Addeddebug@^2.6.0
+ Addeddebug@2.6.9(transitive)
+ Addedms@2.0.0(transitive)
Updatedcode42day-vis-why@^1.1.6