2066 lines
90 KiB
JavaScript
2066 lines
90 KiB
JavaScript
'use strict';
|
|
|
|
var assert = require('../support/assert');
|
|
var _ = require('underscore');
|
|
var redis = require('redis');
|
|
var step = require('step');
|
|
var strftime = require('strftime');
|
|
var QueryTables = require('cartodb-query-tables').queryTables;
|
|
var NamedMapsCacheEntry = require('../../lib/cache/model/named-maps-entry');
|
|
var redisStatsDb = 5;
|
|
|
|
// Pollute the PG environment to make sure
|
|
// configuration settings are always enforced
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/174
|
|
process.env.PGPORT = '666';
|
|
process.env.PGHOST = 'fake';
|
|
|
|
var path = require('path');
|
|
var fs = require('fs');
|
|
var http = require('http');
|
|
|
|
var helper = require('../support/test-helper');
|
|
|
|
const createServer = require('../../lib/server');
|
|
var serverOptions = require('../../lib/server-options');
|
|
|
|
var LayergroupToken = require('../../lib/models/layergroup-token');
|
|
|
|
describe('template_api', function () {
|
|
var server;
|
|
|
|
before(function () {
|
|
server = createServer(serverOptions);
|
|
server.setMaxListeners(0);
|
|
// FIXME: we need a better way to reset cache while running tests
|
|
server.layergroupAffectedTablesCache.cache.reset();
|
|
});
|
|
|
|
var httpRendererResourcesServer;
|
|
before(function (done) {
|
|
// Start a server to test external resources
|
|
httpRendererResourcesServer = http.createServer(function (request, response) {
|
|
var filename = path.join(__dirname, '/../fixtures/http/light_nolabels-1-0-0.png');
|
|
fs.readFile(filename, { encoding: 'binary' }, function (err, file) {
|
|
if (err) {
|
|
return done();
|
|
}
|
|
response.writeHead(200);
|
|
response.write(file, 'binary');
|
|
response.end();
|
|
});
|
|
});
|
|
httpRendererResourcesServer.listen(8033, done);
|
|
});
|
|
|
|
after(function (done) {
|
|
httpRendererResourcesServer.close(done);
|
|
});
|
|
|
|
var keysToDelete;
|
|
beforeEach(function () {
|
|
keysToDelete = {};
|
|
});
|
|
|
|
afterEach(function (done) {
|
|
helper.deleteRedisKeys(keysToDelete, done);
|
|
});
|
|
|
|
var templateAcceptance1 = {
|
|
version: '0.0.1',
|
|
name: 'acceptance1',
|
|
auth: { method: 'open' },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, -5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2 offset 2',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
function makeTemplate (templateName) {
|
|
return {
|
|
version: '0.0.1',
|
|
name: templateName || 'acceptance1',
|
|
auth: { method: 'open' },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, -5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2 offset 2',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
}
|
|
|
|
function extendDefaultsTemplate (template) {
|
|
return _.extend({}, template, { auth: { method: 'open' }, placeholders: {} });
|
|
}
|
|
|
|
it('can add template, returning id', function (done) {
|
|
var expectedTplId = 'acceptance1';
|
|
var postRequest1 = {
|
|
url: '/api/v1/map/named',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance1)
|
|
};
|
|
step(
|
|
function postUnauthenticated () {
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function postTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'), res.body);
|
|
err = parsed.errors[0];
|
|
assert.ok(err.match(/only.*authenticated.*user/i),
|
|
'Unexpected error response: ' + err);
|
|
postRequest1.url += '?api_key=1234';
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function rePostTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
var expectedBody = { template_id: expectedTplId };
|
|
assert.deepStrictEqual(parsedBody, expectedBody);
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkFailure (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
assert.ok(parsedBody.errors[0].match(/already exists/i),
|
|
'Unexpected error for pre-existing template name: ' + parsedBody.errors);
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/128
|
|
it("cannot create template with auth='token' and no valid tokens", function (done) {
|
|
var tplId;
|
|
step(
|
|
function postTemplate1 () {
|
|
// clone the valid one, and give it another name
|
|
var brokenTemplate = JSON.parse(JSON.stringify(templateAcceptance1));
|
|
brokenTemplate.name = 'broken1';
|
|
// Set auth='token' and specify no tokens
|
|
brokenTemplate.auth.method = 'token';
|
|
delete brokenTemplate.auth.tokens;
|
|
var postRequest1 = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(brokenTemplate)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkFailure1 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
var re = /invalid.*authentication.*missing/i;
|
|
assert.ok(parsedBody.errors[0].match(re),
|
|
'Error for invalid authentication does not match ' + re + ': ' + parsedBody.errors);
|
|
return null;
|
|
},
|
|
function postTemplate2 (err) {
|
|
assert.ifError(err);
|
|
// clone the valid one and rename it
|
|
var brokenTemplate = JSON.parse(JSON.stringify(templateAcceptance1));
|
|
brokenTemplate.name = 'broken1';
|
|
// Set auth='token' and specify no tokens
|
|
brokenTemplate.auth.method = 'token';
|
|
brokenTemplate.auth.tokens = [];
|
|
var postRequest1 = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(brokenTemplate)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkFailure2 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
var re = new RegExp(/invalid.*authentication.*missing/i);
|
|
assert.ok(parsedBody.errors[0].match(re),
|
|
'Error for invalid authentication does not match ' + re + ': ' + parsedBody.errors);
|
|
return null;
|
|
},
|
|
function postTemplateValid (err) {
|
|
assert.ifError(err);
|
|
// clone the valid one and rename it
|
|
var brokenTemplate = JSON.parse(JSON.stringify(templateAcceptance1));
|
|
brokenTemplate.name = 'broken1';
|
|
var postRequest1 = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(brokenTemplate)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function putTemplateInvalid (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
// clone the valid one and rename it
|
|
var brokenTemplate = JSON.parse(JSON.stringify(templateAcceptance1));
|
|
brokenTemplate.name = 'broken1';
|
|
// Set auth='token' and specify no tokens
|
|
brokenTemplate.auth.method = 'token';
|
|
brokenTemplate.auth.tokens = [];
|
|
var putRequest1 = {
|
|
url: '/api/v1/map/named/' + tplId + '/?api_key=1234',
|
|
method: 'PUT',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(brokenTemplate)
|
|
};
|
|
var next = this;
|
|
assert.response(server, putRequest1, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function deleteTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
var re = /invalid.*authentication.*missing/i;
|
|
assert.ok(parsed.errors[0].match(re),
|
|
'Error for invalid authentication on PUT does not match ' +
|
|
re + ': ' + parsed.errors);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res, err) { next(err, res); });
|
|
},
|
|
function checkDelete (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 204, res.statusCode + ': ' + res.body);
|
|
assert.ok(!res.body, 'Unexpected body in DELETE /template response');
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('instance endpoint should return CORS headers', function (done) {
|
|
step(function postTemplate1 () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost.localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance1)
|
|
};
|
|
assert.response(server, postRequest, {}, function (res) { next(null, res); });
|
|
},
|
|
function testCORS () {
|
|
const allowHeaders = 'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization, ' +
|
|
'Carto-Event, Carto-Event-Source, Carto-Event-Group-Id, Content-Type';
|
|
assert.response(server, {
|
|
url: '/api/v1/map/named/acceptance1',
|
|
method: 'OPTIONS'
|
|
}, {
|
|
status: 200,
|
|
headers: {
|
|
'Access-Control-Allow-Headers': allowHeaders,
|
|
'Access-Control-Allow-Origin': '*'
|
|
}
|
|
}, function () { done(); });
|
|
});
|
|
});
|
|
|
|
describe('server-metadata', function () {
|
|
var serverMetadata;
|
|
beforeEach(function () {
|
|
serverMetadata = global.environment.serverMetadata;
|
|
global.environment.serverMetadata = { cdn_url: { http: 'test', https: 'tests' } };
|
|
});
|
|
|
|
afterEach(function () {
|
|
global.environment.serverMetadata = serverMetadata;
|
|
});
|
|
|
|
it('instance endpoint should return server metadata', function (done) {
|
|
var tmpl = _.clone(templateAcceptance1);
|
|
tmpl.name = 'rambotemplate2';
|
|
|
|
step(function postTemplate1 () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(tmpl)
|
|
};
|
|
assert.response(server, postRequest, {}, function (res) {
|
|
next(null, res);
|
|
});
|
|
},
|
|
function testCORS () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: '/api/v1/map/named/' + tmpl.name,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' }
|
|
}, {
|
|
status: 200
|
|
}, function (res) {
|
|
var parsed = JSON.parse(res.body);
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
assert.ok(_.isEqual(parsed.cdn_url, global.environment.serverMetadata.cdn_url));
|
|
next(null);
|
|
});
|
|
},
|
|
function deleteTemplate (err) {
|
|
assert.ifError(err);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tmpl.name + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' }
|
|
};
|
|
assert.response(server, delRequest, {}, function () {
|
|
done();
|
|
});
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('can list templates', function (done) {
|
|
var tplid1, tplid2;
|
|
step(
|
|
function postTemplate1 () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance1)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function postTemplate2 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplid1 = parsed.template_id;
|
|
|
|
var next = this;
|
|
var backupName = templateAcceptance1.name;
|
|
templateAcceptance1.name += '_new';
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance1)
|
|
};
|
|
templateAcceptance1.name = backupName;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function litsTemplatesUnauthenticated (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplid2 = parsed.template_id;
|
|
var next = this;
|
|
var getRequest = {
|
|
url: '/api/v1/map/named',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function litsTemplates (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
'Missing error from response: ' + res.body);
|
|
err = parsed.errors[0];
|
|
assert.ok(err.match(/authenticated user/), err);
|
|
var next = this;
|
|
var getRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkList (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_ids'),
|
|
"Missing 'template_ids' from response body: " + res.body);
|
|
var ids = parsed.template_ids;
|
|
assert.strictEqual(ids.length, 2);
|
|
assert.ok(ids.indexOf(tplid1) !== -1,
|
|
'Missing "' + tplid1 + "' from list response: " + ids.join(','));
|
|
assert.ok(ids.indexOf(tplid2) !== -1,
|
|
'Missing "' + tplid2 + "' from list response: " + ids.join(','));
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can update template', function (done) {
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(makeTemplate())
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function putMisnamedTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var backupName = templateAcceptance1.name;
|
|
templateAcceptance1.name = 'changed_name';
|
|
var putRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '/?api_key=1234',
|
|
method: 'PUT',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance1)
|
|
};
|
|
templateAcceptance1.name = backupName;
|
|
var next = this;
|
|
assert.response(server, putRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function putUnexistentTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
assert.ok(parsedBody.errors[0].match(/cannot update name/i),
|
|
'Unexpected error for invalid update: ' + parsedBody.errors);
|
|
var putRequest = {
|
|
url: '/api/v1/map/named/unexistent/?api_key=1234',
|
|
method: 'PUT',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(makeTemplate())
|
|
};
|
|
var next = this;
|
|
assert.response(server, putRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function putValidTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
assert.ok(parsedBody.errors[0].match(/cannot update name/i),
|
|
'Unexpected error for invalid update: ' + parsedBody.errors);
|
|
var putRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '/?api_key=1234',
|
|
method: 'PUT',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(makeTemplate())
|
|
};
|
|
var next = this;
|
|
assert.response(server, putRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkValidUpate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
assert.strictEqual(tplId, parsed.template_id);
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can get a template by id', function (done) {
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(makeTemplate())
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function getTemplateUnauthorized (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var getRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function getTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403, res.statusCode + ': ' + res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsedBody, 'errors'), res.body);
|
|
assert.ok(parsedBody.errors[0].match(/only.*authenticated.*user/i),
|
|
'Unexpected error for unauthenticated template get: ' + parsedBody.errors);
|
|
var getRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkReturnTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template'),
|
|
"Missing 'template' from response body: " + res.body);
|
|
assert.deepStrictEqual(extendDefaultsTemplate(makeTemplate()), parsed.template);
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can delete a template by id', function (done) {
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(makeTemplate())
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function getTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var getRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function deleteTemplateUnauthorized (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template'),
|
|
"Missing 'template' from response body: " + res.body);
|
|
assert.deepStrictEqual(extendDefaultsTemplate(makeTemplate()), parsed.template);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function deleteTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/only.*authenticated.*user/i),
|
|
'Unexpected error for unauthenticated template get: ' + parsed.errors);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function getMissingTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 204, res.statusCode + ': ' + res.body);
|
|
assert.ok(!res.body, 'Unexpected body in DELETE /template response');
|
|
var getRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkGetFailure (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 404, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/cannot find/i),
|
|
'Unexpected error for missing template: ' + parsed.errors);
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template by id wadus', function (done) {
|
|
// This map fetches data from a private table
|
|
var templateAcceptance2 = {
|
|
version: '0.0.1',
|
|
name: 'acceptance1',
|
|
auth: { method: 'token', valid_tokens: ['valid1', 'valid2'] },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var tplId;
|
|
var layergroupid;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance2)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/173
|
|
function instanciateForeignDB (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Unexpected success instanciating template with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/unauthorized/i),
|
|
'Unexpected error for unauthorized instance : ' + parsed.errors);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'foreign', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 404, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'), "Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/not found/i), 'Unexpected error for forbidden instance : ' + parsed.errors);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
layergroupid = parsed.layergroupid;
|
|
assert.ok(layergroupid.match(/^localhost@/),
|
|
'Returned layergroupid does not start with signer name: ' + layergroupid);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'last_updated'),
|
|
"Missing 'last_updated' from response body: " + res.body);
|
|
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
|
|
// TODO: check value of last_updated ?
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb0/0/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Fetching tile with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/permission denied/i),
|
|
'Unexpected error for unauthorized instance (expected /permission denied/): ' + parsed.errors);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + '/0/0/0.png?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkTile (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
return null;
|
|
},
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/172
|
|
function fetchTileForeignSignature (err) {
|
|
assert.ifError(err);
|
|
var foreignsigned = layergroupid.replace(/[^@]*@/, 'foreign@');
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + foreignsigned + '/0/0/0.png?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkForeignSignerError (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/cannot use/i),
|
|
'Unexpected error for unauthorized instance (expected /cannot use/): ' + parsed.errors);
|
|
return null;
|
|
},
|
|
function deleteTemplate (err) {
|
|
assert.ifError(err);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileDeleted (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 204,
|
|
'Deleting template: ' + res.statusCode + ':' + res.body);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + '/0/0/0.png?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkTileAvailable (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, 'Tile should be accessible');
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template with torque layer by id', function (done) {
|
|
// This map fetches data from a private table
|
|
var template = {
|
|
version: '0.0.1',
|
|
name: 'acceptance1',
|
|
auth: { method: 'token', valid_tokens: ['valid1', 'valid2'] },
|
|
layergroup: {
|
|
version: '1.1.0',
|
|
layers: [
|
|
{
|
|
type: 'torque',
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: 'Map { -torque-frame-count:1; -torque-resolution:1; ' +
|
|
"-torque-aggregation-function:'count(*)'; -torque-time-attribute:'updated_at'; }"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var tplId;
|
|
var layergroupid;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(template)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Unexpected success instanciating template with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/unauthorized/i),
|
|
'Unexpected error for unauthorized instance : ' + parsed.errors);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
layergroupid = parsed.layergroupid;
|
|
assert.ok(layergroupid.match(/^localhost@/),
|
|
'Returned layergroupid does not start with signer name: ' + layergroupid);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'last_updated'),
|
|
"Missing 'last_updated' from response body: " + res.body);
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
// TODO: check value of last_updated ?
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb0/0/0/0/0.json.torque',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Fetching tile with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/permission denied/i),
|
|
'Unexpected error for unauthorized instance (expected /permission denied): ' + parsed.errors);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb1/0/0/0/0.json.torque?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkTileFetchOnRestart (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
var cc = res.headers['x-cache-channel'];
|
|
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
|
|
assert.ok(cc);
|
|
assert.strictEqual(cc, expectedCC);
|
|
// hack simulating restart...
|
|
// FIXME: we need a better way to reset cache while running tests
|
|
server.layergroupAffectedTablesCache.cache.reset(); // need to clean channel cache
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb1/0/0/0/1.json.torque?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkCacheChannel (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Unexpected error for authorized instance: ' + res.statusCode + ' -- ' + res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
var cc = res.headers['x-cache-channel'];
|
|
var expectedCC = 'test_windshaft_cartodb_user_1_db:public.test_table_private_1';
|
|
assert.ok(cc, 'Missing X-Cache-Channel on fetch-after-restart');
|
|
assert.strictEqual(cc, expectedCC);
|
|
return null;
|
|
},
|
|
function deleteTemplate (err) {
|
|
assert.ifError(err);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchTileDeleted (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 204,
|
|
'Deleting template: ' + res.statusCode + ':' + res.body);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb2/0/0/0/0.json.torque?auth_token=valid1',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkTorqueTileAvailable (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, 'Torque tile should be accessible');
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template with attribute service by id', function (done) {
|
|
// This map fetches data from a private table
|
|
var template = {
|
|
version: '0.0.1',
|
|
name: 'acceptance1',
|
|
auth: { method: 'token', valid_tokens: ['valid1', 'valid2'] },
|
|
layergroup: {
|
|
version: '1.1.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 where cartodb_id in ( 5,6 )',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
attributes: { id: 'cartodb_id', columns: ['name', 'address'] }
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var tplId;
|
|
var layergroupid;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(template)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Unexpected success instanciating template with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/unauthorized/i),
|
|
'Unexpected error for unauthorized instance : ' + parsed.errors);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchAttributeNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
layergroupid = parsed.layergroupid;
|
|
assert.ok(layergroupid.match(/^localhost@/),
|
|
'Returned layergroupid does not start with signer name: ' + layergroupid);
|
|
assert.strictEqual(res.headers['x-layergroup-id'], parsed.layergroupid);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'last_updated'),
|
|
"Missing 'last_updated' from response body: " + res.body);
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
// TODO: check value of last_updated ?
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb0/0/attributes/5',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchAttributeAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 403,
|
|
'Fetching tile with no auth: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'errors'),
|
|
"Missing 'errors' from response body: " + res.body);
|
|
assert.ok(parsed.errors[0].match(/permission denied/i),
|
|
'Unexpected error for unauthorized getAttributes (expected /permission denied/): ' + parsed.errors);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb1/0/attributes/5?auth_token=valid2',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkAttribute (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Unexpected error for authorized getAttributes: ' + res.statusCode + ' -- ' + res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
return null;
|
|
},
|
|
function deleteTemplate (err) {
|
|
assert.ifError(err);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function fetchAttrDeleted (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 204,
|
|
'Deleting template: ' + res.statusCode + ':' + res.body);
|
|
var getRequest = {
|
|
url: '/api/v1/map/' + layergroupid + ':cb2/0/attributes/5?auth_token=valid2',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
};
|
|
var next = this;
|
|
assert.response(server, getRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkLayerAttributesAvailable (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, 'Layer attributes should be accessible');
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template by id with open auth', function (done) {
|
|
// This map fetches data from a private table
|
|
var templateAcceptanceOpen = {
|
|
version: '0.0.1',
|
|
name: 'acceptance_open',
|
|
auth: { method: 'open' },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptanceOpen)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
helper.checkNoCache(res);
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Unexpected success instanciating template with no auth: ' + res.statusCode + ': ' + res.body);
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template using jsonp', function (done) {
|
|
// This map fetches data from a private table
|
|
var templateAcceptanceOpen = {
|
|
version: '0.0.1',
|
|
name: 'acceptance_open_jsonp',
|
|
auth: { method: 'open' },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptanceOpen)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '/jsonp?callback=jsonTest',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkInstanciation (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/176
|
|
helper.checkCache(res);
|
|
var expectedSurrogateKey = [
|
|
new QueryTables.QueryMetadata([{
|
|
dbname: 'test_windshaft_cartodb_user_1_db',
|
|
schema_name: 'public',
|
|
table_name: 'test_table_private_1'
|
|
}]).key(),
|
|
new NamedMapsCacheEntry('localhost', templateAcceptanceOpen.name).key()
|
|
].join(' ');
|
|
helper.checkSurrogateKey(res, expectedSurrogateKey);
|
|
|
|
/* eslint-disable no-unused-vars, no-eval */
|
|
function jsonTest (body) {
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(body.layergroupid).token] = 0;
|
|
}
|
|
eval(res.body);
|
|
/* eslint-enable */
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
return null;
|
|
},
|
|
function finish (err) {
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can instanciate a template using jsonp with params', function (done) {
|
|
// This map fetches data from a private table
|
|
var templateAcceptanceOpen = {
|
|
version: '0.0.1',
|
|
name: 'acceptance_open_jsonp_params',
|
|
auth: { method: 'open' },
|
|
placeholders: {
|
|
color: { type: 'css_color', default: 'red' }
|
|
},
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill: <%= color %>; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var tplId;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptanceOpen)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instanciateNoAuth (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '/jsonp?callback=jsonTest&config=' + JSON.stringify({ color: 'blue' }),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkInstanciation (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/176
|
|
helper.checkCache(res);
|
|
var expectedSurrogateKey = [
|
|
new QueryTables.QueryMetadata([{
|
|
dbname: 'test_windshaft_cartodb_user_1_db',
|
|
schema_name: 'public',
|
|
table_name: 'test_table_private_1'
|
|
}]).key(),
|
|
new NamedMapsCacheEntry('localhost', templateAcceptanceOpen.name).key()
|
|
].join(' ');
|
|
helper.checkSurrogateKey(res, expectedSurrogateKey);
|
|
|
|
/* eslint-disable no-unused-vars, no-eval */
|
|
function jsonTest (body) {
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(body.layergroupid).token] = 0;
|
|
}
|
|
eval(res.body);
|
|
/* eslint-enable */
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('template instantiation raises mapviews counter', function (done) {
|
|
var layergroup = {
|
|
stat_tag: 'random_tag',
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select 1 as cartodb_id, !pixel_height! as h,' +
|
|
' ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
|
|
cartocss: '#layer { polygon-fill:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
var template = {
|
|
version: '0.0.1',
|
|
name: 'stat_gathering',
|
|
auth: { method: 'open' },
|
|
layergroup: layergroup
|
|
};
|
|
var statskey = 'user:localhost:mapviews';
|
|
var redisStatsClient = redis.createClient(
|
|
{
|
|
port: global.environment.redis.port,
|
|
host: global.environment.redis.host
|
|
});
|
|
var templateId; // will be set on template post
|
|
var now = strftime('%Y%m%d', new Date());
|
|
var errors = [];
|
|
step(
|
|
function cleanStats () {
|
|
var next = this;
|
|
redisStatsClient.select(redisStatsDb, function (err) {
|
|
if (err) {
|
|
next(err);
|
|
} else {
|
|
redisStatsClient.del(statskey + ':global', next);
|
|
}
|
|
});
|
|
},
|
|
function doPostTempate (err) {
|
|
assert.ifError(err);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(template)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instantiateTemplate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
templateId = JSON.parse(res.body).template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + templateId,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify({})
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function checkGlobalStats (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
redisStatsClient.ZSCORE(statskey + ':global', now, this);
|
|
},
|
|
function checkTagStats (err, val) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(val, '1', 'Expected score of ' + now + ' in ' + statskey + ':global to be 1, got ' + val);
|
|
redisStatsClient.ZSCORE(statskey + ':stat_tag:random_tag', now, this);
|
|
},
|
|
function checkTagStatsValue (err, val) {
|
|
assert.ifError(err);
|
|
assert.equal(val, '1', 'Expected score of ' + now + ' in ' + statskey + ':stat_tag:' + layergroup.stat_tag +
|
|
' to be 1, got ' + val);
|
|
return null;
|
|
},
|
|
function deleteTemplate (err) {
|
|
assert.ifError(err);
|
|
var delRequest = {
|
|
url: '/api/v1/map/named/' + templateId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var next = this;
|
|
assert.response(server, delRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function cleanupStats (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
assert.strictEqual(res.statusCode, 204, res.statusCode + ': ' + res.body);
|
|
if (err) {
|
|
errors.push('' + err);
|
|
}
|
|
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete[statskey + ':stat_tag:' + layergroup.stat_tag] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('instance map token changes with templates certificate changes', function (done) {
|
|
// This map fetches data from a private table
|
|
var templateAcceptance2 = {
|
|
version: '0.0.1',
|
|
name: 'acceptance2',
|
|
auth: { method: 'token', valid_tokens: ['valid1', 'valid2'] },
|
|
layergroup: {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var tplId;
|
|
var layergroupid;
|
|
step(
|
|
function postTemplate () {
|
|
var next = this;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateAcceptance2)
|
|
};
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instance1 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
tplId = parsed.template_id;
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res, err) { next(err, res); });
|
|
},
|
|
function checkInstance1 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
layergroupid = parsed.layergroupid;
|
|
helper.checkSurrogateKey(res, new NamedMapsCacheEntry('localhost', templateAcceptance2.name).key());
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
return null;
|
|
},
|
|
function updateTemplate (err) {
|
|
assert.ifError(err);
|
|
// clone the valid one and rename it
|
|
var changedTemplate = JSON.parse(JSON.stringify(templateAcceptance2));
|
|
changedTemplate.auth.method = 'open';
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '/?api_key=1234',
|
|
method: 'PUT',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(changedTemplate)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res) { next(null, res); });
|
|
},
|
|
function instance2 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'),
|
|
"Missing 'template_id' from response body: " + res.body);
|
|
assert.strictEqual(tplId, parsed.template_id);
|
|
var postRequest = {
|
|
url: '/api/v1/map/named/' + tplId + '?auth_token=valid2',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(server, postRequest, {},
|
|
function (res, err) { next(err, res); });
|
|
},
|
|
function checkInstance2 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200,
|
|
'Instantiating template: ' + res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'),
|
|
"Missing 'layergroupid' from response body: " + res.body);
|
|
assert.ok(layergroupid !== parsed.layergroupid);
|
|
helper.checkSurrogateKey(res, new NamedMapsCacheEntry('localhost', templateAcceptance2.name).key());
|
|
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('can use an http layer', function (done) {
|
|
var username = 'localhost';
|
|
|
|
var httpTemplateName = 'acceptance_http';
|
|
var httpTemplate = {
|
|
version: '0.0.1',
|
|
name: httpTemplateName,
|
|
layergroup: {
|
|
version: '1.3.0',
|
|
layers: [
|
|
{
|
|
type: 'http',
|
|
options: {
|
|
urlTemplate: 'http://127.0.0.1:8033/{s}/{z}/{x}/{y}.png',
|
|
subdomains: [
|
|
'a',
|
|
'b',
|
|
'c'
|
|
]
|
|
}
|
|
},
|
|
{
|
|
type: 'cartodb',
|
|
options: {
|
|
sql: 'select * from test_table_private_1',
|
|
cartocss: '#layer { marker-fill:blue; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var templateParams = {};
|
|
|
|
var expectedTemplateId = httpTemplateName;
|
|
var layergroupid;
|
|
step(
|
|
function createTemplate () {
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: {
|
|
host: username,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(httpTemplate)
|
|
},
|
|
{
|
|
status: 200
|
|
},
|
|
function (res, err) {
|
|
next(err, res);
|
|
}
|
|
);
|
|
},
|
|
function instantiateTemplate (err, res) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
assert.deepStrictEqual(JSON.parse(res.body), { template_id: expectedTemplateId });
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map/named/' + expectedTemplateId,
|
|
method: 'POST',
|
|
headers: {
|
|
host: username,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(templateParams)
|
|
},
|
|
{
|
|
status: 200
|
|
},
|
|
function (res) {
|
|
next(null, res);
|
|
}
|
|
);
|
|
},
|
|
function fetchTile (err, res) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(
|
|
Object.prototype.hasOwnProperty.call(parsed, 'layergroupid'), "Missing 'layergroupid' from response body: " + res.body);
|
|
layergroupid = parsed.layergroupid;
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map/' + layergroupid + '/all/0/0/0.png',
|
|
method: 'GET',
|
|
headers: {
|
|
host: username
|
|
},
|
|
encoding: 'binary'
|
|
},
|
|
{
|
|
status: 200
|
|
},
|
|
function (res) {
|
|
next(null, res);
|
|
}
|
|
);
|
|
},
|
|
function checkTile (err, res) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
return null;
|
|
},
|
|
function deleteTemplate (err) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map/named/' + expectedTemplateId + '?api_key=1234',
|
|
method: 'DELETE',
|
|
headers: {
|
|
host: username
|
|
}
|
|
},
|
|
{
|
|
status: 204
|
|
},
|
|
function (res, err) {
|
|
next(err, res);
|
|
}
|
|
);
|
|
},
|
|
function finish (err) {
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
describe('named map nonexistent tokens', function () {
|
|
var username = 'localhost';
|
|
var templateHash = 'deadbeef';
|
|
var nonexistentToken = 'wadus';
|
|
|
|
function request (token) {
|
|
return {
|
|
url: '/api/v1/map/' + token + '/all/0/0/0.png',
|
|
method: 'GET',
|
|
headers: {
|
|
host: username
|
|
},
|
|
encoding: 'binary'
|
|
};
|
|
}
|
|
|
|
var expectedResponse = {
|
|
headers: {
|
|
'Content-Type': 'application/json; charset=utf-8'
|
|
},
|
|
status: 400
|
|
};
|
|
|
|
function checkTileFn (done) {
|
|
return function checkTile (res, err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
assert.deepStrictEqual(JSON.parse(res.body).errors,
|
|
["Invalid or nonexistent map configuration token '" + nonexistentToken + "'"]);
|
|
|
|
done();
|
|
};
|
|
}
|
|
|
|
it('returns an error for named map nonexistent tokens', function (done) {
|
|
var nonexistentNamedMapToken = username + '@' + templateHash + '@' + nonexistentToken;
|
|
|
|
assert.response(
|
|
server,
|
|
request(nonexistentNamedMapToken),
|
|
expectedResponse,
|
|
checkTileFn(done)
|
|
);
|
|
});
|
|
|
|
it('returns an error for named map nonexistent tokens without template hash', function (done) {
|
|
var nonexistentNamedMapToken = username + '@' + nonexistentToken;
|
|
|
|
assert.response(
|
|
server,
|
|
request(nonexistentNamedMapToken),
|
|
expectedResponse,
|
|
checkTileFn(done)
|
|
);
|
|
});
|
|
});
|
|
|
|
var torqueParamsScenarios = [
|
|
{
|
|
templateParams: {},
|
|
expectedTile: [{ x__uint8: 125, y__uint8: 159, vals__uint8: [2], dates__uint16: [0] }]
|
|
},
|
|
{
|
|
templateParams: { namesFilter: "'Hawai'" },
|
|
expectedTile: [{ x__uint8: 125, y__uint8: 159, vals__uint8: [1], dates__uint16: [0] }]
|
|
}
|
|
];
|
|
torqueParamsScenarios.forEach(function (scenario) {
|
|
it('can instantiate with torque layer and params=' + JSON.stringify(scenario.templateParams), function (done) {
|
|
var torqueParamsTemplate = {
|
|
version: '0.0.1',
|
|
name: 'acceptance_torque_params',
|
|
auth: {
|
|
method: 'open'
|
|
},
|
|
placeholders: {
|
|
namesFilter: {
|
|
type: 'sql_ident',
|
|
default: "'Hawai', 'El Estocolmo'"
|
|
}
|
|
},
|
|
layergroup: {
|
|
version: '1.4.0',
|
|
layers: [
|
|
{
|
|
type: 'torque',
|
|
options: {
|
|
sql: 'select * from test_table_private_1 where name in (<%= namesFilter %>)',
|
|
cartocss: 'Map { -torque-frame-count:1; -torque-resolution:1; ' +
|
|
"-torque-aggregation-function:'count(*)'; -torque-time-attribute:'cartodb_id'; }",
|
|
cartocss_version: '2.0.2'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
var layergroupIdToDelete;
|
|
step(
|
|
function createTemplate () {
|
|
var next = this;
|
|
var createTemplateRequest = {
|
|
url: '/api/v1/map/named?api_key=1234',
|
|
method: 'POST',
|
|
headers: {
|
|
host: 'localhost',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(torqueParamsTemplate)
|
|
};
|
|
assert.response(
|
|
server,
|
|
createTemplateRequest,
|
|
{
|
|
status: 200
|
|
},
|
|
function (res, err) {
|
|
next(err, res);
|
|
}
|
|
);
|
|
},
|
|
function instantiateTemplate (err, res) {
|
|
assert.ifError(err);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(parsed, 'template_id'), "Missing 'template_id' from response: " + res.body);
|
|
var templateId = parsed.template_id;
|
|
var instantiatePostRequest = {
|
|
url: '/api/v1/map/named/' + templateId,
|
|
method: 'POST',
|
|
headers: {
|
|
host: 'localhost',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(scenario.templateParams)
|
|
};
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
instantiatePostRequest,
|
|
{
|
|
status: 200
|
|
},
|
|
function (res, err) {
|
|
return next(err, res);
|
|
}
|
|
);
|
|
},
|
|
function requestTile (err, res) {
|
|
assert.ifError(err);
|
|
|
|
var layergroupId = JSON.parse(res.body).layergroupid;
|
|
layergroupIdToDelete = LayergroupToken.parse(layergroupId).token;
|
|
|
|
var torqueTileRequest = {
|
|
url: '/api/v1/map/' + layergroupId + '/0/0/0/0.torque.json',
|
|
method: 'GET',
|
|
headers: {
|
|
host: 'localhost'
|
|
}
|
|
};
|
|
var next = this;
|
|
assert.response(
|
|
server,
|
|
torqueTileRequest,
|
|
{
|
|
status: 200
|
|
},
|
|
function (res, err) {
|
|
return next(err, res);
|
|
}
|
|
);
|
|
},
|
|
function validateTileAndFinish (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
keysToDelete['map_cfg|' + layergroupIdToDelete] = 0;
|
|
keysToDelete['map_tpl|localhost'] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
assert.deepStrictEqual(
|
|
JSON.parse(res.body),
|
|
scenario.expectedTile
|
|
);
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|