lib/routes/location/v1/location.js
/*
UpdateLocation POST
By doing a HTTP Post on the /UpdateLocation method of this server you should
be able to trigger this method here.
curl -H "Content-Type: application/json" -X POST http://localhost:3000/UpdateLocation -d '{"JSON": "HERE"}'
curl -H "Content-Type: application/json" -X POST http://localhost:3000/UpdateLocation -d '{"MiataruConfig":{"EnableLocationHistory":"Fa":"15"},"MiataruLocation":[{"Device":"7b8e6e0ee5296db345162dc2ef652c1350761823","Timestamp":"1376735651302","Longitude":"10.837502","Latitude":"49.828925","HorizontalAccuracy":"50.00"}]}'
To store information Redis is used. The naming conventions are as follows:
Last Known Location (expiring key-value): miad:$deviceid:last
Location History (non expiring list): miad:$deviceid:hist
*/
var seq = require('seq');
var configuration = require('../../../configuration');
var db = require('../../../db');
var kb = require('../../../utils/keyBuilder');
var models = require('../../../models');
var errors = require('../../../errors');
var KEY_HISTORY = 'hist';
var KEY_LAST = 'last';
var KEY_VISIT = 'visit';
/**
* GetLocationHistory
* Retrieves the complete History
* @param req
* @param res
* @param next
*/
function getLocationHistory(req, res, next) {
var locationRequest = req.MIATARU.request;
var requestConfig = req.MIATARU.config;
var key = kb.build(locationRequest.device(), KEY_HISTORY);
seq()
.par('listLength', function() {
db.llen(key, this);
})
.par('list', function() {
db.lrange(key, 0, locationRequest.amount()-1, this);
})
.seq(function() {
// only if this device is existing we update the visitor list...
if (this.vars.listLength != 0)
{
// store the visitor for this device (if it's not empty)...
var visitKey = kb.build(locationRequest.device(), KEY_VISIT);
var miataruVisitorObject = requestConfig.requestMiataruVisitorObject();
// only if the object is not null (= we do not want to store the visitor history of unknown devices)
if (miataruVisitorObject != null)
{
var visitValue = JSON.stringify(miataruVisitorObject);
seq()
// we do want to store visitor history, so check if we got, so lpush and trim
.seq(function() {
db.lpush(visitKey, visitValue, this);
})
// take care that the visitor history does not grow beyond the set range of maximumNumberOfLocationVistors
.seq(function() {
db.ltrim(visitKey, 0, configuration.maximumNumberOfLocationVistors-1, this);
});
}
}
res.send((new models.ResponseLocationHistory(
this.vars.listLength,
configuration.maximumNumberOfHistoryItems,
this.vars.list.map(function(value) {
return JSON.parse(value)
})
)).data());
})
.catch(function(error) {
next(new errors.InternalServerError(error));
});
}
/**
* UpdateLocation
* api endpoint that updates the currentLocation and a arbitrary number of history locations
*
* @param req
* @param res
* @param next
*/
function updateLocation(req, res, next) {
var MIATARU = req.MIATARU;
var requestConfig = MIATARU.config;
var locations = MIATARU.locations;
var chain = seq();
if(requestConfig.enableLocationHistory()) {
locations.forEach(function(loc, idx) {
var value = JSON.stringify(loc.data());
var historyKey = kb.build(loc.device(), KEY_HISTORY);
var lastKey = kb.build(loc.device(), KEY_LAST);
chain
// we do want to store location history, so check if we got, so lpush and trim
.seq(function() {
db.lpush(historyKey, value, this);
})
// take care that the location history does not grow beyond the set range of maximumNumberOfHistoryItems
.seq(function() {
db.ltrim(historyKey, 0, configuration.maximumNumberOfHistoryItems-1, this);
});
if(idx === locations.length - 1) {
// finally also update the last known location...
chain.seq(function() {
db.set(lastKey, value, this);
});
}
});
} else {
//we're only interested in the last item as we're not saving a history
var loc = locations[locations.length-1];
var timeToKeep = requestConfig.locationDataRetentionTime() * 60;
var value = JSON.stringify(loc.data());
seq()
// we do not want to save a location history, this means if there's one we delete it...
.par(function() {
db.del(kb.build(loc.device(), KEY_HISTORY), this);
})
// update the last known location
.par(function() {
db.setex(kb.build(loc.device(), KEY_LAST), timeToKeep, value, this);
});
}
chain
.seq(function() {
var eyes = ['x', '!', '^', '°'];
res.send((new models.ResponseUpdateLocation(eyes[parseInt(Math.random() * eyes.length, 10)])).data());
})
.catch(next);
}
/**
* GetLocation
* This is used to get the current location of a device.
*
* @param req
* @param res
* @param next
*/
function getLocation(req, res, next) {
var chain = seq();
var requestConfig = req.MIATARU.config;
var response = new models.ResponseLocation();
req.MIATARU.devices.forEach(function(device) {
chain.par(function() {
var done = this;
db.get(kb.build(device.device(), KEY_LAST), function (error, reply) {
if(error) return done(error);
// only if this device is existing we update the visitor list...
if (reply != null)
{
// store the visitor for this device (if it's not empty)...
var visitKey = kb.build(device.device(), KEY_VISIT);
var miataruVisitorObject = requestConfig.requestMiataruVisitorObject();
// only if the object is not null (= we do not want to store the visitor history of unknown devices)
if (miataruVisitorObject != null)
{
var visitValue = JSON.stringify(miataruVisitorObject);
seq()
// we do want to store visitor history, so check if we got, so lpush and trim
.seq(function() {
db.lpush(visitKey, visitValue, this);
})
// take care that the visitor history does not grow beyond the set range of maximumNumberOfLocationVistors
.seq(function() {
db.ltrim(visitKey, 0, configuration.maximumNumberOfLocationVistors-1, this);
});
}
}
response.pushLocation(JSON.parse(reply));
done();
});
});
});
chain.seq(function() {
res.send(response.data());
})
.catch(next);
}
/**
* GetLocationGeoJSON
* This is used to get the current location of a device encoded as GeoJSON
*
* @param req
* @param res
* @param next
*/
function getLocationGeoJSON(req, res, next) {
var chain = seq();
var response = new models.ResponseLocationGeoJSON();
req.MIATARU.devices.forEach(function(device) {
chain.par(function() {
var done = this;
db.get(kb.build(device.device(), KEY_LAST), function (error, reply) {
if(error) return done(error);
response.pushLocation(JSON.parse(reply));
done();
});
});
});
chain.seq(function() {
res.send(response.data());
})
.catch(next);
}
/**
* GetLocationGeoJSONGET
* This is used to get the current location of a device encoded as GeoJSON, but this time as a one-shot request for just one device
* @param req
* @param res
* @param next
*/
function getLocationGeoJSONGET(id, res, next) {
var chain = seq();
var response = new models.ResponseLocationGeoJSON();
chain.par(function() {
var done = this;
db.get(kb.build(id, KEY_LAST), function (error, reply) {
if(error) return done(error);
response.pushLocation(JSON.parse(reply));
done();
});
})
chain.seq(function() {
res.send(response.data());
}).catch(next);
}
/**
* GetVisitorHistory
* Retrieves the complete visitor history
* @param req
* @param res
* @param next
*/
function getVisitorHistory(req, res, next) {
var locationRequest = req.MIATARU.request;
var key = kb.build(locationRequest.device(), KEY_VISIT);
seq()
.par('listLength', function() {
db.llen(key, this);
})
.par('list', function() {
db.lrange(key, 0, locationRequest.amount()-1, this);
})
.seq(function() {
res.send((new models.ResponseVisitorHistory(
this.vars.listLength,
configuration.maximumNumberOfLocationVistors,
this.vars.list.map(function(value) {
return JSON.parse(value)
})
)).data());
})
.catch(function(error) {
next(new errors.InternalServerError(error));
});
}
module.exports = {
updateLocation: updateLocation,
getLocation: getLocation,
getLocationGeoJSON: getLocationGeoJSON,
getLocationGeoJSONGET: getLocationGeoJSONGET,
getLocationHistory: getLocationHistory,
getVisitorHistory: getVisitorHistory
};