From 773833c629ff879d7f9e1eaadbe4159796611af7 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 19 Jun 2018 14:05:08 -0400 Subject: [PATCH 01/15] opened new version for development --- CHANGELOG.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825f46c..e22c681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,4 +52,6 @@ API Server for CogniCity * Remove ?format option from /reports endpoint * Change floods endpoint to return new attributes column * Requires cognicity-schema v3.0.7 or later -* Updated npm packages \ No newline at end of file +* Updated npm packages + +### v3.1.0 diff --git a/package.json b/package.json index 3670553..7aa11a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cognicity-server", - "version": "3.0.6", + "version": "3.1.0", "description": "Data Server for CogniCity", "main": "dist", "engines": { From 07c8af34d8e93b9d7dac1a9adae451410544c135 Mon Sep 17 00:00:00 2001 From: Tomas Holderness Date: Fri, 22 Jun 2018 13:11:46 -0400 Subject: [PATCH 02/15] Add sites to list of infrastructure tables --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index f84a2fc..d5a50e9 100644 --- a/src/config.js +++ b/src/config.js @@ -44,7 +44,7 @@ export default { IMAGES_BUCKET: process.env.IMAGES_BUCKET || 'petabencana-image-uploads', IMAGES_HOST: process.env.IMAGES_HOST || 'images.petabencana.id', IMAGE_MIME_TYPES: (process.env.IMAGE_MIME_TYPES || 'image/png,image/jpeg,image/gif').split(','), - INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,waterways').split(','), + INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,sites,waterways').split(','), LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','), LOG_CONSOLE: process.env.LOG_CONSOLE === 'true' || false, LOG_DIR: process.env.LOG_DIR || '', From f15d3e851ac526d8367de978248e2642277d3b3a Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 22 Jun 2018 14:36:40 -0400 Subject: [PATCH 03/15] fix #95 - add tags to infrastructure query --- src/api/routes/infrastructure/model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index 3fcfdca..1484bd9 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -16,7 +16,7 @@ export default (config, db, logger) => ({ // A list of all infrastructure matching a given type all: (city, type) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT name, the_geom + let query = `SELECT name, tags, the_geom FROM infrastructure.${type} WHERE ($1 IS NULL OR tags->>'instance_region_code'=$1)`; From c871264526165e9507ade421cf72f0a40c42d5cf Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 22 Jun 2018 14:55:14 -0400 Subject: [PATCH 04/15] tweak readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48e86d6..d26de67 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ A few points to note on config: Run `npm run -s build` to build. ### Testing -Testing is run by [Travis](https://travis-ci.org/urbanriskmap/cognicity-server). ESLint runs to check syntax. Integration tests, formed by chaining unit tests, are used to check the API. Coverage is provided by Istanbul and [Coveralls](https://coveralls.io/github/urbanriskmap/cognicity-server). See src/test/ for scripts. Beware that integration tests may pollute tables (e.g. user tables in feeds), it is not recommended to run tests against prod databases with live data. The default database (used for testing) is cognicity. Travis-ci creates a new schema instance for testing using https://github.com/urbanriskmap/cognicity-schema, see .travis.yml for more details. +Testing is run by [Travis](https://travis-ci.org/urbanriskmap/cognicity-server). ESLint runs to check syntax. Integration tests, formed by chaining unit tests, are used to check the API. Coverage is provided by Istanbul and [Coveralls](https://coveralls.io/github/urbanriskmap/cognicity-server). See src/test/ for scripts. Beware that integration tests may pollute tables (e.g. user tables in feeds), it is not recommended to run tests against prod databases with live data. The default database (used for testing) is cognicity. Travis-ci creates a new schema instance for testing using https://github.com/urbanriskmap/cognicity-schema, see the .travis.yml file for more details. To run tests locally a new database "cognicity_server_testing" is required on localhost. From 077b34c3dd070fe7979956b9a673e38d7852ce9e Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 25 Jun 2018 15:28:59 -0400 Subject: [PATCH 05/15] add basins to infrastructure types --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index d5a50e9..03f4971 100644 --- a/src/config.js +++ b/src/config.js @@ -44,7 +44,7 @@ export default { IMAGES_BUCKET: process.env.IMAGES_BUCKET || 'petabencana-image-uploads', IMAGES_HOST: process.env.IMAGES_HOST || 'images.petabencana.id', IMAGE_MIME_TYPES: (process.env.IMAGE_MIME_TYPES || 'image/png,image/jpeg,image/gif').split(','), - INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,sites,waterways').split(','), + INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'basins,floodgates,pumps,sites,waterways').split(','), LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','), LOG_CONSOLE: process.env.LOG_CONSOLE === 'true' || false, LOG_DIR: process.env.LOG_DIR || '', From 66f5f44cb07031bd5086773a5827899be4244443 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 11:34:41 -0400 Subject: [PATCH 06/15] Add city filter to /floods/archive - ref #66 --- src/api/routes/floods/archive/index.js | 3 ++- src/api/routes/floods/archive/model.js | 17 +++++++++++++---- src/api/routes/floods/index.js | 4 ++-- src/api/routes/floods/model.js | 4 ++-- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/api/routes/floods/archive/index.js b/src/api/routes/floods/archive/index.js index 67fed71..4958665 100644 --- a/src/api/routes/floods/archive/index.js +++ b/src/api/routes/floods/archive/index.js @@ -34,6 +34,7 @@ export default ({config, db, logger}) => { start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ') .min(Joi.ref('start')).required(), + city: Joi.alternatives().try(Joi.string(), Joi.any().valid(null)).default(null, 'city'), }, }), (req, res, next) => { @@ -53,7 +54,7 @@ export default ({config, db, logger}) => { ]}}); return; } - archive(config, db, logger).maxstate(req.query.start, req.query.end) + archive(config, db, logger).maxstate(req.query.start, req.query.end, req.query.city) .then((data) => res.status(200).json({statusCode: 200, result: data})) .catch((err) => { /* istanbul ignore next */ diff --git a/src/api/routes/floods/archive/model.js b/src/api/routes/floods/archive/model.js index bf0c369..320cbb2 100644 --- a/src/api/routes/floods/archive/model.js +++ b/src/api/routes/floods/archive/model.js @@ -15,13 +15,21 @@ export default (config, db, logger) => ({ // Get max state of all flood reports over time - maxstate: (start, end) => new Promise((resolve, reject) => { + maxstate: (start, end, city) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT local_area as area_id, changed as last_updated, - max_state FROM cognicity.rem_get_max_flood($1, $2)`; + let query = `SELECT + mf.local_area AS area_id, + mf.changed AS last_updated, + mf.max_state + FROM + cognicity.rem_get_max_flood($1, $2) AS mf, + ${config.TABLE_LOCAL_AREAS} AS la + WHERE + mf.local_area = la.pkey AND + ($3 IS NULL OR la.instance_region_code = $3)`; // Setup values - let values = [start, end]; + let values = [start, end, city]; // Execute logger.debug(query, values); @@ -35,3 +43,4 @@ export default (config, db, logger) => ({ }); }), }); + diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index d36bb58..5ec92ea 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -87,7 +87,7 @@ export default ({config, db, logger}) => { message: 'format must be \'json\' when geoformat ' +'IN (\'geojson\',\'topojson\')'}); } else { -floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) + floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? // If CAP format has been required convert to geojson then to CAP @@ -110,7 +110,7 @@ floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) /* istanbul ignore next */ next(err); }); -} + } } ); diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index 3c52b46..efa414a 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -14,7 +14,7 @@ */ export default (config, db, logger) => ({ - // Get all flood reports for a given city + // Get all flooded areas for a given city all: (city, minimumState) => new Promise((resolve, reject) => { // Setup query let query = `SELECT local_area as area_id, state, last_updated @@ -38,7 +38,7 @@ export default (config, db, logger) => ({ }); }), - // Get all flood reports for a given city + // Get all flooded areas for a given city allGeo: (city, minimumState) => new Promise((resolve, reject) => { // Setup query let query = `SELECT la.the_geom, la.pkey as area_id, la.geom_id, From 16ca3ec509c9b0715c44d35821ecb0aa80373db7 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 11:42:35 -0400 Subject: [PATCH 07/15] add city param to /floods/timeseries - fixes #66 --- src/api/routes/floods/timeseries/index.js | 3 ++- src/api/routes/floods/timeseries/model.js | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/api/routes/floods/timeseries/index.js b/src/api/routes/floods/timeseries/index.js index 0c1da00..e92ddd7 100644 --- a/src/api/routes/floods/timeseries/index.js +++ b/src/api/routes/floods/timeseries/index.js @@ -34,6 +34,7 @@ export default ({config, db, logger}) => { start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ') .min(Joi.ref('start')).required(), + city: Joi.alternatives().try(Joi.string(), Joi.any().valid(null)).default(null, 'city'), }, }), (req, res, next) => { @@ -54,7 +55,7 @@ export default ({config, db, logger}) => { return; } - timeseries(config, db, logger).count(req.query.start, req.query.end) + timeseries(config, db, logger).count(req.query.start, req.query.end, req.query.city) .then((data) => res.status(200).json({statusCode: 200, result: data})) .catch((err) => { /* istanbul ignore next */ diff --git a/src/api/routes/floods/timeseries/model.js b/src/api/routes/floods/timeseries/model.js index a6e9e25..8a191b6 100644 --- a/src/api/routes/floods/timeseries/model.js +++ b/src/api/routes/floods/timeseries/model.js @@ -15,17 +15,24 @@ export default (config, db, logger) => ({ // Get all flood reports for a given city - count: (start, end) => new Promise((resolve, reject) => { + count: (start, end, city) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT ts, count(local_area) FROM - (SELECT (cognicity.rem_get_flood(ts)).local_area, ts - FROM generate_series(date_trunc('hour', $1::timestamp with time zone), + let query = `SELECT series.ts, count(series.local_area) + FROM + (SELECT (cognicity.rem_get_flood(ts)).local_area, ts + FROM + generate_series(date_trunc('hour', $1::timestamp with time zone), date_trunc('hour', $2::timestamp with time zone),'1 hour') - as series(ts)) output - GROUP BY ts ORDER BY ts`; + AS series(ts)) AS series, + ${config.TABLE_LOCAL_AREAS} AS la + WHERE + series.local_area = la.pkey AND + ($3 IS NULL OR la.instance_region_code = $3) + GROUP BY series.ts + ORDER BY series.ts`; // Setup values - let values = [start, end]; + let values = [start, end, city]; // Execute logger.debug(query, values); From 901393fa03859014abf699859d4f3bfdb62c014c Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 11:44:40 -0400 Subject: [PATCH 08/15] update changelog for endpoint fixes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e22c681..e6dc684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,3 +55,4 @@ API Server for CogniCity * Updated npm packages ### v3.1.0 +* Add city query parameter to /floods/timeseries and /floods/archive endpoints From e7e9431fd062be5e2e475c53aecebe51a5cfe492 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 13:17:47 -0400 Subject: [PATCH 09/15] fix lint for new function calls --- src/api/routes/floods/archive/index.js | 6 ++++-- src/api/routes/floods/index.js | 3 ++- src/api/routes/floods/timeseries/index.js | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/api/routes/floods/archive/index.js b/src/api/routes/floods/archive/index.js index 4958665..7d03013 100644 --- a/src/api/routes/floods/archive/index.js +++ b/src/api/routes/floods/archive/index.js @@ -34,7 +34,8 @@ export default ({config, db, logger}) => { start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ') .min(Joi.ref('start')).required(), - city: Joi.alternatives().try(Joi.string(), Joi.any().valid(null)).default(null, 'city'), + city: Joi.alternatives().try(Joi.string(), + Joi.any().valid(null)).default(null, 'city'), }, }), (req, res, next) => { @@ -54,7 +55,8 @@ export default ({config, db, logger}) => { ]}}); return; } - archive(config, db, logger).maxstate(req.query.start, req.query.end, req.query.city) + archive(config, db, logger).maxstate(req.query.start, req.query.end, + req.query.city) .then((data) => res.status(200).json({statusCode: 200, result: data})) .catch((err) => { /* istanbul ignore next */ diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index 5ec92ea..e66c071 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -87,7 +87,8 @@ export default ({config, db, logger}) => { message: 'format must be \'json\' when geoformat ' +'IN (\'geojson\',\'topojson\')'}); } else { - floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) + floods(config, db, logger).allGeo(req.query.city, + req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? // If CAP format has been required convert to geojson then to CAP diff --git a/src/api/routes/floods/timeseries/index.js b/src/api/routes/floods/timeseries/index.js index e92ddd7..0d9a187 100644 --- a/src/api/routes/floods/timeseries/index.js +++ b/src/api/routes/floods/timeseries/index.js @@ -34,7 +34,8 @@ export default ({config, db, logger}) => { start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ') .min(Joi.ref('start')).required(), - city: Joi.alternatives().try(Joi.string(), Joi.any().valid(null)).default(null, 'city'), + city: Joi.alternatives().try(Joi.string(), + Joi.any().valid(null)).default(null, 'city'), }, }), (req, res, next) => { @@ -55,7 +56,8 @@ export default ({config, db, logger}) => { return; } - timeseries(config, db, logger).count(req.query.start, req.query.end, req.query.city) + timeseries(config, db, logger) + .count(req.query.start, req.query.end, req.query.city) .then((data) => res.status(200).json({statusCode: 200, result: data})) .catch((err) => { /* istanbul ignore next */ From 9d11bb042ef432ee0000b865676e2fc4abf7ef44 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 13:30:46 -0400 Subject: [PATCH 10/15] updated time stamp validation for endpoints --- src/api/routes/cards/index.js | 7 ++++-- src/api/routes/feeds/index.js | 8 ++++--- src/api/routes/reports/archive/index.js | 1 - src/test/testCards.js | 32 +++++++++++++++++++++++-- src/test/testFeeds.js | 8 +++---- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index bb93b93..c053411 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -14,7 +14,9 @@ import {cacheResponse, handleResponse} from '../../../lib/util'; import Notify from '../../../lib/notify'; // Import validation dependencies -import Joi from 'joi'; +import BaseJoi from 'joi'; +import Extension from 'joi-date-extensions'; +const Joi = BaseJoi.extend(Extension); import validate from 'celebrate'; // Import image upload capabilities @@ -130,7 +132,8 @@ export default ({config, db, logger}) => { }), text: Joi.string().allow(''), image_url: Joi.string().allow(''), - created_at: Joi.date().iso().required(), + created_at: Joi.alternatives(Joi.date().format('YYYY-MM-DDTHH:mm:ssZ'), + Joi.date().format('YYYY-MM-DDTHH:mm:ss.SSSZ')).required(), location: Joi.object().required().keys({ lat: Joi.number().min(-90).max(90).required(), lng: Joi.number().min(-180).max(180).required(), diff --git a/src/api/routes/feeds/index.js b/src/api/routes/feeds/index.js index 6c96677..797c275 100644 --- a/src/api/routes/feeds/index.js +++ b/src/api/routes/feeds/index.js @@ -8,7 +8,9 @@ import feeds from './model'; // Import validation dependencies -import Joi from 'joi'; +import BaseJoi from 'joi'; +import Extension from 'joi-date-extensions'; +const Joi = BaseJoi.extend(Extension); import validate from 'celebrate'; /** @@ -28,7 +30,7 @@ export default ({config, db, logger}) => { api.post('/qlue', validate({ body: Joi.object().keys({ post_id: Joi.number().integer().required(), - created_at: Joi.date().iso().required(), + created_at: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), title: Joi.string().allow(''), text: Joi.string().allow('').required(), image_url: Joi.string(), @@ -57,7 +59,7 @@ export default ({config, db, logger}) => { api.post('/detik', validate({ body: Joi.object().keys({ contribution_id: Joi.number().integer().required(), - created_at: Joi.date().iso().required(), + created_at: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), title: Joi.string().allow(''), text: Joi.string().allow('').required(), url: Joi.string().allow(''), diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 8613323..cb12f66 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -14,7 +14,6 @@ import {cacheResponse, handleGeoResponse} from '../../../../lib/util'; import BaseJoi from 'joi'; import Extension from 'joi-date-extensions'; const Joi = BaseJoi.extend(Extension); - import validate from 'celebrate'; /** * Methods to get historic flood reports from database diff --git a/src/test/testCards.js b/src/test/testCards.js index fdcae19..0b35584 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -15,7 +15,7 @@ import * as test from 'unit.js'; export default function(app, createdAt) { // Cards endpoint describe('Cards endpoint', function() { - // Cards + // Cards 404 error handling it('Return 404 if card requested without ID (GET /cards)', function(done) { test.httpAgent(app) .get('/cards') @@ -30,7 +30,7 @@ export default function(app, createdAt) { }); }); - // Can get reports + // Cards 400 card ID error handling it('Return 400 if card ID is invalid (GET /cards/:id)', function(done) { test.httpAgent(app) .get('/cards/1') @@ -104,6 +104,34 @@ export default function(app, createdAt) { }); }); + // Request a card, catch time zone error + it('Put card data', function(done) { + test.httpAgent(app) + .put('/cards/'+cardId) + .send({ + 'disaster_type': 'flood', + 'card_data': { + 'flood_depth': 20, + 'report_type': 'flood', + }, + 'text': 'integration testing', + 'created_at': '2017-11-01T00:00', + 'location': { + 'lat': -6.4, + 'lng': 106.6, + }, + }) + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } else { + done(); + } + }); + }); + // Request a card, submit and get resulting report it('Put card data', function(done) { test.httpAgent(app) diff --git a/src/test/testFeeds.js b/src/test/testFeeds.js index d93c9ed..a2fd4aa 100644 --- a/src/test/testFeeds.js +++ b/src/test/testFeeds.js @@ -20,7 +20,7 @@ export default function(app) { .post('/feeds/qlue') .send({ 'post_id': '9999', - 'created_at': '2017-06-07T14:32+0700', + 'created_at': '2017-06-07T14:32:00+0700', 'qlue_city': 'surabaya', 'disaster_type': 'flood', 'text': 'test report', @@ -46,7 +46,7 @@ export default function(app) { .post('/feeds/qlue') .send({ 'post_id': '9999', - 'created_at': '2017-06-07T14:32+0700', + 'created_at': '2017-06-07T14:32:00+0700', 'qlue_city': 'surabaya', 'disaster_type': 'flood', 'text': 'test report', @@ -73,7 +73,7 @@ export default function(app) { .post('/feeds/detik') .send({ 'contribution_id': '9999', - 'created_at': '2017-06-07T14:32+0700', + 'created_at': '2017-06-07T14:32:00+0700', 'disaster_type': 'flood', 'location': { 'latitude': 45, @@ -98,7 +98,7 @@ export default function(app) { .post('/feeds/detik') .send({ 'contribution_id': '9999', - 'created_at': '2017-06-07T14:32+0700', + 'created_at': '2017-06-07T14:32:00+0700', 'disaster_type': 'flood', 'location': { 'latitude': 45, From b7a1b35650d1ce79487de8741b6270b2e896483b Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 26 Jun 2018 14:00:00 -0400 Subject: [PATCH 11/15] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6dc684..d6073e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,3 +56,5 @@ API Server for CogniCity ### v3.1.0 * Add city query parameter to /floods/timeseries and /floods/archive endpoints +* Add time zone validation to all endpoints accepting timestamps +* Notifications of received reports are now run from the server (added for testing in v3.0.6) \ No newline at end of file From c4028fb09f920cde02aed50bfa0c277a7ca7428d Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 27 Jun 2018 11:18:22 -0400 Subject: [PATCH 12/15] add deployment for India --- .travis.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0b8233f..186063c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ deploy: #build the docs for dev branch only zip_file: dist.zip on: branch: dev - - provider: elasticbeanstalk # deploy the dev us server + - provider: elasticbeanstalk # deploy the prod us server access_key_id: $AWS_ACCESS_KEY_ID secret_access_key: $AWS_SECRET_ACCESS_KEY region: $AWS_EB_REGION_US @@ -70,4 +70,15 @@ deploy: #build the docs for dev branch only skip_cleanup: true zip_file: dist.zip on: - branch: master \ No newline at end of file + branch: master + - provider: elasticbeanstalk # deploy the dev in server + access_key_id: $AWS_ACCESS_KEY_ID + secret_access_key: $AWS_SECRET_ACCESS_KEY + region: $AWS_EB_REGION_IN + app: $AWS_EB_APP_IN + env: $AWS_EB_APP_ENV_IN_DEV + bucket_name: $AWS_EB_APP_BUCKET_IN_DEV + skip_cleanup: true + zip_file: dist.zip + on: + branch: dev \ No newline at end of file From fa9c39811f2bd1a200429fe6a9758823428bddd1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 27 Jun 2018 11:40:42 -0400 Subject: [PATCH 13/15] update swagger for india, standardise paths --- apigw/{pb => id}/swagger.json | 0 apigw/rm/swagger.json | 335 ++++++++-------------------------- apigw/{br => us}/swagger.json | 0 3 files changed, 73 insertions(+), 262 deletions(-) rename apigw/{pb => id}/swagger.json (100%) rename apigw/{br => us}/swagger.json (100%) diff --git a/apigw/pb/swagger.json b/apigw/id/swagger.json similarity index 100% rename from apigw/pb/swagger.json rename to apigw/id/swagger.json diff --git a/apigw/rm/swagger.json b/apigw/rm/swagger.json index 78c1fbf..5dc5e82 100644 --- a/apigw/rm/swagger.json +++ b/apigw/rm/swagger.json @@ -4,7 +4,7 @@ "version": "2017-05-11T05:43:00Z", "title": "cognicity" }, - "host": "data.riskmap.in", + "host": "data-dev.riskmap.in", "schemes": [ "https" ], @@ -23,12 +23,12 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in", "responses": { "default": { "statusCode": "200" } }, - "uri": "https://${stageVariables.env}.riskmap.in", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "type": "http_proxy" @@ -54,12 +54,12 @@ } ], "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/cards", "responses": { "default": { "statusCode": "200" } }, - "uri": "https://${stageVariables.env}.riskmap.in/cards", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "type": "http_proxy" @@ -102,10 +102,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -148,10 +148,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } }, @@ -213,6 +213,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/cards/{cardId}", "responses": { "400": { "statusCode": "400", @@ -248,7 +249,6 @@ "requestParameters": { "integration.request.path.cardId": "method.request.path.cardId" }, - "uri": "https://${stageVariables.env}.riskmap.in/cards/{cardId}", "passthroughBehavior": "when_no_match", "httpMethod": "ANY", "type": "http" @@ -288,6 +288,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/cards/{cardId}/images", "responses": { "default": { "statusCode": "200", @@ -300,7 +301,6 @@ "integration.request.path.cardId": "method.request.path.cardId", "integration.request.header.content-type": "method.request.header.content-type" }, - "uri": "https://${stageVariables.env}.riskmap.in/cards/{cardId}/images", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "type": "http" @@ -343,10 +343,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -379,12 +379,12 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/cities", "responses": { "default": { "statusCode": "200" } }, - "uri": "https://${stageVariables.env}.riskmap.in/cities", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "cacheNamespace": "h1la88", @@ -432,10 +432,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -478,10 +478,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -510,6 +510,7 @@ } ], "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/feeds/qlue", "responses": { "default": { "statusCode": "200", @@ -518,7 +519,6 @@ } } }, - "uri": "https://${stageVariables.env}.riskmap.in/feeds/qlue", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "type": "http" @@ -561,10 +561,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -608,6 +608,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/floodgauges", "responses": { "default": { "statusCode": "200", @@ -621,7 +622,6 @@ "integration.request.querystring.geoformat": "method.request.querystring.geoformat", "integration.request.querystring.format": "method.request.querystring.format" }, - "uri": "https://${stageVariables.env}.riskmap.in/floodgauges", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "cacheNamespace": "so358j", @@ -670,10 +670,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -716,10 +716,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } }, @@ -740,6 +740,7 @@ ], "responses": {}, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/floodgauges/{id}", "responses": { "default": { "statusCode": "200" @@ -748,7 +749,6 @@ "requestParameters": { "integration.request.path.id": "method.request.path.id" }, - "uri": "https://${stageVariables.env}.riskmap.in/floodgauges/{id}", "passthroughBehavior": "when_no_match", "httpMethod": "ANY", "cacheNamespace": "p829iq", @@ -808,6 +808,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/floods/", "responses": { "default": { "statusCode": "200", @@ -823,7 +824,6 @@ "integration.request.querystring.minimum_state": "method.request.querystring.minimum_state", "integration.request.querystring.format": "method.request.querystring.format" }, - "uri": "https://${stageVariables.env}.riskmap.in/floods/", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "cacheNamespace": "nvy9s5", @@ -857,10 +857,10 @@ "statusCode": "200" } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } }, @@ -901,10 +901,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -948,6 +948,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/floods/states", "responses": { "default": { "statusCode": "200", @@ -961,7 +962,6 @@ "integration.request.querystring.minimum_state": "method.request.querystring.minimum_state", "integration.request.querystring.format": "method.request.querystring.format" }, - "uri": "https://${stageVariables.env}.riskmap.in/floods/states", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "cacheNamespace": "cxp4qo", @@ -1010,10 +1010,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -1056,10 +1056,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } }, @@ -1080,6 +1080,7 @@ ], "responses": {}, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/floods/{id}", "responses": { "default": { "statusCode": "200" @@ -1088,7 +1089,6 @@ "requestParameters": { "integration.request.path.id": "method.request.path.id" }, - "uri": "https://${stageVariables.env}.riskmap.in/floods/{id}", "passthroughBehavior": "when_no_match", "httpMethod": "ANY", "cacheNamespace": "8q9uet", @@ -1138,10 +1138,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } @@ -1184,10 +1184,10 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } }, @@ -1220,6 +1220,7 @@ ], "responses": {}, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/infrastructure/{type}", "responses": { "default": { "statusCode": "200" @@ -1228,7 +1229,6 @@ "requestParameters": { "integration.request.path.type": "method.request.path.type" }, - "uri": "https://${stageVariables.env}.riskmap.in/infrastructure/{type}", "passthroughBehavior": "when_no_match", "httpMethod": "ANY", "cacheNamespace": "qtdnlp", @@ -1287,6 +1287,7 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/reports", "responses": { "default": { "statusCode": "200", @@ -1301,7 +1302,6 @@ "integration.request.querystring.timeperiod": "method.request.querystring.timeperiod", "integration.request.querystring.format": "method.request.querystring.format" }, - "uri": "https://${stageVariables.env}.riskmap.in/reports", "passthroughBehavior": "when_no_match", "httpMethod": "GET", "cacheNamespace": "8vxlp8", @@ -1351,19 +1351,16 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } }, - "/reports/{id+}": { - "options": { - "consumes": [ - "application/json" - ], + "/reports/archive": { + "get": { "produces": [ "application/json" ], @@ -1372,76 +1369,21 @@ "description": "200 response", "schema": { "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Headers": { - "type": "string" - } } } }, "x-amazon-apigateway-integration": { - "responses": { - "default": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", - "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", - "method.response.header.Access-Control-Allow-Origin": "'*'" - } - } - }, - "passthroughBehavior": "when_no_match", - "requestTemplates": { - "application/json": "{\"statusCode\": 200}" - }, - "type": "mock" - } - }, - "x-amazon-apigateway-any-method": { - "parameters": [ - { - "name": "format", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "id", - "in": "path", - "required": true, - "type": "string" - } - ], - "responses": {}, - "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/reports/archive", "responses": { "default": { "statusCode": "200" } }, - "requestParameters": { - "integration.request.path.id": "method.request.path.id" - }, - "uri": "https://${stageVariables.env}.riskmap.in/reports/{id}", "passthroughBehavior": "when_no_match", - "httpMethod": "ANY", - "cacheNamespace": "5tep32", - "cacheKeyParameters": [ - "method.request.path.id", - "method.request.querystring.format" - ], + "httpMethod": "GET", "type": "http_proxy" } - } - }, - "/stats": { + }, "options": { "consumes": [ "application/json" @@ -1479,15 +1421,15 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } }, - "/stats/floodedRWsSummary": { + "/reports/timeseries": { "get": { "produces": [ "application/json" @@ -1497,28 +1439,19 @@ "description": "200 response", "schema": { "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } } } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.us/reports/timeseries", "responses": { "default": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - } + "statusCode": "200" } }, - "uri": "arn:aws:apigateway:ap-south-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-south-1:917524458155:function:cognicity-statistics-${stageVariables.stage}-floodedRWsSummary/invocations", "passthroughBehavior": "when_no_match", - "httpMethod": "POST", - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws_proxy" + "httpMethod": "GET", + "type": "http_proxy" } }, "options": { @@ -1552,103 +1485,32 @@ "default": { "statusCode": "200", "responseParameters": { - "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'", - "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", + "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'", + "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'", "method.response.header.Access-Control-Allow-Origin": "'*'" } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } } }, - "/stats/floodedRegionsSummary": { + "/{id}": { "get": { "produces": [ "application/json" ], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } - } - }, - "x-amazon-apigateway-integration": { - "responses": { - "default": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - } - } - }, - "uri": "arn:aws:apigateway:ap-south-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-south-1:917524458155:function:cognicity-statistics-${stageVariables.stage}-floodedRegionsSummary/invocations", - "passthroughBehavior": "when_no_match", - "httpMethod": "POST", - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws_proxy" - } - }, - "options": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Headers": { - "type": "string" - } - } + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" } - }, - "x-amazon-apigateway-integration": { - "responses": { - "default": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Methods": "'GET,OPTIONS'", - "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", - "method.response.header.Access-Control-Allow-Origin": "'*'" - } - } - }, - "passthroughBehavior": "when_no_match", - "requestTemplates": { - "application/json": "{\"statusCode\": 200}" - }, - "type": "mock" - } - } - }, - "/stats/reportsSummary": { - "get": { - "produces": [ - "application/json" ], "responses": { "200": { @@ -1659,16 +1521,18 @@ } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/reports/{id}", "responses": { "default": { "statusCode": "200" } }, - "uri": "arn:aws:apigateway:ap-south-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-south-1:917524458155:function:cognicity-statistics-${stageVariables.stage}-reportsSummary/invocations", + "requestParameters": { + "integration.request.path.id": "method.request.path.id" + }, "passthroughBehavior": "when_no_match", - "httpMethod": "POST", - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws_proxy" + "httpMethod": "GET", + "type": "http_proxy" } }, "options": { @@ -1708,99 +1572,46 @@ } } }, - "passthroughBehavior": "when_no_match", "requestTemplates": { "application/json": "{\"statusCode\": 200}" }, + "passthroughBehavior": "when_no_match", "type": "mock" } - } - }, - "/twilio": { - "post": { - "consumes": [ - "application/x-www-form-urlencoded" - ], - "produces": [ - "text/xml" - ], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } - } - }, - "x-amazon-apigateway-integration": { - "responses": { - ".*": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - }, - "responseTemplates": { - "text/xml": "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n$errorMessageObj.message\n" - } - } - }, - "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:twilio/invocations", - "passthroughBehavior": "when_no_templates", - "httpMethod": "POST", - "requestTemplates": { - "application/x-www-form-urlencoded": "{\n \"reqbody\":\"$input.path('$')\"\n}" - }, - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws" - } }, - "options": { - "consumes": [ - "application/json" - ], + "patch": { "produces": [ "application/json" ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], "responses": { "200": { "description": "200 response", "schema": { "$ref": "#/definitions/Empty" - }, - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Headers": { - "type": "string" - } } } }, "x-amazon-apigateway-integration": { + "uri": "https://${stageVariables.env}.riskmap.in/reports/{id}", "responses": { "default": { - "statusCode": "200", - "responseParameters": { - "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'", - "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'", - "method.response.header.Access-Control-Allow-Origin": "'*'" - } + "statusCode": "200" } }, - "passthroughBehavior": "when_no_match", - "requestTemplates": { - "application/json": "{\"statusCode\": 200}" + "requestParameters": { + "integration.request.path.id": "method.request.path.id" }, - "type": "mock" + "passthroughBehavior": "when_no_match", + "httpMethod": "PATCH", + "type": "http_proxy" } } } diff --git a/apigw/br/swagger.json b/apigw/us/swagger.json similarity index 100% rename from apigw/br/swagger.json rename to apigw/us/swagger.json From 67fab4db9d34ed03b79511828efda6db0f6f5f8c Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 9 Jul 2018 12:02:39 -0400 Subject: [PATCH 14/15] add prod build for India --- .travis.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 186063c..141aee8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,4 +81,15 @@ deploy: #build the docs for dev branch only skip_cleanup: true zip_file: dist.zip on: - branch: dev \ No newline at end of file + branch: dev + - provider: elasticbeanstalk # deploy the prod in server + access_key_id: $AWS_ACCESS_KEY_ID + secret_access_key: $AWS_SECRET_ACCESS_KEY + region: $AWS_EB_REGION_IN + app: $AWS_EB_APP_IN + env: $AWS_EB_APP_ENV_IN_PROD + bucket_name: $AWS_EB_APP_BUCKET_IN_PROD + skip_cleanup: true + zip_file: dist.zip + on: + branch: master \ No newline at end of file From b61dea27749d931b383e0d737db294485adaa602 Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 9 Jul 2018 12:07:47 -0400 Subject: [PATCH 15/15] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6073e6..20aad1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,4 +57,5 @@ API Server for CogniCity ### v3.1.0 * Add city query parameter to /floods/timeseries and /floods/archive endpoints * Add time zone validation to all endpoints accepting timestamps -* Notifications of received reports are now run from the server (added for testing in v3.0.6) \ No newline at end of file +* Notifications of received reports are now run from the server (added for testing in v3.0.6) +* Add deployments for India