From 60ab3eb6bad9cd3346981c135d37759355e5e4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Mon, 23 May 2016 10:26:09 +0200 Subject: [PATCH 01/12] Fixes #293, set cancel status to a job that actually is no running but has running status. --- batch/job_canceller.js | 9 +++++++-- batch/job_service.js | 22 +++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/batch/job_canceller.js b/batch/job_canceller.js index fb41b683..56e1a2e0 100644 --- a/batch/job_canceller.js +++ b/batch/job_canceller.js @@ -26,6 +26,10 @@ function doCancel(job_id, userDatabaseMetadata, callback) { return callback(err); } + if (!pid) { + return callback(); + } + doCancelQuery(pg, pid, function (err, isCancelled) { if (err) { return callback(err); @@ -35,7 +39,7 @@ function doCancel(job_id, userDatabaseMetadata, callback) { return callback(new Error('Query has not been cancelled')); } - callback(null); + callback(); }); }); } @@ -49,7 +53,8 @@ function getQueryPID(pg, job_id, callback) { } if (!result.rows[0] || !result.rows[0].pid) { - return callback(new Error('Query is not running currently')); + // query is not running actually, but we have to callback w/o error to cancel the job anyway. + return callback(); } callback(null, result.rows[0].pid); diff --git a/batch/job_service.js b/batch/job_service.js index 99ad458c..ea397461 100644 --- a/batch/job_service.js +++ b/batch/job_service.js @@ -36,19 +36,19 @@ JobService.prototype.list = function (user, callback) { } var jobList = dataList.map(function (data) { - var job; + var job; - try { - job = JobFactory.create(data); - } catch (err) { - return debug(err); - } + try { + job = JobFactory.create(data); + } catch (err) { + return debug(err); + } - return job; - }) - .filter(function (job) { - return job !== undefined; - }); + return job; + }) + .filter(function (job) { + return job !== undefined; + }); callback(null, jobList); }); From 232c8147de7ed00133ccaa375b8d7f45298544d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Tue, 24 May 2016 11:26:23 +0200 Subject: [PATCH 02/12] Adjust max-complexity value to linter --- batch/models/job_fallback.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 29b85056..abc4a1b0 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -76,7 +76,7 @@ JobFallback.is = function (query) { }; JobFallback.prototype.init = function () { - // jshint maxcomplexity: 10 + // jshint maxcomplexity: 8 for (var i = 0; i < this.data.query.query.length; i++) { if ((this.data.query.query[i].onsuccess || this.data.query.query[i].onerror) && !this.data.query.query[i].status) { @@ -111,7 +111,7 @@ JobFallback.prototype._hasNextQueryFromQuery = function () { }; JobFallback.prototype._getNextQueryFromQuery = function () { - // jshint maxcomplexity: 10 + // jshint maxcomplexity: 8 for (var i = 0; i < this.data.query.query.length; i++) { if (this.data.query.query[i].fallback_status) { @@ -238,7 +238,7 @@ JobFallback.prototype._setJobStatus = function (status, isChangeAppliedToQueryFa }; JobFallback.prototype._shiftJobStatus = function (status, isChangeAppliedToQueryFallback) { - // jshint maxcomplexity: 10 + // jshint maxcomplexity: 7 // In some scenarios we have to change the normal flow in order to keep consistency // between query's status and job's status. From 2724133591324d24484935a0ba0c955e2bb07713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Tue, 24 May 2016 12:38:30 +0200 Subject: [PATCH 03/12] Release 1.29.0 --- NEWS.md | 10 ++++++++-- npm-shrinkwrap.json | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index b231c055..f1681c0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,12 +1,18 @@ -1.28.2 - 2016-mm-dd +1.29.0 - 2016-05-24 ------------------- +New features: + * Add support for fallback-jobs in Batch API #296 + +Bug fixes: + * Fix issue in Batch API when a 'no longer running' job reports as 'running' before and after a job cancel #293 + 1.28.1 - 2016-05-12 ------------------- Bug fixes: - * OGR with _needSRS=true fails for empty tables #299 + * OGR with _needSRS=true_ fails for empty tables #299 1.28.0 - 2016-05-11 diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 62121384..dddea09b 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "cartodb_sql_api", - "version": "1.28.2", + "version": "1.29.0", "dependencies": { "cartodb-psql": { "version": "0.6.1", diff --git a/package.json b/package.json index 023ec099..c508a29e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "keywords": [ "cartodb" ], - "version": "1.28.2", + "version": "1.29.0", "repository": { "type": "git", "url": "git://github.com/CartoDB/CartoDB-SQL-API.git" From caa07e0968e2339f697e6c64f5433fed177a8ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Tue, 24 May 2016 12:41:09 +0200 Subject: [PATCH 04/12] Stubs next version --- NEWS.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index f1681c0e..2752e118 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +1.29.1 - 2016-mm-dd +------------------- + + 1.29.0 - 2016-05-24 ------------------- diff --git a/package.json b/package.json index c508a29e..fc9eda7b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "keywords": [ "cartodb" ], - "version": "1.29.0", + "version": "1.29.1", "repository": { "type": "git", "url": "git://github.com/CartoDB/CartoDB-SQL-API.git" From 23228b2d738da633918779e457994aafca0861a3 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 24 May 2016 14:28:00 +0200 Subject: [PATCH 05/12] Payload size validates multiple queries and fallback queries It uses a middleware to check the body size --- app/controllers/job_controller.js | 43 +++++--------- test/acceptance/job.query.limit.test.js | 77 ++++++++++++++++++++----- 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/app/controllers/job_controller.js b/app/controllers/job_controller.js index 2d5b186c..21283f81 100644 --- a/app/controllers/job_controller.js +++ b/app/controllers/job_controller.js @@ -13,21 +13,9 @@ var cdbReq = new CdbRequest(); var ONE_KILOBYTE_IN_BYTES = 1024; var MAX_LIMIT_QUERY_SIZE_IN_BYTES = 4 * ONE_KILOBYTE_IN_BYTES; // 4kb -function reachMaxQuerySizeLimit(query) { - var querySize; - - try { - querySize = (typeof query === 'string') ? query.length : JSON.stringify(query).length; - } catch (e) { - return false; - } - - return querySize > MAX_LIMIT_QUERY_SIZE_IN_BYTES; -} - function getMaxSizeErrorMessage(sql) { return util.format([ - 'Your payload is too large (%s). Max size allowed is %s (%skb).', + 'Your payload is too large: %s bytes. Max size allowed is %s bytes (%skb).', 'Are you trying to import data?.', 'Please, check out import api http://docs.cartodb.com/cartodb-platform/import-api/' ].join(' '), @@ -42,15 +30,26 @@ function JobController(userDatabaseService, jobService) { this.jobService = jobService; } +function bodyPayloadSizeMiddleware(req, res, next) { + var payload = JSON.stringify(req.body); + if (payload.length > MAX_LIMIT_QUERY_SIZE_IN_BYTES) { + return handleException(new Error(getMaxSizeErrorMessage(payload)), res); + } else { + return next(null); + } +} + module.exports = JobController; +module.exports.MAX_LIMIT_QUERY_SIZE_IN_BYTES = MAX_LIMIT_QUERY_SIZE_IN_BYTES; +module.exports.getMaxSizeErrorMessage = getMaxSizeErrorMessage; JobController.prototype.route = function (app) { - app.post(global.settings.base_url + '/sql/job', this.createJob.bind(this)); + app.post(global.settings.base_url + '/sql/job', bodyPayloadSizeMiddleware, this.createJob.bind(this)); app.get(global.settings.base_url + '/sql/job', this.listJob.bind(this)); app.get(global.settings.base_url + '/sql/job/:job_id', this.getJob.bind(this)); app.delete(global.settings.base_url + '/sql/job/:job_id', this.cancelJob.bind(this)); - app.put(global.settings.base_url + '/sql/job/:job_id', this.updateJob.bind(this)); - app.patch(global.settings.base_url + '/sql/job/:job_id', this.updateJob.bind(this)); + app.put(global.settings.base_url + '/sql/job/:job_id', bodyPayloadSizeMiddleware, this.updateJob.bind(this)); + app.patch(global.settings.base_url + '/sql/job/:job_id', bodyPayloadSizeMiddleware, this.updateJob.bind(this)); }; JobController.prototype.cancelJob = function (req, res) { @@ -253,18 +252,12 @@ JobController.prototype.getJob = function (req, res) { }; JobController.prototype.createJob = function (req, res) { - // jshint maxcomplexity: 7 var self = this; var body = (req.body) ? req.body : {}; var params = _.extend({}, req.query, body); // clone so don't modify req.params or req.body so oauth is not broken var sql = (params.query === "" || _.isUndefined(params.query)) ? null : params.query; var cdbUsername = cdbReq.userByReq(req); - // TODO: in job.validate() - if (reachMaxQuerySizeLimit(sql)) { - return handleException(new Error(getMaxSizeErrorMessage(sql)), res); - } - if ( req.profiler ) { req.profiler.start('sqlapi.job'); req.profiler.done('init'); @@ -331,7 +324,6 @@ JobController.prototype.createJob = function (req, res) { }; JobController.prototype.updateJob = function (req, res) { - // jshint maxcomplexity: 7 var self = this; var job_id = req.params.job_id; var body = (req.body) ? req.body : {}; @@ -339,11 +331,6 @@ JobController.prototype.updateJob = function (req, res) { var sql = (params.query === "" || _.isUndefined(params.query)) ? null : params.query; var cdbUsername = cdbReq.userByReq(req); - // TODO: in jobValidate - if (reachMaxQuerySizeLimit(sql)) { - return handleException(new Error(getMaxSizeErrorMessage(sql)), res); - } - if ( req.profiler ) { req.profiler.start('sqlapi.job'); req.profiler.done('init'); diff --git a/test/acceptance/job.query.limit.test.js b/test/acceptance/job.query.limit.test.js index 79561c86..c9cbceab 100644 --- a/test/acceptance/job.query.limit.test.js +++ b/test/acceptance/job.query.limit.test.js @@ -13,10 +13,11 @@ * */ require('../helper'); +var JobController = require('../../app/controllers/job_controller'); var app = require(global.settings.app_root + '/app/app')(); var assert = require('../support/assert'); -var querystring = require('querystring'); +var querystring = require('qs'); var metadataBackend = require('cartodb-redis')({ host: global.settings.redis_host, port: global.settings.redis_port, @@ -25,11 +26,23 @@ var metadataBackend = require('cartodb-redis')({ reapIntervalMillis: global.settings.redisReapIntervalMillis }); -var queryMaxSize = new Array(4097).join('a'); +function payload(query) { + return JSON.stringify({query: query}); +} +function payloadSize(query) { + return payload(query).length; +} + +var minPayloadSize = payloadSize(''); +var queryMaxSize = new Array(JobController.MAX_LIMIT_QUERY_SIZE_IN_BYTES - minPayloadSize + 1).join('a'); var queryTooLong = queryMaxSize.concat('a'); describe('job query limit', function() { + function expectedErrorMessage(query) { + return JobController.getMaxSizeErrorMessage(payload(query)); + } + after(function (done) { // batch services is not activate, so we need empty the queue to avoid unexpected // behaviour in further tests @@ -49,13 +62,7 @@ describe('job query limit', function() { status: 400 }, function (res) { var error = JSON.parse(res.body); - assert.deepEqual(error, { error: [ - [ - 'Your payload is too large (4097). Max size allowed is 4096 (4kb).', - 'Are you trying to import data?.', - 'Please, check out import api http://docs.cartodb.com/cartodb-platform/import-api/' - ].join(' ') - ]}); + assert.deepEqual(error, { error: [expectedErrorMessage(queryTooLong)] }); done(); }); }); @@ -73,13 +80,7 @@ describe('job query limit', function() { status: 400 }, function (res) { var error = JSON.parse(res.body); - assert.deepEqual(error, { error: [ - [ - 'Your payload is too large (4097). Max size allowed is 4096 (4kb).', - 'Are you trying to import data?.', - 'Please, check out import api http://docs.cartodb.com/cartodb-platform/import-api/' - ].join(' ') - ]}); + assert.deepEqual(error, { error: [expectedErrorMessage(queryTooLong)] }); done(); }); }); @@ -102,4 +103,48 @@ describe('job query limit', function() { }); }); + it('POST /api/v2/sql/job with a invalid query size should consider multiple queries', function (done){ + var queries = [queryTooLong, 'select 1']; + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST', + data: querystring.stringify({ + query: queries + }) + }, { + status: 400 + }, function (res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [expectedErrorMessage(queries)] }); + done(); + }); + }); + + it('POST /api/v2/sql/job with a invalid query size should consider fallback queries/callbacks', function (done){ + var fallbackQueries = { + query: [{ + query: queryTooLong, + onsuccess: "SELECT * FROM untitle_table_4 limit 1" + }, { + query: "SELECT * FROM untitle_table_4 limit 2", + onsuccess: "SELECT * FROM untitle_table_4 limit 3" + }] + }; + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { 'host': 'vizzuality.cartodb.com', 'Content-Type': 'application/x-www-form-urlencoded' }, + method: 'POST', + data: querystring.stringify({ + query: fallbackQueries + }) + }, { + status: 400 + }, function (res) { + var error = JSON.parse(res.body); + assert.deepEqual(error, { error: [expectedErrorMessage(fallbackQueries)] }); + done(); + }); + }); + }); From a91c7a6a886a2aca6d833d764136ac43b1149bdd Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 24 May 2016 15:31:54 +0200 Subject: [PATCH 06/12] Increase job payload size to 8kb --- app/controllers/job_controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/job_controller.js b/app/controllers/job_controller.js index 21283f81..3aa00d73 100644 --- a/app/controllers/job_controller.js +++ b/app/controllers/job_controller.js @@ -11,7 +11,8 @@ var handleException = require('../utils/error_handler'); var cdbReq = new CdbRequest(); var ONE_KILOBYTE_IN_BYTES = 1024; -var MAX_LIMIT_QUERY_SIZE_IN_BYTES = 4 * ONE_KILOBYTE_IN_BYTES; // 4kb +var MAX_LIMIT_QUERY_SIZE_IN_KB = 8; +var MAX_LIMIT_QUERY_SIZE_IN_BYTES = MAX_LIMIT_QUERY_SIZE_IN_KB * ONE_KILOBYTE_IN_BYTES; function getMaxSizeErrorMessage(sql) { return util.format([ From 49ac1b812da01ac80bd43f75632a8dce7e9e5d8d Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 24 May 2016 16:57:06 +0200 Subject: [PATCH 07/12] Update news --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index 2752e118..14db5c53 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,9 @@ 1.29.1 - 2016-mm-dd ------------------- +Announcements: + * Change Batch API size limit: 8kb per job. + 1.29.0 - 2016-05-24 ------------------- From b738295dc202b13f724f2270e18429bb03598eaa Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 24 May 2016 17:14:20 +0200 Subject: [PATCH 08/12] Release 1.29.1 --- NEWS.md | 2 +- npm-shrinkwrap.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 14db5c53..357be777 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -1.29.1 - 2016-mm-dd +1.29.1 - 2016-05-24 ------------------- Announcements: diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index dddea09b..3afd2445 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "cartodb_sql_api", - "version": "1.29.0", + "version": "1.29.1", "dependencies": { "cartodb-psql": { "version": "0.6.1", From 3a3c49e40dd43e2e0613732af7940c6cde15c3bb Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Tue, 24 May 2016 17:22:46 +0200 Subject: [PATCH 09/12] Stubs next version --- NEWS.md | 4 ++++ npm-shrinkwrap.json | 2 +- package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 357be777..00025eea 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +1.29.2 - 2016-mm-dd +------------------- + + 1.29.1 - 2016-05-24 ------------------- diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 3afd2445..9a5824db 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "cartodb_sql_api", - "version": "1.29.1", + "version": "1.29.2", "dependencies": { "cartodb-psql": { "version": "0.6.1", diff --git a/package.json b/package.json index fc9eda7b..a0b55977 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "keywords": [ "cartodb" ], - "version": "1.29.1", + "version": "1.29.2", "repository": { "type": "git", "url": "git://github.com/CartoDB/CartoDB-SQL-API.git" From e079491a7e226a2cd0c0bb98b45ec93b939afa84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Wed, 25 May 2016 17:00:27 +0200 Subject: [PATCH 10/12] Fixed issue with status transition fallback jobs --- batch/models/job_fallback.js | 7 ++- test/acceptance/job.fallback.test.js | 75 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index abc4a1b0..22d08547 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -257,6 +257,11 @@ JobFallback.prototype._shiftJobStatus = function (status, isChangeAppliedToQuery }; +JobFallback.prototype._shouldTryToApplyStatusTransitionToQueryFallback = function (index) { + return (this.data.query.query[index].status === jobStatus.DONE && this.data.query.query[index].onsuccess) || + (this.data.query.query[index].status === jobStatus.FAILED && this.data.query.query[index].onerror); +}; + JobFallback.prototype._setQueryStatus = function (status, errorMesssage) { // jshint maxcomplexity: 7 var isValid = false; @@ -275,7 +280,7 @@ JobFallback.prototype._setQueryStatus = function (status, errorMesssage) { break; } - if (this.data.query.query[i].fallback_status) { + if (this._shouldTryToApplyStatusTransitionToQueryFallback(i)) { isValid = this.isValidStatusTransition(this.data.query.query[i].fallback_status, status); if (isValid) { diff --git a/test/acceptance/job.fallback.test.js b/test/acceptance/job.fallback.test.js index 83fd904e..9a13491e 100644 --- a/test/acceptance/job.fallback.test.js +++ b/test/acceptance/job.fallback.test.js @@ -875,6 +875,81 @@ describe('Batch API fallback job', function () { }); }); + describe('"onerror" should not be triggered for any query', function () { + var fallbackJob = {}; + + it('should create a job', function (done) { + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'POST', + data: querystring.stringify({ + query: { + query: [{ + query: "SELECT * FROM untitle_table_4 limit 1", + onerror: "SELECT * FROM untitle_table_4 limit 2" + }, { + query: "SELECT * FROM untitle_table_4 limit 3", + onerror: "SELECT * FROM untitle_table_4 limit 4" + }] + } + }) + }, { + status: 201 + }, function (res, err) { + if (err) { + return done(err); + } + fallbackJob = JSON.parse(res.body); + done(); + }); + }); + + it('job should be failed', function (done) { + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1', + onerror: 'SELECT * FROM untitle_table_4 limit 2', + status: 'done', + fallback_status: 'pending' + }, { + query: 'SELECT * FROM untitle_table_4 limit 3', + onerror: 'SELECT * FROM untitle_table_4 limit 4', + status: 'done', + fallback_status: 'pending' + }] + }; + + var interval = setInterval(function () { + assert.response(app, { + url: '/api/v2/sql/job/' + fallbackJob.job_id + '?api_key=1234&', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'GET' + }, { + status: 200 + }, function (res, err) { + if (err) { + return done(err); + } + var job = JSON.parse(res.body); + if (job.status === jobStatus.DONE) { + clearInterval(interval); + assert.deepEqual(job.query, expectedQuery); + done(); + } else if (job.status === jobStatus.FAILED || job.status === jobStatus.CANCELLED) { + clearInterval(interval); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); + } + }); + }, 50); + }); + }); describe('"onsuccess" for first query should fail', function () { var fallbackJob = {}; From f0a0a9a0f5748686e3624b87903cbeaedb5b44b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Wed, 25 May 2016 17:36:13 +0200 Subject: [PATCH 11/12] Release 1.29.2 --- NEWS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 00025eea..6fa8c57a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,9 @@ -1.29.2 - 2016-mm-dd +1.29.2 - 2016-05-25 ------------------- +Bug fixes: + * Fixed issue with status transition in fallback jobs #308 + 1.29.1 - 2016-05-24 ------------------- From d9f5ac67f5ec1e97284f71b9002593861224ae2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Wed, 25 May 2016 17:39:04 +0200 Subject: [PATCH 12/12] Stubs next version --- NEWS.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6fa8c57a..179cc051 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +1.29.3 - 2016-mm-dd +------------------- + + 1.29.2 - 2016-05-25 ------------------- diff --git a/package.json b/package.json index a0b55977..8e4e61cd 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "keywords": [ "cartodb" ], - "version": "1.29.2", + "version": "1.29.3", "repository": { "type": "git", "url": "git://github.com/CartoDB/CartoDB-SQL-API.git"