diff --git a/lib/cartodb/controllers/base.js b/lib/cartodb/controllers/base.js index e5952ff6..b1b8bf6a 100644 --- a/lib/cartodb/controllers/base.js +++ b/lib/cartodb/controllers/base.js @@ -214,15 +214,15 @@ BaseController.prototype.sendError = function(req, res, err, label) { statusCode = 200; } - var errorResponseBody = { errors: allErrors.map(errorMessage) }; + var errorResponseBody = { + errors: allErrors.map(errorMessage), + errors_with_context: allErrors.map(errorMessageWithContext) + }; this.send(req, res, errorResponseBody, statusCode); }; -function errorMessage(err) { - // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 - var message = (_.isString(err) ? err : err.message) || 'Unknown error'; - +function stripConnectionInfo(message) { // Strip connection info, if any return message // See https://github.com/CartoDB/Windshaft/issues/173 @@ -230,6 +230,24 @@ function errorMessage(err) { // See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644 .replace(/is the server.*encountered/im, 'encountered'); } + +function errorMessage(err) { + // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 + var message = (_.isString(err) ? err : err.message) || 'Unknown error'; + + return stripConnectionInfo(message); +} + +function errorMessageWithContext(err) { + // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68 + var message = (_.isString(err) ? err : err.message) || 'Unknown error'; + + return { + type: err.type || 'unknown', + message: stripConnectionInfo(message), + context: err.context || 'unknown' + }; +} module.exports.errorMessage = errorMessage; function findStatusCode(err) { diff --git a/lib/cartodb/models/mapconfig/adapter/analysis-mapconfig-adapter.js b/lib/cartodb/models/mapconfig/adapter/analysis-mapconfig-adapter.js index 7a7150db..46f5185a 100644 --- a/lib/cartodb/models/mapconfig/adapter/analysis-mapconfig-adapter.js +++ b/lib/cartodb/models/mapconfig/adapter/analysis-mapconfig-adapter.js @@ -58,13 +58,27 @@ AnalysisMapConfigAdapter.prototype.getMapConfig = function(user, requestMapConfi requestMapConfig = appendFiltersToNodes(requestMapConfig, dataviewsFiltersBySourceId); - function createAnalysis(analysisDefinition, done) { - self.analysisBackend.create(analysisConfiguration, analysisDefinition, done); + function createAnalysis(analysisDefinition, index, done) { + self.analysisBackend.create(analysisConfiguration, analysisDefinition, function (err, analysis) { + if (err) { + err.context = { + type: 'analysis', + analysis: { + index: index, + id: analysisDefinition.id, + type: analysisDefinition.type + } + }; + return done(err); + } + + done(null, analysis); + }); } var analysesQueue = queue(requestMapConfig.analyses.length); - requestMapConfig.analyses.forEach(function(analysis) { - analysesQueue.defer(createAnalysis, analysis); + requestMapConfig.analyses.forEach(function(analysis, index) { + analysesQueue.defer(createAnalysis, analysis, index); }); analysesQueue.awaitAll(function(err, analysesResults) { diff --git a/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js b/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js index 3eb29197..2756eb2d 100644 --- a/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js +++ b/lib/cartodb/models/mapconfig/adapter/turbo-carto-adapter.js @@ -22,8 +22,8 @@ TurboCartoAdapter.prototype.getMapConfig = function (user, requestMapConfig, par var parseCartoQueue = queue(layers.length); - layers.forEach(function(layer) { - parseCartoQueue.defer(self._parseCartoCss.bind(self), user, layer); + layers.forEach(function(layer, index) { + parseCartoQueue.defer(self._parseCartoCss.bind(self), user, layer, index); }); parseCartoQueue.awaitAll(function (err, layers) { @@ -51,7 +51,7 @@ var pixelSizeTemplate = dot.template('40075017 * cos(ST_Y(ST_Centroid({{=it._bbo var scaleDenominatorTemplate = dot.template('({{=it._pixelSize}} / 0.00028)::numeric'); -TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback) { +TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, index, callback) { if (!shouldParseLayerCartocss(layer)) { return callback(null, layer); } @@ -84,9 +84,16 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback this.turboCartoParser.process(username, layer.options.cartocss, sql, function (err, cartocss) { // Only return turbo-carto errors if (err && err.name === 'TurboCartoError') { - err = new Error('turbo-carto: ' + err.message); - err.http_status = 400; - return callback(err); + var error = new Error('turbo-carto: ' + err.message); + error.http_status = 400; + error.type = 'turbo-carto'; + error.context = err.context; + error.context.layer = { + index: index, + type: layer.type + }; + + return callback(error); } // Try to continue in the rest of the cases diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 7932e0b0..1feb86ef 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1257,9 +1257,9 @@ "resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.3.0.tgz" }, "turbo-carto": { - "version": "0.11.0", - "from": "turbo-carto@0.11.0", - "resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.11.0.tgz", + "version": "0.12.0", + "from": "turbo-carto@0.12.0", + "resolved": "https://registry.npmjs.org/turbo-carto/-/turbo-carto-0.12.0.tgz", "dependencies": { "colorbrewer": { "version": "1.0.0", @@ -2971,6 +2971,11 @@ "from": "abbrev@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz" }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + }, "ansi": { "version": "0.3.1", "from": "ansi@>=0.3.1 <0.4.0", @@ -2981,61 +2986,51 @@ "from": "ansi-regex@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" }, - "ansi-styles": { - "version": "2.2.1", - "from": "ansi-styles@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + "async": { + "version": "1.5.2", + "from": "async@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" }, "are-we-there-yet": { "version": "1.1.2", "from": "are-we-there-yet@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz" }, - "asn1": { - "version": "0.2.3", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "async": { - "version": "1.5.2", - "from": "async@>=1.5.2 <2.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - }, - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.4.1", - "from": "aws4@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz" - }, "balanced-match": { "version": "0.4.1", "from": "balanced-match@>=0.4.1 <0.5.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz" }, - "block-stream": { - "version": "0.0.9", - "from": "block-stream@*", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, "boom": { "version": "2.10.1", "from": "boom@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "aws4": { + "version": "1.4.1", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz" + }, "brace-expansion": { "version": "1.1.4", "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.4.tgz" }, + "debug": { + "version": "2.2.0", + "from": "debug@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + }, "caseless": { "version": "0.11.0", "from": "caseless@>=0.11.0 <0.12.0", @@ -3191,11 +3186,6 @@ "from": "http-signature@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" }, - "inflight": { - "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz" - }, "inherits": { "version": "2.0.1", "from": "inherits@>=2.0.1 <2.1.0", @@ -3221,10 +3211,15 @@ "from": "is-typedarray@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz" + }, + "is-property": { + "version": "1.0.2", + "from": "is-property@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" }, "jodid25519": { "version": "1.0.2", @@ -3251,6 +3246,11 @@ "from": "json-stringify-safe@>=5.0.1 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, + "extend": { + "version": "3.0.0", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, "jsonpointer": { "version": "2.0.0", "from": "jsonpointer@2.0.0", @@ -3261,6 +3261,11 @@ "from": "jsprim@>=1.2.2 <2.0.0", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.2.2.tgz" }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, "lodash._baseslice": { "version": "4.0.0", "from": "lodash._baseslice@>=4.0.0 <4.1.0", @@ -3271,66 +3276,66 @@ "from": "lodash._basetostring@>=4.12.0 <4.13.0", "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz" }, - "lodash.pad": { - "version": "4.4.0", - "from": "lodash.pad@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.4.0.tgz" - }, - "lodash.padend": { - "version": "4.5.0", - "from": "lodash.padend@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.5.0.tgz" - }, "lodash.padstart": { "version": "4.5.0", "from": "lodash.padstart@>=4.1.0 <5.0.0", "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.5.0.tgz" }, - "lodash.tostring": { - "version": "4.1.3", - "from": "lodash.tostring@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz" + "lodash.pad": { + "version": "4.4.0", + "from": "lodash.pad@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.4.0.tgz" + }, + "has-unicode": { + "version": "2.0.0", + "from": "has-unicode@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.0.tgz" }, "mime-db": { "version": "1.23.0", "from": "mime-db@>=1.23.0 <1.24.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz" }, - "mime-types": { - "version": "2.1.11", - "from": "mime-types@>=2.1.7 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz" + "jsbn": { + "version": "0.1.0", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz" }, - "minimatch": { - "version": "3.0.0", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz" - }, - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "lodash.tostring": { + "version": "4.1.3", + "from": "lodash.tostring@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.3.tgz" }, "mkdirp": { "version": "0.5.1", "from": "mkdirp@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" }, - "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "minimatch": { + "version": "3.0.0", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz" }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + "lodash.padend": { + "version": "4.5.0", + "from": "lodash.padend@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.5.0.tgz" + }, + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" }, "nopt": { "version": "3.0.6", "from": "nopt@>=3.0.1 <3.1.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, "npmlog": { "version": "2.0.3", "from": "npmlog@>=2.0.0 <2.1.0", @@ -3341,10 +3346,15 @@ "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" }, - "path-is-absolute": { - "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + "readable-stream": { + "version": "2.1.2", + "from": "readable-stream@>=2.0.0 <3.0.0||>=1.1.13 <2.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.2.tgz" + }, + "qs": { + "version": "6.1.0", + "from": "qs@>=6.1.0 <6.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz" }, "oauth-sign": { "version": "0.8.2", @@ -3361,20 +3371,15 @@ "from": "pinkie-promise@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" }, - "qs": { - "version": "6.1.0", - "from": "qs@>=6.1.0 <6.2.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz" - }, - "readable-stream": { - "version": "2.1.2", - "from": "readable-stream@>=2.0.0 <3.0.0||>=1.1.13 <2.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.2.tgz" + "mime-types": { + "version": "2.1.11", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz" }, "request": { "version": "2.72.0", @@ -3396,25 +3401,20 @@ "from": "sntp@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.7 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" }, "stringstream": { "version": "0.0.5", "from": "stringstream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - }, - "strip-json-comments": { - "version": "1.0.4", - "from": "strip-json-comments@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" + "semver": { + "version": "5.1.0", + "from": "semver@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" }, "supports-color": { "version": "2.0.0", @@ -3431,26 +3431,36 @@ "from": "tough-cookie@>=2.2.0 <2.3.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" }, - "tunnel-agent": { - "version": "0.4.3", - "from": "tunnel-agent@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz" - }, "tweetnacl": { "version": "0.13.3", "from": "tweetnacl@>=0.13.0 <0.14.0", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz" }, - "uid-number": { - "version": "0.0.6", - "from": "uid-number@>=0.0.6 <0.1.0", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + "rimraf": { + "version": "2.5.2", + "from": "rimraf@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz" + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + }, + "tar": { + "version": "2.2.1", + "from": "tar@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" }, "util-deprecate": { "version": "1.0.2", "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, "verror": { "version": "1.3.6", "from": "verror@1.3.6", @@ -3466,18 +3476,6 @@ "from": "xtend@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, - "bl": { - "version": "1.1.2", - "from": "bl@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" - } - } - }, "dashdash": { "version": "1.13.1", "from": "dashdash@>=1.12.0 <2.0.0", @@ -3490,6 +3488,18 @@ } } }, + "bl": { + "version": "1.1.2", + "from": "bl@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.5 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" + } + } + }, "getpass": { "version": "0.1.6", "from": "getpass@>=0.1.1 <0.2.0", @@ -3502,6 +3512,18 @@ } } }, + "tar-pack": { + "version": "3.1.3", + "from": "tar-pack@>=3.1.0 <3.2.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.3.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.4 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" + } + } + }, "rc": { "version": "1.1.6", "from": "rc@>=1.1.0 <1.2.0", @@ -3525,18 +3547,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" } } - }, - "tar-pack": { - "version": "3.1.3", - "from": "tar-pack@>=3.1.0 <3.2.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.3.tgz", - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.4 <2.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" - } - } } } }, diff --git a/package.json b/package.json index 8768a928..c15b47d4 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "request": "~2.62.0", "step": "~0.0.6", "step-profiler": "~0.3.0", - "turbo-carto": "0.11.0", + "turbo-carto": "0.12.0", "underscore": "~1.6.0", "windshaft": "2.3.0" }, diff --git a/test/acceptance/analysis/error-cases.js b/test/acceptance/analysis/error-cases.js index fcfc6f13..c876f147 100644 --- a/test/acceptance/analysis/error-cases.js +++ b/test/acceptance/analysis/error-cases.js @@ -20,6 +20,13 @@ describe('analysis-layers error cases', function() { } }; + var AUTH_ERROR_RESPONSE = { + status: 403, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }; + it('should handle missing analysis nodes for layers', function(done) { var mapConfig = createMapConfig( [ @@ -64,4 +71,112 @@ describe('analysis-layers error cases', function() { testClient.drain(done); }); }); + + it('camshaft: should return error missing analysis nodes for layers with some context', function(done) { + var mapConfig = createMapConfig( + [ + { + "type": "cartodb", + "options": { + "source": { + "id": "HEAD" + }, + "cartocss": '#polygons { polygon-fill: red; }', + "cartocss_version": "2.3.0" + } + } + ], + {}, + [ + { + "id": "HEAD", + "type": "buffer", + "params": { + "source": { + "id": "HEAD", + "type": "source", + "params": { + "query": "select * from populated_places_simple_reduced" + } + }, + "radius": 50000 + } + } + ] + ); + + var testClient = new TestClient(mapConfig, 11111); + + testClient.getLayergroup(AUTH_ERROR_RESPONSE, function(err, layergroupResult) { + assert.ok(!err, err); + + assert.equal(layergroupResult.errors.length, 1); + assert.equal( + layergroupResult.errors[0], + 'Analysis requires authentication with API key: permission denied.' + ); + + assert.equal(layergroupResult.errors_with_context[0].context.type, 'analysis'); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.index, 0); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.id, 'HEAD'); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.type, 'buffer'); + + testClient.drain(done); + }); + }); + + + it('camshaft: should return error: Missing required param "radius"; with context', function(done) { + var mapConfig = createMapConfig( + [ + { + "type": "cartodb", + "options": { + "source": { + "id": "HEAD" + }, + "cartocss": '#polygons { polygon-fill: red; }', + "cartocss_version": "2.3.0" + } + } + ], + {}, + [ + { + "id": "HEAD", + "type": "buffer", + "params": { + "source": { + "id": "HEAD", + "type": "source", + "params": { + "query": "select * from populated_places_simple_reduced" + } + } + } + } + ] + ); + + var testClient = new TestClient(mapConfig, 1234); + + testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroupResult) { + assert.ok(!err, err); + + assert.equal(layergroupResult.errors.length, 1); + assert.equal( + layergroupResult.errors[0], + 'Missing required param "radius"' + ); + + assert.equal(layergroupResult.errors_with_context[0].context.type, 'analysis'); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.index, 0); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.id, 'HEAD'); + assert.equal(layergroupResult.errors_with_context[0].context.analysis.type, 'buffer'); + + testClient.drain(done); + }); + }); + + }); diff --git a/test/acceptance/dataviews/error-cases.js b/test/acceptance/dataviews/error-cases.js index ebbfdc34..7e37e8ee 100644 --- a/test/acceptance/dataviews/error-cases.js +++ b/test/acceptance/dataviews/error-cases.js @@ -54,7 +54,7 @@ describe('histogram-dataview', function() { this.testClient.getLayergroup(ERROR_RESPONSE, function(err, errObj) { assert.ok(!err, err); - assert.deepEqual(errObj, { errors: [ '"dataviews" must be a valid JSON object: "string" type found' ] }); + assert.deepEqual(errObj.errors, [ '"dataviews" must be a valid JSON object: "string" type found' ]); done(); }); @@ -66,7 +66,7 @@ describe('histogram-dataview', function() { this.testClient.getLayergroup(ERROR_RESPONSE, function(err, errObj) { assert.ok(!err, err); - assert.deepEqual(errObj, { errors: [ '"dataviews" must be a valid JSON object: "array" type found' ] }); + assert.deepEqual(errObj.errors, [ '"dataviews" must be a valid JSON object: "array" type found' ]); done(); }); diff --git a/test/acceptance/limits.js b/test/acceptance/limits.js index 30a4e739..d0126623 100644 --- a/test/acceptance/limits.js +++ b/test/acceptance/limits.js @@ -106,7 +106,7 @@ describe('render limits', function() { }, function(res) { var parsed = JSON.parse(res.body); - assert.deepEqual(parsed, { errors: [ 'Render timed out' ] }); + assert.deepEqual(parsed.errors, [ 'Render timed out' ]); done(); } ); @@ -171,7 +171,7 @@ describe('render limits', function() { }, function(res) { var parsed = JSON.parse(res.body); - assert.deepEqual(parsed, { errors: ['Render timed out'] }); + assert.deepEqual(parsed.errors, ['Render timed out']); done(); } ); diff --git a/test/acceptance/multilayer_server.js b/test/acceptance/multilayer_server.js index 251ecc38..e4c13b54 100644 --- a/test/acceptance/multilayer_server.js +++ b/test/acceptance/multilayer_server.js @@ -228,7 +228,7 @@ describe('tests from old api translated to multilayer', function() { }, function(res) { var parsed = JSON.parse(res.body); - assert.deepEqual(parsed, { errors: [ 'Unexpected token W' ] }); + assert.deepEqual(parsed.errors, [ 'Unexpected token W' ]); done(); } @@ -334,9 +334,7 @@ describe('tests from old api translated to multilayer', function() { assert.ok(!res.headers.hasOwnProperty('x-cache-channel')); var parsed = JSON.parse(res.body); - assert.deepEqual(parsed, { - errors: ["fake error message"] - }); + assert.deepEqual(parsed.errors, ["fake error message"]); done(); } diff --git a/test/acceptance/named_layers.js b/test/acceptance/named_layers.js index 8788e5a6..2b1cf4ef 100644 --- a/test/acceptance/named_layers.js +++ b/test/acceptance/named_layers.js @@ -181,7 +181,7 @@ describe('named_layers', function() { } var parsedBody = JSON.parse(response.body); - assert.deepEqual(parsedBody, { errors: ["Template 'nonexistent' of user 'localhost' not found"] }); + assert.deepEqual(parsedBody.errors, ["Template 'nonexistent' of user 'localhost' not found"]); return null; }, @@ -234,10 +234,7 @@ describe('named_layers', function() { } var parsedBody = JSON.parse(response.body); - assert.deepEqual( - parsedBody, - { errors: [ "Unauthorized 'auth_valid_template' template instantiation" ] } - ); + assert.deepEqual(parsedBody.errors, [ "Unauthorized 'auth_valid_template' template instantiation" ]); return null; }, @@ -347,7 +344,7 @@ describe('named_layers', function() { } var parsedBody = JSON.parse(response.body); - assert.deepEqual(parsedBody, { errors: [ 'Nested named layers are not allowed' ] }); + assert.deepEqual(parsedBody.errors, ['Nested named layers are not allowed' ]); return null; }, diff --git a/test/acceptance/named_maps_authentication.js b/test/acceptance/named_maps_authentication.js index f984391c..ba791f37 100644 --- a/test/acceptance/named_maps_authentication.js +++ b/test/acceptance/named_maps_authentication.js @@ -169,8 +169,8 @@ describe('named maps authentication', function() { getNamedTile(nonexistentName, 0, 0, 0, { status: 404 }, function(err, res) { assert.ok(!err); assert.deepEqual( - JSON.parse(res.body), - { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] } + JSON.parse(res.body).errors, + ["Template '" + nonexistentName + "' of user '" + username + "' not found"] ); done(); }); @@ -179,7 +179,7 @@ describe('named maps authentication', function() { it('should return 403 if not properly authorized', function(done) { getNamedTile(tokenAuthTemplateName, 0, 0, 0, { status: 403 }, function(err, res) { assert.ok(!err); - assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] }); + assert.deepEqual(JSON.parse(res.body).errors, ['Unauthorized template instantiation']); done(); }); }); @@ -238,8 +238,8 @@ describe('named maps authentication', function() { getStaticMap(nonexistentName, { status: 404 }, function(err, res) { assert.ok(!err); assert.deepEqual( - JSON.parse(res.body), - { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] } + JSON.parse(res.body).errors, + ["Template '" + nonexistentName + "' of user '" + username + "' not found"] ); done(); }); @@ -248,7 +248,7 @@ describe('named maps authentication', function() { it('should return 403 if not properly authorized', function(done) { getStaticMap(tokenAuthTemplateName, { status: 403 }, function(err, res) { assert.ok(!err); - assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] }); + assert.deepEqual(JSON.parse(res.body).errors, ['Unauthorized template instantiation']); done(); }); }); diff --git a/test/acceptance/named_maps_cache.js b/test/acceptance/named_maps_cache.js index a2f2295e..5d121afa 100644 --- a/test/acceptance/named_maps_cache.js +++ b/test/acceptance/named_maps_cache.js @@ -124,8 +124,8 @@ describe('named maps provider cache', function() { getNamedTile({ statusCode: 404 }, function(err, res) { assert.ok(!err); assert.deepEqual( - JSON.parse(res.body), - { errors: ["Template 'template_with_color' of user 'localhost' not found"] } + JSON.parse(res.body).errors, + ["Template 'template_with_color' of user 'localhost' not found"] ); // add template again so it's clean in afterEach diff --git a/test/acceptance/ported/attributes.js b/test/acceptance/ported/attributes.js index 5151cc09..5478bbb6 100644 --- a/test/acceptance/ported/attributes.js +++ b/test/acceptance/ported/attributes.js @@ -231,7 +231,11 @@ describe('attributes', function() { assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal( res.body, - '/**/ typeof test === \'function\' && test({"errors":["Layer 0 has no exposed attributes"]});' + '/**/ typeof test === \'function\' && ' + + 'test({"errors":["Layer 0 has no exposed attributes"],' + + '"errors_with_context":[{' + + '"type":"unknown","message":"Layer 0 has no exposed attributes","context":"unknown"' + + '}]});' ); return null; }, diff --git a/test/acceptance/ported/blend_http_fallback.js b/test/acceptance/ported/blend_http_fallback.js index 91e832f6..480dc5c0 100644 --- a/test/acceptance/ported/blend_http_fallback.js +++ b/test/acceptance/ported/blend_http_fallback.js @@ -138,11 +138,9 @@ describe('blend http fallback', function() { testClient.getTileLayer(mapConfig, tileRequest, expectedResponse, function(err, res) { assert.ok(!err); var parsedBody = JSON.parse(res.body); - assert.deepEqual(parsedBody, { - errors: [ - "Unable to fetch http tile: http://127.0.0.1:8033/error404/1/0/0.png [404]" - ] - }); + assert.deepEqual(parsedBody.errors, [ + "Unable to fetch http tile: http://127.0.0.1:8033/error404/1/0/0.png [404]" + ]); done(); }); }); diff --git a/test/acceptance/ported/external_resources.js b/test/acceptance/ported/external_resources.js index 6f73b52e..11ad5176 100644 --- a/test/acceptance/ported/external_resources.js +++ b/test/acceptance/ported/external_resources.js @@ -119,12 +119,11 @@ describe('external resources', function() { var mapConfig = testClient.defaultTableMapConfig('test_table_3', style); testClient.createLayergroup(mapConfig, { statusCode: 400 }, function(err, res) { - assert.deepEqual(JSON.parse(res.body), { - errors: ["Unable to download '" + url + "' for 'style0' (server returned 404)"] - }); + assert.deepEqual(JSON.parse(res.body).errors, [ + "Unable to download '" + url + "' for 'style0' (server returned 404)"] + ); done(); }); }); }); - diff --git a/test/acceptance/ported/multilayer_error_cases.js b/test/acceptance/ported/multilayer_error_cases.js index 11c11e63..9d59a5b3 100644 --- a/test/acceptance/ported/multilayer_error_cases.js +++ b/test/acceptance/ported/multilayer_error_cases.js @@ -31,7 +31,7 @@ describe('multilayer error cases', function() { }, {}, function(res) { assert.equal(res.statusCode, 400, res.body); var parsedBody = JSON.parse(res.body); - assert.deepEqual(parsedBody, {"errors":["layergroup POST data must be of type application/json"]}); + assert.deepEqual(parsedBody.errors, ["layergroup POST data must be of type application/json"]); done(); }); }); @@ -44,7 +44,7 @@ describe('multilayer error cases', function() { }, {}, function(res) { assert.equal(res.statusCode, 400, res.body); var parsedBody = JSON.parse(res.body); - assert.deepEqual(parsedBody, {"errors":["Missing layers array from layergroup config"]}); + assert.deepEqual(parsedBody.errors, ["Missing layers array from layergroup config"]); done(); }); }); @@ -58,7 +58,10 @@ describe('multilayer error cases', function() { assert.equal(res.statusCode, 200); assert.equal( res.body, - '/**/ typeof test === \'function\' && test({"errors":["Missing layers array from layergroup config"]});' + '/**/ typeof test === \'function\' && ' + + 'test({"errors":["Missing layers array from layergroup config"],' + + '"errors_with_context":[{"type":"unknown",' + + '"message":"Missing layers array from layergroup config","context":"unknown"}]});' ); done(); }); @@ -83,7 +86,7 @@ describe('multilayer error cases', function() { }, {}, function(res) { assert.equal(res.statusCode, 400, res.body); var parsedBody = JSON.parse(res.body); - assert.deepEqual(parsedBody, {errors:["Missing cartocss_version for layer 0 options"]}); + assert.deepEqual(parsedBody.errors, ["Missing cartocss_version for layer 0 options"]); done(); }); }); @@ -355,7 +358,7 @@ describe('multilayer error cases', function() { var mapConfig = testClient.singleLayerMapConfig('select * from test_table', null, null, 'name'); testClient.getGrid(mapConfig, 1, 13, 4011, 3088, defaultErrorExpectedResponse, function(err, res) { - assert.deepEqual(JSON.parse(res.body), { errors: ["Layer '1' not found in layergroup"] }); + assert.deepEqual(JSON.parse(res.body).errors, ["Layer '1' not found in layergroup"]); done(); }); }); @@ -383,7 +386,7 @@ describe('multilayer error cases', function() { // FIXME: should be 404 assert.equal(res.statusCode, 400, res.statusCode + ':' + res.body); var parsed = JSON.parse(res.body); - assert.deepEqual(parsed, {"errors": ["Invalid or nonexistent map configuration token 'deadbeef'"]}); + assert.deepEqual(parsed.errors, ["Invalid or nonexistent map configuration token 'deadbeef'"]); return null; }, function finish(err) { diff --git a/test/acceptance/ported/raster.js b/test/acceptance/ported/raster.js index 2cea4bf4..fc26661b 100644 --- a/test/acceptance/ported/raster.js +++ b/test/acceptance/ported/raster.js @@ -148,11 +148,10 @@ describe('raster', function() { assert.ok(!err); checkCORSHeaders(res); var parsedBody = JSON.parse(res.body); - assert.deepEqual(parsedBody, { errors: [ 'Mapnik raster layers do not support interactivity' ] }); + assert.deepEqual(parsedBody.errors, [ 'Mapnik raster layers do not support interactivity' ]); done(); } ); }); }); - diff --git a/test/acceptance/ported/regressions.js b/test/acceptance/ported/regressions.js index 66c1262f..2a2188c1 100644 --- a/test/acceptance/ported/regressions.js +++ b/test/acceptance/ported/regressions.js @@ -29,7 +29,7 @@ describe('regressions', function() { contentType: 'application/json; charset=utf-8' }; requestTile('/0/0/0.png?testUnexpectedError=1', options, function(err, res) { - assert.deepEqual(JSON.parse(res.body), { "errors": ["test unexpected error"] }); + assert.deepEqual(JSON.parse(res.body).errors, ["test unexpected error"]); finish(done); }); }); diff --git a/test/acceptance/ported/retina.js b/test/acceptance/ported/retina.js index 1c4b16f1..0962619f 100644 --- a/test/acceptance/ported/retina.js +++ b/test/acceptance/ported/retina.js @@ -129,7 +129,7 @@ describe('retina support', function() { }, function(res, err) { assert.ok(!err, 'Failed to request 0/0/0' + scaleFactor + '.png tile'); - assert.deepEqual(JSON.parse(res.body), { errors: ["Tile with specified resolution not found"] } ); + assert.deepEqual(JSON.parse(res.body).errors, ["Tile with specified resolution not found"]); done(); } diff --git a/test/acceptance/ported/server.js b/test/acceptance/ported/server.js index 7d8f2e73..650cf2c5 100644 --- a/test/acceptance/ported/server.js +++ b/test/acceptance/ported/server.js @@ -112,7 +112,7 @@ describe('server', function() { } }; testClient.getGrid(mapConfig, 0, 13, 4011, 3088, expectedResponse, function(err, res) { - assert.deepEqual(JSON.parse(res.body), {"errors":["Tileset has no interactivity"]}); + assert.deepEqual(JSON.parse(res.body).errors, ["Tileset has no interactivity"]); done(); }); }); diff --git a/test/acceptance/templates.js b/test/acceptance/templates.js index e8e0a2ca..7201b7fb 100644 --- a/test/acceptance/templates.js +++ b/test/acceptance/templates.js @@ -1927,9 +1927,8 @@ describe('template_api', function() { if (err) { return done(err); } - assert.deepEqual(JSON.parse(res.body), { - errors: ["Invalid or nonexistent map configuration token '" + nonexistentToken + "'"] - }); + assert.deepEqual(JSON.parse(res.body).errors, + ["Invalid or nonexistent map configuration token '" + nonexistentToken + "'"]); done(); }; diff --git a/test/acceptance/turbo-cartocss/error-cases.js b/test/acceptance/turbo-cartocss/error-cases.js index 3967738b..8582d753 100644 --- a/test/acceptance/turbo-cartocss/error-cases.js +++ b/test/acceptance/turbo-cartocss/error-cases.js @@ -106,4 +106,34 @@ describe('turbo-carto error cases', function() { done(); }); }); + + it('turbo-carto: should return error invalid column from datasource with some context', function(done) { + this.testClient = new TestClient(makeMapconfig(null, 'ramp([wadus_column], (red, green, blue))')); + this.testClient.getLayergroup(ERROR_RESPONSE, function(err, layergroup) { + assert.ok(!err, err); + + assert.ok(layergroup.hasOwnProperty('errors')); + assert.equal(layergroup.errors_with_context.length, 1); + assert.equal(layergroup.errors_with_context[0].type, 'turbo-carto'); + assert.ok(layergroup.errors_with_context[0].message.match(/^turbo-carto/)); + assert.ok(layergroup.errors_with_context[0].message.match(/unable\sto\scompute\sramp/i)); + assert.ok(layergroup.errors_with_context[0].message.match(/wadus_column/)); + + assert.equal(layergroup.errors_with_context[0].context.layer.index, 0); + assert.equal(layergroup.errors_with_context[0].context.layer.type, 'mapnik'); + + assert.equal(layergroup.errors_with_context[0].context.selector, '#populated_places_simple_reduced'); + assert.deepEqual(layergroup.errors_with_context[0].context.source, { + start: { + line: 10, + column: 3 + }, + end: { + line: 10, + column: 56 + } + }); + done(); + }); + }); });