Merge branch 'release/api_key'
This commit is contained in:
commit
28a35064a7
12
README.md
12
README.md
@ -12,18 +12,21 @@ core requirements
|
|||||||
-------------
|
-------------
|
||||||
* postgres
|
* postgres
|
||||||
* redis
|
* redis
|
||||||
* node v0.4.8+
|
* node >v0.4.8 && < 0.7.0>
|
||||||
* npm
|
* npm
|
||||||
|
|
||||||
usage
|
usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
Edit config/environments/<environment>.js
|
||||||
Make sure redis is running and knows about active cartodb user.
|
Make sure redis is running and knows about active cartodb user.
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
node [cluster.js|app.js] [developement|test|production]
|
node [cluster.js|app.js] <environment>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Supported <environment> values are developement, test, production
|
||||||
|
|
||||||
for examples of use, see /tests
|
for examples of use, see /tests
|
||||||
|
|
||||||
|
|
||||||
@ -38,3 +41,8 @@ npm install
|
|||||||
tests
|
tests
|
||||||
------
|
------
|
||||||
see test/README.md
|
see test/README.md
|
||||||
|
|
||||||
|
|
||||||
|
note on 0.4.x
|
||||||
|
--------------
|
||||||
|
output of large result sets is slow under node 0.4. Recommend running under 0.6 where possible.
|
@ -19,10 +19,11 @@ var express= require('express')
|
|||||||
buffer: true,
|
buffer: true,
|
||||||
format: '[:date] :req[X-Real-IP] \033[90m:method\033[0m \033[36m:req[Host]:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'
|
format: '[:date] :req[X-Real-IP] \033[90m:method\033[0m \033[36m:req[Host]:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'
|
||||||
}))
|
}))
|
||||||
, Step = require('step')
|
, Step = require('step')
|
||||||
, Meta = require(global.settings.app_root + '/app/models/metadata')
|
, Meta = require(global.settings.app_root + '/app/models/metadata')
|
||||||
, oAuth = require(global.settings.app_root + '/app/models/oauth')
|
, oAuth = require(global.settings.app_root + '/app/models/oauth')
|
||||||
, PSQL = require(global.settings.app_root + '/app/models/psql')
|
, PSQL = require(global.settings.app_root + '/app/models/psql')
|
||||||
|
, ApiKeyAuth = require(global.settings.app_root + '/app/models/apikey_auth')
|
||||||
, _ = require('underscore');
|
, _ = require('underscore');
|
||||||
|
|
||||||
app.use(express.bodyParser());
|
app.use(express.bodyParser());
|
||||||
@ -35,6 +36,7 @@ function handleQuery(req, res){
|
|||||||
// sanitize input
|
// sanitize input
|
||||||
var body = (req.body) ? req.body : {};
|
var body = (req.body) ? req.body : {};
|
||||||
var sql = req.query.q || body.q; // get and post
|
var sql = req.query.q || body.q; // get and post
|
||||||
|
var api_key = req.query.api_key || body.api_key;
|
||||||
var database = req.query.database; // deprecate this in future
|
var database = req.query.database; // deprecate this in future
|
||||||
var limit = parseInt(req.query.rows_per_page);
|
var limit = parseInt(req.query.rows_per_page);
|
||||||
var offset = parseInt(req.query.page);
|
var offset = parseInt(req.query.page);
|
||||||
@ -68,7 +70,11 @@ function handleQuery(req, res){
|
|||||||
function setDBGetUser(err, data) {
|
function setDBGetUser(err, data) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
database = (data == "" || _.isNull(data)) ? database : data;
|
database = (data == "" || _.isNull(data)) ? database : data;
|
||||||
oAuth.verifyRequest(req, this);
|
if(api_key) {
|
||||||
|
ApiKeyAuth.verifyRequest(req, this);
|
||||||
|
} else {
|
||||||
|
oAuth.verifyRequest(req, this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
function querySql(err, user_id){
|
function querySql(err, user_id){
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
114
app/models/apikey_auth.js
Normal file
114
app/models/apikey_auth.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* this module allows to auth user using an pregenerated api key
|
||||||
|
*/
|
||||||
|
|
||||||
|
var RedisPool = require("./redis_pool")
|
||||||
|
, _ = require('underscore')
|
||||||
|
, Step = require('step');
|
||||||
|
|
||||||
|
module.exports = (function() {
|
||||||
|
|
||||||
|
var me = {
|
||||||
|
user_metadata_db: 5,
|
||||||
|
table_metadata_db: 0,
|
||||||
|
user_key: "rails:users:<%= username %>",
|
||||||
|
map_key: "rails:users:<%= username %>:map_key",
|
||||||
|
table_key: "rails:<%= database_name %>:<%= table_name %>"
|
||||||
|
};
|
||||||
|
|
||||||
|
me.retrieve = function(db, redisKey, hashKey, callback) {
|
||||||
|
this.redisCmd(db,'HGET',[redisKey, hashKey], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
me.inSet = function(db, setKey, member, callback) {
|
||||||
|
this.redisCmd(db,'SISMEMBER',[setKey, member], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use Redis
|
||||||
|
*
|
||||||
|
* @param db - redis database number
|
||||||
|
* @param redisFunc - the redis function to execute
|
||||||
|
* @param redisArgs - the arguments for the redis function in an array
|
||||||
|
* @param callback - function to pass results too.
|
||||||
|
*/
|
||||||
|
me.redisCmd = function(db, redisFunc, redisArgs, callback) {
|
||||||
|
|
||||||
|
var redisClient;
|
||||||
|
Step(
|
||||||
|
function() {
|
||||||
|
var step = this;
|
||||||
|
RedisPool.acquire(db, function(_redisClient) {
|
||||||
|
redisClient = _redisClient;
|
||||||
|
redisArgs.push(step);
|
||||||
|
redisClient[redisFunc.toUpperCase()].apply(redisClient, redisArgs);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function releaseRedisClient(err, data) {
|
||||||
|
if (err) throw err;
|
||||||
|
RedisPool.release(db, redisClient);
|
||||||
|
callback(err, data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user id for this particular subdomain/username
|
||||||
|
*
|
||||||
|
* @param req - standard express req object. importantly contains host information
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
me.getId = function(req, callback) {
|
||||||
|
// strip subdomain from header host
|
||||||
|
var username = req.headers.host.split('.')[0];
|
||||||
|
var redisKey = _.template(this.user_key, {username: username});
|
||||||
|
|
||||||
|
this.retrieve(this.user_metadata_db, redisKey, 'id', callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user map key for this particular subdomain/username
|
||||||
|
*
|
||||||
|
* @param req - standard express req object. importantly contains host information
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
me.checkAPIKey= function(req, callback) {
|
||||||
|
// strip subdomain from header host
|
||||||
|
var username = req.headers.host.split('.')[0];
|
||||||
|
var redisKey = _.template(this.map_key, {username: username});
|
||||||
|
var api_key = req.query.api_key || req.body.api_key;
|
||||||
|
this.inSet(this.user_metadata_db, redisKey, api_key, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get privacy for cartodb table
|
||||||
|
*
|
||||||
|
* @param req - standard req object. Importantly contains table and host information
|
||||||
|
* @param callback - user_id if ok, null if auth fails
|
||||||
|
*/
|
||||||
|
me.verifyRequest = function(req, callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
Step(
|
||||||
|
// check api key
|
||||||
|
function(){
|
||||||
|
that.checkAPIKey(req, this);
|
||||||
|
},
|
||||||
|
// get user id or fail
|
||||||
|
function (err, apikey_valid) {
|
||||||
|
if (apikey_valid) {
|
||||||
|
that.getId(req, this);
|
||||||
|
} else {
|
||||||
|
// no auth
|
||||||
|
callback(false, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (err, user_id){
|
||||||
|
if (err) throw err;
|
||||||
|
callback(false, user_id);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return me;
|
||||||
|
})();
|
@ -65,7 +65,7 @@ var oAuth = function(){
|
|||||||
me.verifyRequest = function(req, callback){
|
me.verifyRequest = function(req, callback){
|
||||||
var that = this;
|
var that = this;
|
||||||
//TODO: review this
|
//TODO: review this
|
||||||
var http = arguments['2'];
|
var http = true;//arguments['2'];
|
||||||
var passed_tokens;
|
var passed_tokens;
|
||||||
var ohash;
|
var ohash;
|
||||||
var signature;
|
var signature;
|
||||||
|
12
package.json
12
package.json
@ -2,7 +2,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"name": "cartodb_api",
|
"name": "cartodb_api",
|
||||||
"description": "high speed SQL api for cartodb",
|
"description": "high speed SQL api for cartodb",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Simon Tokumine, Vizzuality",
|
"name": "Simon Tokumine, Vizzuality",
|
||||||
"url": "http://vizzuality.com",
|
"url": "http://vizzuality.com",
|
||||||
@ -10,14 +10,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cluster": "0.6.4",
|
"cluster": "0.6.4",
|
||||||
"express": "2.4.3",
|
"express": "2.5.8",
|
||||||
"underscore" : "1.1.x",
|
"underscore" : "1.1.x",
|
||||||
"underscore.string": "1.1.5",
|
"underscore.string": "1.1.5",
|
||||||
"pg": "0.5.6",
|
"pg": "0.6.14",
|
||||||
"generic-pool": "1.0.x",
|
"generic-pool": "1.0.x",
|
||||||
"redis": "0.6.1",
|
"redis": "0.7.1",
|
||||||
|
"hiredis": "*",
|
||||||
"step": "0.0.x",
|
"step": "0.0.x",
|
||||||
"oauth-client": "0.2.0"
|
"oauth-client": "0.2.0",
|
||||||
|
"node-uuid":"1.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"expresso": "0.8.x"
|
"expresso": "0.8.x"
|
||||||
|
56
spike/app.js
Normal file
56
spike/app.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
var express = require('express')
|
||||||
|
, app = express.createServer(
|
||||||
|
express.logger({
|
||||||
|
buffer: true,
|
||||||
|
format: '[:date] :req[X-Real-IP] \033[90m:method\033[0m \033[36m:req[Host]:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'
|
||||||
|
}))
|
||||||
|
, Step = require('step')
|
||||||
|
, _ = require('underscore');
|
||||||
|
|
||||||
|
app.use(express.bodyParser());
|
||||||
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
app.enable('jsonp callback');
|
||||||
|
|
||||||
|
var io = require('socket.io');
|
||||||
|
io = io.listen(app);
|
||||||
|
|
||||||
|
io.configure('development', function(){
|
||||||
|
io.set('log level', 1);
|
||||||
|
io.set('origins', '*:*');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(8080);
|
||||||
|
|
||||||
|
// hacked postgres setup
|
||||||
|
//var pg = require('pg');
|
||||||
|
var pg = require('pg').native //native libpq bindings = `
|
||||||
|
var conString = "tcp://postgres@localhost/cartodb_dev_user_2_db";
|
||||||
|
|
||||||
|
var client = new pg.Client(conString);
|
||||||
|
client.connect();
|
||||||
|
|
||||||
|
|
||||||
|
io.sockets.on('connection', function (socket) {
|
||||||
|
socket.emit('news', { hello: 'world' });
|
||||||
|
socket.on('my other event', function (data) {
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('sql_query', function(data){
|
||||||
|
|
||||||
|
var query = client.query(data.sql);
|
||||||
|
var id = data.id;
|
||||||
|
|
||||||
|
query.on('row', function(row) {
|
||||||
|
socket.emit("sql_result", {r:row, id:id, state:1})
|
||||||
|
});
|
||||||
|
|
||||||
|
query.on('end',function(){
|
||||||
|
socket.emit("sql_result", {id:id, state:0});
|
||||||
|
});
|
||||||
|
|
||||||
|
query.on('error', function(row){
|
||||||
|
socket.emit("sql_result", {r:row, id:id, state:-1})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
150
spike/public/gmaps_mercator.js
Normal file
150
spike/public/gmaps_mercator.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return a copy of this point with coordinates as int */
|
||||||
|
Point.prototype.floor = function() {
|
||||||
|
return new Point(this.x>>0, this.y>>0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LatLng(lat, lng) {
|
||||||
|
this.lat = lat;
|
||||||
|
this.lng = lng;
|
||||||
|
}
|
||||||
|
|
||||||
|
LatLng.prototype.clone = function() {
|
||||||
|
return new LatLng(this.lat, this.lng);
|
||||||
|
}
|
||||||
|
|
||||||
|
var TILE_SIZE = 256;
|
||||||
|
|
||||||
|
MercatorProjection.prototype.TILE_SIZE = TILE_SIZE;
|
||||||
|
|
||||||
|
function bound(value, opt_min, opt_max) {
|
||||||
|
if (opt_min != null) value = Math.max(value, opt_min);
|
||||||
|
if (opt_max != null) value = Math.min(value, opt_max);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function degreesToRadians(deg) {
|
||||||
|
return deg * (Math.PI / 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
function radiansToDegrees(rad) {
|
||||||
|
return rad / (Math.PI / 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MercatorProjection() {
|
||||||
|
this.pixelOrigin_ = new Point(TILE_SIZE / 2,
|
||||||
|
TILE_SIZE / 2);
|
||||||
|
this.pixelsPerLonDegree_ = TILE_SIZE / 360;
|
||||||
|
this.pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
MercatorProjection.prototype.fromLatLngToPixel = function(latLng, zoom) {
|
||||||
|
var p = this.fromLatLngToPoint(latLng);
|
||||||
|
return this.toPixelCoordinate(p, zoom);
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.fromLatLngToPoint = function(latLng,
|
||||||
|
opt_point) {
|
||||||
|
var me = this;
|
||||||
|
var point = opt_point || new Point(0, 0);
|
||||||
|
var origin = me.pixelOrigin_;
|
||||||
|
|
||||||
|
point.x = origin.x + latLng.lng * me.pixelsPerLonDegree_;
|
||||||
|
|
||||||
|
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
|
||||||
|
// 89.189. This is about a third of a tile past the edge of the world
|
||||||
|
// tile.
|
||||||
|
var siny = bound(Math.sin(degreesToRadians(latLng.lat)), -0.9999,
|
||||||
|
0.9999);
|
||||||
|
point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) *
|
||||||
|
-me.pixelsPerLonRadian_;
|
||||||
|
return point;
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.fromPointToLatLng = function(point) {
|
||||||
|
var me = this;
|
||||||
|
var origin = me.pixelOrigin_;
|
||||||
|
var lng = (point.x - origin.x) / me.pixelsPerLonDegree_;
|
||||||
|
var latRadians = (point.y - origin.y) / -me.pixelsPerLonRadian_;
|
||||||
|
var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) -
|
||||||
|
Math.PI / 2);
|
||||||
|
return new LatLng(lat, lng);
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.tileBBox = function(x, y, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var inc = TILE_SIZE/numTiles;
|
||||||
|
var px = x*TILE_SIZE/numTiles;
|
||||||
|
var py = y*TILE_SIZE/numTiles;
|
||||||
|
return [
|
||||||
|
this.fromPointToLatLng(new Point(px, py + inc)),
|
||||||
|
this.fromPointToLatLng(new Point(px + inc, py))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.tilePoint = function(x, y, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var px = x*TILE_SIZE;
|
||||||
|
var py = y*TILE_SIZE;
|
||||||
|
return [px, py];
|
||||||
|
}
|
||||||
|
MercatorProjection.prototype.fromPixelToLatLng = function(pixel, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var p = new Point(
|
||||||
|
pixel.x/numTiles,
|
||||||
|
pixel.y/numTiles);
|
||||||
|
return this.fromPointToLatLng(p);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MercatorProjection.prototype.toPixelCoordinate = function(worldCoordinate, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
return new Point(
|
||||||
|
worldCoordinate.x * numTiles,
|
||||||
|
worldCoordinate.y * numTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
MercatorProjection.prototype.latLngToTilePoint = function(latLng, x, y, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var projection = this;
|
||||||
|
var worldCoordinate = projection.fromLatLngToPoint(latLng);
|
||||||
|
var pixelCoordinate = new Point(
|
||||||
|
worldCoordinate.x * numTiles,
|
||||||
|
worldCoordinate.y * numTiles);
|
||||||
|
var tp = this.tilePoint(x, y, zoom);
|
||||||
|
return new Point(
|
||||||
|
Math.floor(pixelCoordinate.x - tp[0]),
|
||||||
|
Math.floor(pixelCoordinate.y - tp[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
MercatorProjection.prototype.pixelToTile = function(pixelCoordinate) {
|
||||||
|
return new Point(
|
||||||
|
Math.floor(pixelCoordinate.x / TILE_SIZE),
|
||||||
|
Math.floor(pixelCoordinate.y / TILE_SIZE));
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.pointToTile = function(point, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var pixelCoordinate = new Point(
|
||||||
|
point.x * numTiles,
|
||||||
|
point.y * numTiles);
|
||||||
|
return this.pixelToTile(pixelCoordinate);
|
||||||
|
};
|
||||||
|
|
||||||
|
MercatorProjection.prototype.latLngToTile = function(latLng, zoom) {
|
||||||
|
var numTiles = 1 << zoom;
|
||||||
|
var projection = this;
|
||||||
|
var worldCoordinate = projection.fromLatLngToPoint(latLng);
|
||||||
|
var pixelCoordinate = new Point(
|
||||||
|
worldCoordinate.x * numTiles,
|
||||||
|
worldCoordinate.y * numTiles);
|
||||||
|
return new Point(
|
||||||
|
Math.floor(pixelCoordinate.x / TILE_SIZE),
|
||||||
|
Math.floor(pixelCoordinate.y / TILE_SIZE));
|
||||||
|
}
|
59
spike/public/index.html
Normal file
59
spike/public/index.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||||
|
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="http://code.leafletjs.com/leaflet-0.3.1/leaflet.css" />
|
||||||
|
<!--[if lte IE 8]>
|
||||||
|
<link rel="stylesheet" href="http://code.leafletjs.com/leaflet-0.3.1/leaflet.ie.css" />
|
||||||
|
<![endif]-->
|
||||||
|
<script src="http://code.leafletjs.com/leaflet-0.3.1/leaflet.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
var map = new L.Map('map');
|
||||||
|
var cloudmade = new L.TileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {
|
||||||
|
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
|
||||||
|
maxZoom: 18
|
||||||
|
});
|
||||||
|
var london = new L.LatLng(51.505, -0.09);
|
||||||
|
map.setView(london, 3).addLayer(cloudmade);
|
||||||
|
var geojson = new L.GeoJSON();
|
||||||
|
map.addLayer(geojson);
|
||||||
|
|
||||||
|
var socket = io.connect('http://localhost:8080');
|
||||||
|
socket.on('news', function (data) {
|
||||||
|
console.log(data);
|
||||||
|
socket.emit('my other event', { my:'data' });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#go').click(function () {
|
||||||
|
var sql = $("#sql_input").val();
|
||||||
|
socket.emit("sql_query", {sql:sql, id:'TUMADRE'});
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('sql_result', function(data){
|
||||||
|
if (data.state == 1){
|
||||||
|
//console.log(data.r['the_geom']);
|
||||||
|
geojson.addGeoJSON(JSON.parse(data.r['the_geom']));
|
||||||
|
}
|
||||||
|
|
||||||
|
//$('#sql_result').append(JSON.stringify(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<input type="text" id="sql_input"></input>
|
||||||
|
<button type="submit" id="go">go</button>
|
||||||
|
<div id="map" style="height: 100%;width:100%;"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
266
spike/public/map.js
Normal file
266
spike/public/map.js
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
|
||||||
|
window.requestAnimFrame = (function(){
|
||||||
|
return window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
window.oRequestAnimationFrame ||
|
||||||
|
window.msRequestAnimationFrame ||
|
||||||
|
function( callback ){
|
||||||
|
window.setTimeout(callback, 1000 / 60);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function Event() {}
|
||||||
|
Event.prototype.on = function(evt, callback) {
|
||||||
|
var cb = this.callbacks = this.callbacks || {};
|
||||||
|
var l = cb[evt] || (cb[evt] = []);
|
||||||
|
l.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Event.prototype.emit = function(evt) {
|
||||||
|
var c = this.callbacks && this.callbacks[evt];
|
||||||
|
for(var i = 0; c && i < c.length; ++i) {
|
||||||
|
c[i].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function MapModel(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
this.projection = new MercatorProjection();
|
||||||
|
this.setCenter(opts.center || new LatLng(0,0));
|
||||||
|
this.setZoom(opts.zoom || 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MapModel.prototype = new Event();
|
||||||
|
|
||||||
|
MapModel.prototype.setCenter = function(center) {
|
||||||
|
this.center = new LatLng(center.lat, center.lng);
|
||||||
|
this.center_pixel = this.projection.fromLatLngToPixel(this.center, this.zoom).floor();
|
||||||
|
this.emit('center_changed', this.center);
|
||||||
|
};
|
||||||
|
|
||||||
|
MapModel.prototype.setZoom = function(zoom) {
|
||||||
|
this.zoom = zoom;
|
||||||
|
this.center_pixel = this.projection.fromLatLngToPixel(this.center, this.zoom).floor();
|
||||||
|
this.emit('zoom_changed', this.center);
|
||||||
|
};
|
||||||
|
|
||||||
|
MapModel.prototype.getCenterPixel = function() {
|
||||||
|
var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom);
|
||||||
|
return center_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapModel.prototype.getTopLeft = function(width, height) {
|
||||||
|
var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom);
|
||||||
|
var widthHalf = width / 2;
|
||||||
|
var heightHalf = height / 2;
|
||||||
|
center_point.x -= widthHalf;
|
||||||
|
center_point.y -= heightHalf;
|
||||||
|
return center_point;
|
||||||
|
}
|
||||||
|
MapModel.prototype.getBBox = function(width, height) {
|
||||||
|
var center_point = this.projection.fromLatLngToPixel(this.center, this.zoom);
|
||||||
|
var widthHalf = width / 2;
|
||||||
|
var heightHalf = height / 2;
|
||||||
|
center_point.x -= widthHalf;
|
||||||
|
center_point.y += heightHalf;
|
||||||
|
var bottomleft = this.projection.fromPixelToLatLng(center_point, this.zoom);
|
||||||
|
center_point.x += width;
|
||||||
|
center_point.y -= height;
|
||||||
|
var topRight = this.projection.fromPixelToLatLng(center_point, this.zoom);
|
||||||
|
return [bottomleft, topRight]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a list of tiles inside the spcified zone
|
||||||
|
* the center will be placed on the center of that zone
|
||||||
|
*/
|
||||||
|
MapModel.prototype.visibleTiles = function(width, height) {
|
||||||
|
var self = this;
|
||||||
|
var widthHalf = width / 2;
|
||||||
|
var heightHalf = height / 2;
|
||||||
|
var center_point = self.projection.fromLatLngToPixel(self.center, self.zoom);
|
||||||
|
center_point.x -= widthHalf;
|
||||||
|
center_point.y -= heightHalf;
|
||||||
|
var tile = this.projection.pixelToTile(center_point, self.zoom);
|
||||||
|
var offset_x = center_point.x%this.projection.TILE_SIZE;
|
||||||
|
var offset_y = center_point.y%this.projection.TILE_SIZE;
|
||||||
|
|
||||||
|
var num_tiles_x = Math.ceil((width + offset_x)/this.projection.TILE_SIZE);
|
||||||
|
var num_tiles_y = Math.ceil((height + offset_y)/this.projection.TILE_SIZE);
|
||||||
|
|
||||||
|
var tiles = [];
|
||||||
|
for(var i = 0; i < num_tiles_x; ++i) {
|
||||||
|
for(var j = 0; j < num_tiles_y; ++j) {
|
||||||
|
var tile_x = tile.x + i;
|
||||||
|
var tile_y = tile.y + j;
|
||||||
|
tiles.push({
|
||||||
|
x: tile_x * this.projection.TILE_SIZE,
|
||||||
|
y: tile_y * this.projection.TILE_SIZE,
|
||||||
|
zoom: self.zoom,
|
||||||
|
i: tile_x,
|
||||||
|
j: tile_y
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tiles;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragger(el) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var dragging = false;
|
||||||
|
var x, y;
|
||||||
|
|
||||||
|
el.ontouchstart = el.onmousedown = function(e) {
|
||||||
|
dragging = true;
|
||||||
|
if (e.touches) {
|
||||||
|
var p = e.touches[0];
|
||||||
|
x = p.pageX;
|
||||||
|
y = p.pageY;
|
||||||
|
} else {
|
||||||
|
x = e.clientX;
|
||||||
|
y = e.clientY;
|
||||||
|
}
|
||||||
|
self.emit('startdrag', x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
el.ontouchmove = el.onmousemove = function(e) {
|
||||||
|
var xx, yy;
|
||||||
|
if(!dragging) return;
|
||||||
|
if (e.touches) {
|
||||||
|
var p = e.touches[0];
|
||||||
|
xx = p.pageX;
|
||||||
|
yy = p.pageY;
|
||||||
|
} else {
|
||||||
|
xx = e.clientX;
|
||||||
|
yy = e.clientY;
|
||||||
|
}
|
||||||
|
self.emit('move', xx - x, yy - y);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
el.ontouchend = el.onmouseup = function(e) {
|
||||||
|
dragging = false;
|
||||||
|
self.emit('enddrag', x, y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
dragger.prototype = new Event();
|
||||||
|
|
||||||
|
function CanvasRenderer(el, map) {
|
||||||
|
var self = this;
|
||||||
|
this.el = el;
|
||||||
|
this.tiles = {};
|
||||||
|
this.width = el.offsetWidth >> 0;
|
||||||
|
this.height = el.offsetHeight >> 0;
|
||||||
|
var widthHalf = (this.width / 2) >> 0;
|
||||||
|
var heightHalf = (this.height / 2) >> 0;
|
||||||
|
|
||||||
|
var canvas = this.canvas = document.createElement('canvas');
|
||||||
|
canvas.style.padding = '0';
|
||||||
|
canvas.style.margin= '0';
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.width = this.width;
|
||||||
|
canvas.height = this.height;
|
||||||
|
|
||||||
|
var context = canvas.getContext( '2d' );
|
||||||
|
context.translate( widthHalf, heightHalf );
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.style.width = this.width + "px";
|
||||||
|
div.style.height= this.height + "px";
|
||||||
|
div.style.position = 'relative';
|
||||||
|
div.appendChild(canvas);
|
||||||
|
el.appendChild(div);
|
||||||
|
|
||||||
|
this.center_init = null;
|
||||||
|
this.target_center = new LatLng();
|
||||||
|
this.drag = new dragger(div);
|
||||||
|
this.drag.on('startdrag', function() {
|
||||||
|
self.center_init = map.center.clone();
|
||||||
|
});
|
||||||
|
this.drag.on('enddrag', function() {
|
||||||
|
map.emit('end_move');
|
||||||
|
});
|
||||||
|
|
||||||
|
function go_to_target() {
|
||||||
|
var c = map.center;
|
||||||
|
var t = self.target_center;
|
||||||
|
var dlat = t.lat - c.lat;
|
||||||
|
var dlon = t.lng - c.lng;
|
||||||
|
t.lat += dlat*0.0001;
|
||||||
|
t.lng += dlon*0.0001;
|
||||||
|
map.setCenter(t);
|
||||||
|
if(Math.abs(dlat) + Math.abs(dlon) > 0.001) {
|
||||||
|
requestAnimFrame(go_to_target);
|
||||||
|
} else {
|
||||||
|
//map.emit('end_move');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drag.on('move', function(dx, dy) {
|
||||||
|
var t = 1 << map.zoom;
|
||||||
|
var s = 1/t;
|
||||||
|
s = s/map.projection.pixelsPerLonDegree_;
|
||||||
|
self.target_center.lat = self.center_init.lat + dy*s;
|
||||||
|
self.target_center.lng = self.center_init.lng - dx*s;
|
||||||
|
requestAnimFrame(go_to_target);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.renderTile = function(tile, at) {
|
||||||
|
var self = this;
|
||||||
|
var key = at.x + '_' + at.y
|
||||||
|
if(a=self.tiles[key]) {
|
||||||
|
self.context.drawImage(a, at.x, at.y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//var layer = 'http://a.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{{z}}/{{x}}/{{y}}.png';
|
||||||
|
var layer = 'http://b.tiles.mapbox.com/v3/mapbox.mapbox-light/{{z}}/{{x}}/{{y}}.png64';
|
||||||
|
var url = layer.replace('{{z}}', tile.zoom).replace('{{x}}', tile.i).replace('{{y}}', tile.j);
|
||||||
|
var img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
img.onload = function() {
|
||||||
|
self.context.drawImage(img, at.x, at.y);
|
||||||
|
self.tiles[key] = img;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasRenderer.prototype.renderTiles = function(tiles, center) {
|
||||||
|
for(var i = 0; i < tiles.length; ++i) {
|
||||||
|
var tile = tiles[i];
|
||||||
|
var p = new Point(tile.x, tile.y);
|
||||||
|
p.x -= center.x;
|
||||||
|
p.y -= center.y;
|
||||||
|
this.renderTile(tile, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Map(el, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var self = this;
|
||||||
|
this.model = new MapModel({
|
||||||
|
center: opts.center || new LatLng(41.69, -4.83),
|
||||||
|
zoom: opts.zoom || 1
|
||||||
|
});
|
||||||
|
this.view = new CanvasRenderer(el, this.model);
|
||||||
|
/*function render() {
|
||||||
|
var tiles = self.model.visibleTiles(self.view.width, self.view.height);
|
||||||
|
self.view.renderTiles(tiles, this.center_pixel);
|
||||||
|
}
|
||||||
|
this.model.on('center_changed', render);
|
||||||
|
this.model.on('zoom_changed', render);
|
||||||
|
this.model.emit('center_changed');
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
1
spike/public/run_server.sh
Executable file
1
spike/public/run_server.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
python -m SimpleHTTPServer 8000
|
111
spike/public/test.html
Normal file
111
spike/public/test.html
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
html, body, #map {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#map {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #888;
|
||||||
|
font: bold 16px Helvetica;
|
||||||
|
padding: 5px 10px;
|
||||||
|
width: 10px;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#plus {
|
||||||
|
top: 40px;
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
#minus {
|
||||||
|
top: 40px;
|
||||||
|
left: 73px;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
background-color: #BBB;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="map"></div>
|
||||||
|
<a id="plus" href="#" class="button">+</a>
|
||||||
|
<a id="minus" href="#" class="button">-</a>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script src="gmaps_mercator.js"></script>
|
||||||
|
<script src="map.js"></script>
|
||||||
|
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
|
||||||
|
<script>
|
||||||
|
map = new Map(document.getElementById('map'), {
|
||||||
|
center: new LatLng(41.69, -4.83),
|
||||||
|
zoom: 4
|
||||||
|
});
|
||||||
|
document.getElementById('plus').onclick = function() {
|
||||||
|
map.model.setZoom(map.model.zoom + 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
document.getElementById('minus').onclick = function() {
|
||||||
|
map.model.setZoom(map.model.zoom - 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function render() {
|
||||||
|
//var tiles = self.model.visibleTiles(self.view.width, self.view.height);
|
||||||
|
//self.view.renderTiles(tiles, this.center_pixel);
|
||||||
|
}
|
||||||
|
var socket = io.connect('http://localhost:8080');
|
||||||
|
|
||||||
|
function get_sql() {
|
||||||
|
var bbox = map.model.getBBox(map.view.width, map.view.height);
|
||||||
|
var sql = "select st_asgeojson(the_geom,5) as the_geom_geojson from places WHERE the_geom && ST_SetSRID(ST_MakeBox2D(";
|
||||||
|
sql += "ST_Point(" + bbox[0].lng + "," + bbox[0].lat +"),";
|
||||||
|
sql += "ST_Point(" + bbox[1].lng + "," + bbox[1].lat +")), 4326)";
|
||||||
|
|
||||||
|
return sql ;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.model.on('end_move', jajaja=function() {
|
||||||
|
|
||||||
|
var s = get_sql();
|
||||||
|
//console.log(s);
|
||||||
|
//setTimeout(function() {
|
||||||
|
socket.emit("sql_query", {sql: s, id:'LOVELY'});
|
||||||
|
//}, 1000);
|
||||||
|
var tiles = map.model.visibleTiles(map.view.width, map.view.height);
|
||||||
|
map.view.renderTiles(tiles, this.center_pixel);
|
||||||
|
});
|
||||||
|
|
||||||
|
function add_point(p) {
|
||||||
|
var p = new LatLng(p[1], p[0]);
|
||||||
|
var px = map.model.projection.fromLatLngToPixel(p, map.model.zoom);
|
||||||
|
var centerpx= map.model.getCenterPixel(map.view.width, map.view.height);
|
||||||
|
map.view.context.fillStyle = '#000';
|
||||||
|
map.view.context.fillRect(px.x - centerpx.x , px.y - centerpx.y, 2, 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
socket.on('sql_result', function(data){
|
||||||
|
if (data.state == 1){
|
||||||
|
geo = JSON.parse(data.r['the_geom_geojson']);
|
||||||
|
add_point(geo.coordinates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
map.model.on('zoom_changed', jajaja);
|
||||||
|
map.model.emit('center_changed');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</html>
|
57
spike/public/test/map.test.js
Normal file
57
spike/public/test/map.test.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
describe("Event", function() {
|
||||||
|
|
||||||
|
before_each(function() {
|
||||||
|
this.evt = new Event();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("call event binded", function() {
|
||||||
|
var c = 0;
|
||||||
|
function callback() {
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
this.evt.on('test', callback);
|
||||||
|
this.evt.emit('test');
|
||||||
|
assert(c == 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should works when call non existing event", function() {
|
||||||
|
this.evt.emit('test_no_exists');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("MapModel", function() {
|
||||||
|
|
||||||
|
before_each(function() {
|
||||||
|
this.map_model = new MapModel(new LatLng(0, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("center_changed should be called", function() {
|
||||||
|
var c = 0;
|
||||||
|
this.map_model.on('center_changed', function() {
|
||||||
|
++c;
|
||||||
|
});
|
||||||
|
this.map_model.setCenter(new LatLng(0, 0));
|
||||||
|
assert(c == 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("zoom_changed should be called", function() {
|
||||||
|
var c = 0;
|
||||||
|
this.map_model.on('zoom_changed', function() {
|
||||||
|
++c;
|
||||||
|
});
|
||||||
|
this.map_model.setZoom(2);
|
||||||
|
assert(c == 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("visibleTiles", function() {
|
||||||
|
var ts = this.map_model.projection.TILE_SIZE;
|
||||||
|
this.map_model.setZoom(10);
|
||||||
|
var tiles = this.map_model.visibleTiles(ts, ts);
|
||||||
|
assert(tiles.length == 4);
|
||||||
|
this.map_model.setCenter(new LatLng(0.3, 1.2));
|
||||||
|
tiles = this.map_model.visibleTiles(ts, ts);
|
||||||
|
assert(tiles.length == 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
62
spike/public/test/suite.html
Normal file
62
spike/public/test/suite.html
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang=en>
|
||||||
|
<head>
|
||||||
|
<title>quick'n'dirty js testing framework</title>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
h1{ font: 1.5em Helvetica; }
|
||||||
|
p { font: .9em courier; padding: .5em 1em; margin: 0;}
|
||||||
|
.result { margin-top: 1px; }
|
||||||
|
.pass { color: #4F8A10; background: #DFF2BF }
|
||||||
|
.fail { color: #D8000C; background: #FFBABA }
|
||||||
|
.error{ color: white; background: red; font-weight: bold; }
|
||||||
|
.describe { border-bottom: 1px solid #ccc; padding: 20px; }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var setup_fn = null;
|
||||||
|
function describe(desc, fn) {
|
||||||
|
document.write('<div class="describe">');
|
||||||
|
document.write('<h1>' + desc +'</h1>');
|
||||||
|
fn();
|
||||||
|
document.write('</div>');
|
||||||
|
setup_fn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function before_each(be) {
|
||||||
|
setup_fn = be;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = true;
|
||||||
|
function assert(cond, msg) {
|
||||||
|
if(result) {
|
||||||
|
if(!cond) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function it(desc, fn) {
|
||||||
|
var output = function(klass, msg){
|
||||||
|
document.write('<p class="result ' + klass + '">' + msg + '</p>')
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
if (setup_fn != null)
|
||||||
|
setup_fn();
|
||||||
|
result = true;
|
||||||
|
fn()
|
||||||
|
} catch(err){ result = err }
|
||||||
|
if (typeof result === 'boolean'){
|
||||||
|
result ? output('pass', desc) : output('fail', desc)
|
||||||
|
}else{
|
||||||
|
output('error', ['ERROR:',result.message,'when testing',desc].join(' '))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="../gmaps_mercator.js"> </script>
|
||||||
|
<script src="../map.js"> </script>
|
||||||
|
<script src="map.test.js"> </script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
32
test/acceptance/app.auth.test.js
Normal file
32
test/acceptance/app.auth.test.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
require('../helper');
|
||||||
|
|
||||||
|
var app = require(global.settings.app_root + '/app/controllers/app')
|
||||||
|
, assert = require('assert')
|
||||||
|
, tests = module.exports = {}
|
||||||
|
, querystring = require('querystring');
|
||||||
|
|
||||||
|
tests['valid api key should allow insert in protected tables'] = function(){
|
||||||
|
assert.response(app, {
|
||||||
|
// view prepare_db.sh to see where to set api_key
|
||||||
|
url: "/api/v1/sql?api_key=1234&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('test')&database=cartodb_dev_user_1_db",
|
||||||
|
|
||||||
|
headers: {host: 'vizzuality.cartodb.com' },
|
||||||
|
method: 'GET'
|
||||||
|
},{
|
||||||
|
status: 200
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tests['invalid api key should NOT allow insert in protected tables'] = function(){
|
||||||
|
assert.response(app, {
|
||||||
|
// view prepare_db.sh to see where to set api_key
|
||||||
|
url: "/api/v1/sql?api_key=RAMBO&q=INSERT%20INTO%20private_table%20(name)%20VALUES%20('test')&database=cartodb_dev_user_1_db",
|
||||||
|
|
||||||
|
headers: {host: 'vizzuality.cartodb.com' },
|
||||||
|
method: 'GET'
|
||||||
|
},{
|
||||||
|
status: 400
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -18,8 +18,10 @@ var app = require(global.settings.app_root + '/app/controllers/app')
|
|||||||
, tests = module.exports = {}
|
, tests = module.exports = {}
|
||||||
, querystring = require('querystring');
|
, querystring = require('querystring');
|
||||||
|
|
||||||
var real_oauth_header = 'OAuth realm="http://vizzuality.testhost.lan/",oauth_consumer_key="fZeNGv5iYayvItgDYHUbot1Ukb5rVyX6QAg8GaY2",oauth_token="l0lPbtP68ao8NfStCiA3V3neqfM03JKhToxhUQTR",oauth_signature_method="HMAC-SHA1", oauth_signature="o4hx4hWP6KtLyFwggnYB4yPK8xI%3D",oauth_timestamp="1313581372",oauth_nonce="W0zUmvyC4eVL8cBd4YwlH1nnPTbxW0QBYcWkXTwe4",oauth_version="1.0"';
|
// allow lots of emitters to be set to silence warning
|
||||||
|
app.setMaxListeners(0);
|
||||||
|
|
||||||
|
var real_oauth_header = 'OAuth realm="http://vizzuality.testhost.lan/",oauth_consumer_key="fZeNGv5iYayvItgDYHUbot1Ukb5rVyX6QAg8GaY2",oauth_token="l0lPbtP68ao8NfStCiA3V3neqfM03JKhToxhUQTR",oauth_signature_method="HMAC-SHA1", oauth_signature="o4hx4hWP6KtLyFwggnYB4yPK8xI%3D",oauth_timestamp="1313581372",oauth_nonce="W0zUmvyC4eVL8cBd4YwlH1nnPTbxW0QBYcWkXTwe4",oauth_version="1.0"';
|
||||||
|
|
||||||
|
|
||||||
tests['GET /api/v1/sql'] = function(){
|
tests['GET /api/v1/sql'] = function(){
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
echo "preparing redis..."
|
echo "preparing redis..."
|
||||||
echo "HSET rails:users:vizzuality id 1" | redis-cli -n 5
|
echo "HSET rails:users:vizzuality id 1" | redis-cli -n 5
|
||||||
echo "HSET rails:users:vizzuality database_name cartodb_test_user_1_db" | redis-cli -n 5
|
echo "HSET rails:users:vizzuality database_name cartodb_test_user_1_db" | redis-cli -n 5
|
||||||
|
echo "SADD rails:users:vizzuality:map_key 1234" | redis-cli -n 5
|
||||||
|
|
||||||
echo "preparing postgres..."
|
echo "preparing postgres..."
|
||||||
dropdb -Upostgres -hlocalhost cartodb_test_user_1_db
|
dropdb -Upostgres -hlocalhost cartodb_test_user_1_db
|
||||||
|
@ -62,8 +62,51 @@ ALTER TABLE ONLY untitle_table_4 ADD CONSTRAINT test_table_pkey PRIMARY KEY (car
|
|||||||
CREATE INDEX test_table_the_geom_idx ON untitle_table_4 USING gist (the_geom);
|
CREATE INDEX test_table_the_geom_idx ON untitle_table_4 USING gist (the_geom);
|
||||||
CREATE INDEX test_table_the_geom_webmercator_idx ON untitle_table_4 USING gist (the_geom_webmercator);
|
CREATE INDEX test_table_the_geom_webmercator_idx ON untitle_table_4 USING gist (the_geom_webmercator);
|
||||||
|
|
||||||
|
CREATE TABLE private_table (
|
||||||
|
updated_at timestamp without time zone DEFAULT now(),
|
||||||
|
created_at timestamp without time zone DEFAULT now(),
|
||||||
|
cartodb_id integer NOT NULL,
|
||||||
|
name character varying,
|
||||||
|
address character varying,
|
||||||
|
the_geom geometry,
|
||||||
|
the_geom_webmercator geometry,
|
||||||
|
CONSTRAINT enforce_dims_the_geom CHECK ((st_ndims(the_geom) = 2)),
|
||||||
|
CONSTRAINT enforce_dims_the_geom_webmercator CHECK ((st_ndims(the_geom_webmercator) = 2)),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom CHECK (((geometrytype(the_geom) = 'POINT'::text) OR (the_geom IS NULL))),
|
||||||
|
CONSTRAINT enforce_geotype_the_geom_webmercator CHECK (((geometrytype(the_geom_webmercator) = 'POINT'::text) OR (the_geom_webmercator IS NULL))),
|
||||||
|
CONSTRAINT enforce_srid_the_geom CHECK ((st_srid(the_geom) = 4326)),
|
||||||
|
CONSTRAINT enforce_srid_the_geom_webmercator CHECK ((st_srid(the_geom_webmercator) = 3857))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE test_table_cartodb_id_seq_p
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE test_table_cartodb_id_seq_p OWNED BY private_table.cartodb_id;
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('test_table_cartodb_id_seq_p', 60, true);
|
||||||
|
|
||||||
|
ALTER TABLE private_table ALTER COLUMN cartodb_id SET DEFAULT nextval('test_table_cartodb_id_seq_p'::regclass);
|
||||||
|
|
||||||
|
INSERT INTO private_table VALUES ('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.314252', 1, 'Hawai', 'Calle de Pérez Galdós 9, Madrid, Spain', '0101000020E6100000A6B73F170D990DC064E8D84125364440', '0101000020110F000076491621312319C122D4663F1DCC5241');
|
||||||
|
INSERT INTO private_table VALUES ('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.319101', 2, 'El Estocolmo', 'Calle de la Palma 72, Madrid, Spain', '0101000020E6100000C90567F0F7AB0DC0AB07CC43A6364440', '0101000020110F0000C4356B29423319C15DD1092DADCC5241');
|
||||||
|
INSERT INTO private_table VALUES ('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.324', 3, 'El Rey del Tallarín', 'Plaza Conde de Toreno 2, Madrid, Spain', '0101000020E610000021C8410933AD0DC0CB0EF10F5B364440', '0101000020110F000053E71AC64D3419C10F664E4659CC5241');
|
||||||
|
INSERT INTO private_table VALUES ('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.329509', 4, 'El Lacón', 'Manuel Fernández y González 8, Madrid, Spain', '0101000020E6100000BC5983F755990DC07D923B6C22354440', '0101000020110F00005DACDB056F2319C1EC41A980FCCA5241');
|
||||||
|
INSERT INTO private_table VALUES ('2011-09-21 14:02:21.358706', '2011-09-21 14:02:21.334931', 5, 'El Pico', 'Calle Divino Pastor 12, Madrid, Spain', '0101000020E61000003B6D8D08C6A10DC0371B2B31CF364440', '0101000020110F00005F716E91992A19C17DAAA4D6DACC5241');
|
||||||
|
|
||||||
|
ALTER TABLE ONLY private_table ADD CONSTRAINT test_table_pkey_p PRIMARY KEY (cartodb_id);
|
||||||
|
|
||||||
|
CREATE INDEX test_table_the_geom_idx_p ON private_table USING gist (the_geom);
|
||||||
|
CREATE INDEX test_table_the_geom_webmercator_idx_p ON private_table USING gist (the_geom_webmercator);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE USER publicuser WITH PASSWORD '';
|
CREATE USER publicuser WITH PASSWORD '';
|
||||||
|
CREATE USER test_cartodb_user_1 WITH PASSWORD '';
|
||||||
|
|
||||||
GRANT SELECT ON TABLE untitle_table_4 TO publicuser;
|
GRANT SELECT ON TABLE untitle_table_4 TO publicuser;
|
||||||
|
GRANT ALL ON TABLE private_table TO test_cartodb_user_1;
|
||||||
|
GRANT ALL ON SEQUENCE test_table_cartodb_id_seq_p TO test_cartodb_user_1
|
||||||
|
Loading…
Reference in New Issue
Block a user