apinode
Version:
An API server that can greatly reduce the work needed to implment API services. It can also cooperate with other API node to make it a mesh of services.
217 lines (174 loc) • 5.13 kB
JavaScript
var async = require('async'),
soar = require('sql-soar'),
errUtil = require('../../../util/errUtil.js');
var timeSH = 10 * 60 * 1000; // 10 min in milliseconds
var qLogExpr = soar.sql('coimLog.apiLog')
.column(['userID', 'token', 'c_ip'])
.filter(
soar.chainFilters('AND', [
{name: 'from', field: 'accTime', op:'>='},
{name: 'to', field: 'accTime', op:'<='}
])
)
.extra('GROUP BY token'),
qEachExpr = soar.sql('coimLog.apiLog')
.filter(
soar.chainFilters('AND', [
{name: 'token', op: '='},
{name: 'from', field: 'accTime', op:'>='},
{name: 'to', field: 'accTime', op:'<='}
])
)
.extra('ORDER BY accTime');
var lLogCmd = {
op: 'list',
expr: qLogExpr
},
lItemCmd = {
op: 'list',
expr: qEachExpr
};
exports.checkArguments = function(rt, cb) {
var inData = rt.inData;
//if (isNaN(inData._ps))
// delete inData._ps;
if (!inData.from)
inData.from = toyyyymmdd( new Date() ) + 'T00:00:00';
if (!inData.to)
inData.to = (new Date()).toISOString();
cb( null, true );
}
exports.run = function(rt, cb) {
var inData = rt.inData,
ptype = inData.ptype || 'times';
soar.execute(lLogCmd, rt.inData, function(err, result) {
if (err)
return cb( errUtil.err(errUtil.DB_ERROR) );
var sessionList = [];
async.each(result, function(item, cb) {
if (item.token) {
var query = {
token: item.token,
from: inData.from,
to: inData.to
};
soar.execute(lItemCmd, query, function(err, res) {
if (err)
return cb(err);
var si = toSessions( res );
si.userID = item.userID;
si.device = item.c_ip;
sessionList.push( si );
//console.log('[%d] has %d API calls, %d sessions, total session times', item.userID, si.calls, si.sessionList.length, si.sessionTimes);
cb();
});
}
else
cb();
},
function(err) {
if (err)
return cb( errUtil.err(errUtil.DB_ERROR) );
//console.log('total record counts: ' + sessionList.length);
var rtnValue,
columns = inData.columns || 40;
switch (ptype) {
case 'calls':
var scale = inData.scale || 10, // default 10 calls
rtnValue = groupByCalls(scale, columns, sessionList);
break;
case 'hot':
var size = inData.size || 20;
rtnValue = hotUsers(size, sessionList);
break;
default:
var timeScale = inData.scale || 2000, // default 2 seconds
rtnValue = groupBySessionTimes(timeScale, columns, sessionList);
break;
}
//console.log(JSON.stringify(rtnValue, null, 4));
cb(null, {code: 0, message: 'Ok', value: rtnValue});
});
});
}
function hotUsers(size, list) {
// first, order by session duration (from long to short)
list.sort(function(a, b) {
return b.sessionTimes - a.sessionTimes;
});
var len = Math.min( list.length, size );
return {list: list.slice(0, len)};
}
function groupByCalls(scale, columns, list) {
var callArray = Array.apply(null, Array(columns)).map(function() {return 0});
if (list.length) {
// first, order by API calls
list.sort(function(a, b) {
return a.calls - b.calls;
});
var minCalls = list[0].calls;
for (var i = 0, len = list.length; i < len; i++) {
var idx = Math.floor( (list[i].calls - minCalls) / scale );
callArray[idx >= columns ? columns-1 : idx] += 1;
}
//console.log( JSON.stringify(timeArray) );
}
return {
scale: scale,
data: callArray
};
}
function groupBySessionTimes(timeScale, columns, list) {
// first, order by session duration
list.sort(function(a, b) {
return a.sessionTimes - b.sessionTimes;
});
var minTime = list[0].sessionTimes,
timeArray = Array.apply(null, Array(columns)).map(function() {return 0});
for (var i = 0, len = list.length; i < len; i++) {
var idx = Math.floor( (list[i].sessionTimes - minTime) / timeScale );
timeArray[idx >= columns ? columns-1 : idx] += 1;
}
//console.log( JSON.stringify(timeArray) );
return {
scale: timeScale,
data: timeArray
};
}
/**
* API log list of the same person
*/
function toSessions(list) {
var startTime = list[0].accTime,
lastTime = startTime,
totalTimes = 0,
sessionList = [];
for (var i=1, len=list.length; i < len; i++) {
var item = list[i],
accTime = item.accTime.getTime();
var timeSpan = accTime - lastTime;
if (timeSpan > timeSH) {
sessionList.push( lastTime - startTime );
startTime = accTime;
}
lastTime = accTime;
}
sessionList.push( lastTime - startTime );
for (var i = 0, len = sessionList.length; i < len; i++)
totalTimes += sessionList[i];
//console.log('API calls: %d, max time span: %s', list.length, maxTimeSpan);
return {
calls: list.length,
sessionList: sessionList,
sessionTimes: totalTimes
};
}
function toyyyymmdd(d) {
var y = d.getFullYear().toString(),
m = padZero(d.getMonth() + 1),
day = padZero(d.getDate());
return y + '-' + m + '-' + day;
}
function padZero(n) {
return n < 10 ? '0' + n : n.toString();
}