From 0bc0c1dbe8535eb13aa2ed15e8f6ba2048dbbaa3 Mon Sep 17 00:00:00 2001 From: Abraham Quintero Date: Tue, 17 Jan 2017 16:13:42 +0530 Subject: [PATCH 001/160] added region codes for chennai to sample.env --- sample.env | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sample.env b/sample.env index 258aced..628f0b2 100644 --- a/sample.env +++ b/sample.env @@ -7,3 +7,6 @@ DB_PASSWORD=db_password_goes_here DB_NAME=cognicity LOG_DIR=/tmp/cognicity LOG_LEVEL=debug + +#chennai changes: +REGION_CODES=chn From ce97b84a7885dc31ef2e85584529a0b7b08f1df3 Mon Sep 17 00:00:00 2001 From: MaanasaPriyaa Date: Tue, 17 Jan 2017 16:47:30 +0530 Subject: [PATCH 002/160] Fixed timezone --- src/api/routes/reports/model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 27865af..fa5bf3a 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -7,7 +7,7 @@ export default (config, db, logger) => ({ all: (city) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT pkey, created_at at time zone 'ICT' created_at, source, + let query = `SELECT pkey, created_at at time zone 'IST' created_at, source, status, url, image_url, disaster_type, report_data, tags, title, text, the_geom FROM ${config.TABLE_REPORTS} WHERE created_at >= to_timestamp($1) From 6da384dd6736bd05eb840b511218a4426d0329bc Mon Sep 17 00:00:00 2001 From: Abraham Quintero Date: Wed, 18 Jan 2017 12:57:42 +0530 Subject: [PATCH 003/160] server signedUrl --- package.json | 1 + src/api/routes/cards/index.js | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/package.json b/package.json index df3d99f..164c100 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dependencies": { "api-error-handler": "^1.0.0", "apicache": "^0.5.1", + "aws-sdk": "^2.7.24", "bluebird": "^3.4.7", "body-parser": "^1.15.2", "celebrate": "^4.0.1", diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index cd1ad41..8251035 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -13,6 +13,16 @@ import validate from 'celebrate'; // Import ID generator import shortid from 'shortid'; +// Import image upload capabilities +import AWS from 'aws-sdk'; +var s3 = new AWS.S3( + { + accessKeyId : process.env.accessKeyId || '' , + secretAccessKey : process.env.secretAccessKey || '' + }); + + + // Caching import apicache from 'apicache'; const CACHE_GROUP_CARDS = '/cards'; @@ -125,6 +135,32 @@ export default ({ config, db, logger }) => { } ); + //Gives an s3 signed url for the frontend to upload an image to + api.get('/:cardId/images', validate({ + params: { cardId: Joi.string().min(7).max(14).required() } + }), + (req, res, next) => { + let s3params = { + Bucket: 'mapchennai', + Key: req.params.cardId + ".png", + ContentType:req.query.file_type + }; + s3.getSignedUrl('putObject', s3params, (err, data) => { + if (err){ + logger.error('could not get signed url from S3'); + logger.error(err); + } else { + var returnData = { + signedRequest : data, + url: "https://"+s3params.Bucket + ".s3.amazonaws.com/" + s3params.Key + }; + logger.debug( "s3 signed request: " + returnData.signedRequest); + res.write(JSON.stringify(returnData)); + res.end(); + } + }); + }); + // Update a card report with new details including the image URL api.patch('/:cardId', jwtCheck, validate({ params: { cardId: Joi.string().min(7).max(14).required() }, From e2a0582998cd3dbeaaa3fa9ffd228b17f71eee14 Mon Sep 17 00:00:00 2001 From: Abraham Quintero Date: Wed, 18 Jan 2017 18:11:33 +0530 Subject: [PATCH 004/160] photo upload working --- src/.index.js.swp | Bin 0 -> 20480 bytes src/api/routes/cards/.index.js.swp | Bin 0 -> 16384 bytes src/api/routes/cards/.model.js.swp | Bin 0 -> 16384 bytes src/api/routes/cards/index.js | 38 +++++++++++++++++++++++------ 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 src/.index.js.swp create mode 100644 src/api/routes/cards/.index.js.swp create mode 100644 src/api/routes/cards/.model.js.swp diff --git a/src/.index.js.swp b/src/.index.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..e63985206679acd3894e58c287e873c3f0991509 GIT binary patch literal 20480 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 O)$%>B$<0UfasCPS@Hw&o literal 0 HcmV?d00001 diff --git a/src/api/routes/cards/.index.js.swp b/src/api/routes/cards/.index.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..efbc1c51bb4a33ae5e3afe6441d4a73f47b1cfd1 GIT binary patch literal 16384 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 UGi8Nj=ICPi(#ku2eSPVF0yO2T7ytkO literal 0 HcmV?d00001 diff --git a/src/api/routes/cards/.model.js.swp b/src/api/routes/cards/.model.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..9fb8c6b75f132ff58642afac7743d5b80268dfbd GIT binary patch literal 16384 zcmeI2PmJ7F9mk#UC!LT2Egay2ehnjyM{+#b-H=qf1T;JAhC#D4tMN=g3TWc7-^@Dk z*i--6-C$Jk6XFWufCQKJg2=g2i-d%Pgg|`Xd$u>5Y!U)R zpz4kES=;ab`}+6$y=Sk-=dZRm>5{pk;rC%p`@>gvzxdqECr-Tm0Zntpv9s;Wp5<8& z`ZZ;_zLBXd|LDTc;c?(d-Ch|=f~ou zKv6DFRtKekQa~x76i^B%1(X6x0i}RaKq;UUPzopo{tp#!JWYEN<9eGv$m9S2#qis;2U5Aw7@d>+Xpr6SKvqBdGJke30wqqa2ovXaZP&}d=GpZd<)zF=RpH} z0{r%jru_u;!D;Yke0T6Vcm<^3OP~k3;M3qNcnW+J{N=Q!{Q}$qUjZ3N!C7#-g7Jf2 zgV(?-U>}&^jmI?YyI>Vu01cpl-=D&K!OP%hU=Lga>);7+20R8%fj99Ya;OSf69p|wdAlOC{C_Ds=4oM5zd z3Zm(h#iqx`+4KRUT$5w!MAM=wIv`OC)p;LZ29*Q;ry^&zO4o>gY+~|1Bi}Qq*0;8L z7_l6!T_aJ4*SOm33@w}V>XJd#<@$9)w1W)2tju=snae0$%o!J~QV}Aq()C=0c6ZRD z>b@I=6Mt$Bn(G~Fbfw?4w?>`b6)`L>Yvmwsc^r8QGi7_J0WERr&zvb6WpPl9Nu=&N zDT_xQn?vo-(mqXsDDvd>s;qZsi33`tFbe|N#eGDO6Z^vFtiC1GfiQHLs!PR`4+nCY zTD@k+vRjs3yEEUP=tGIJ_ebKBr z1om)!vppDC8~?*taJ-`yaR3Gni*T90!?>dk`a_HE>%ISqE47;a4f{R;$&DF2#Tz*Y zOEnQ}YB*`}m=@2NhYJyw_&0%kB`||eF@O!pic+dKumigL@gQWDG-)xV4y5)|BKqbZAKG9DJvKXf@LLK&afBZaXf8_)doKMZ3{&%=UESjyrB zUqSDUXM8#55s75j6eYMk+zKNc9G4&ba2hP$!5D`P2(~G`xLa;k43hTqkl=Uk{-Lb6 zCYOYKAH(+ue94fl@)bj@L~T!4Yq@mDY+kt1X(Kk!(skZWCHJ{vGlBZmB~i$FW9yk z8JqG{nq!SSk4P0>M77ewMPhjc_bwN9kyBpDW&B1Gg`;ui2i_JCq- z9dQ+reI!M+*=k}skh6;+i*aBF(zdDF8&G%H>CgaKm|L;DFCg`G+v-@Y0l7s5IH;N7 z6XVdC!6RKWWa(}c-{hx(6HYU1rucagIC;P29TuL78$4dLVAgFf-x9RFN7)Ya<>ot? zGuNN^t}rDUFUa@a9Zr#l;6sf?;4fvC%Rs60#0e61Uq3p!xkik}PEp3|I`yr=u;1m5 zF~7+c($x;b@?rO7&xg0SxVhZmj~L5CLvXnS-eVKewo8kUDm;o^w1hX{fr;-3xpn8E zR_qdau$^Kd5A(>QX>5;gC9!*%I9ZS)IEcpwgS@ekKN*S6^?gnAXQF7nRiz2GtW+E0 z5pL|qUTsa|&;Jgd=YI(J^FJPA#g9M#{}Q|iZh;p7FY^PSeo6tQfKosypcGIFC { (req, res, next) => { let s3params = { Bucket: 'mapchennai', - Key: req.params.cardId + ".png", + Key: 'resized/' + req.params.cardId + ".png", ContentType:req.query.file_type }; s3.getSignedUrl('putObject', s3params, (err, data) => { @@ -152,11 +154,33 @@ export default ({ config, db, logger }) => { } else { var returnData = { signedRequest : data, - url: "https://"+s3params.Bucket + ".s3.amazonaws.com/" + s3params.Key + url: 'https://s3.ap-south-1.amazonaws.com/mapchennai/' + s3params.Key }; - logger.debug( "s3 signed request: " + returnData.signedRequest); - res.write(JSON.stringify(returnData)); - res.end(); + //write the url into the db under image_url for this card + + cards(config, db, logger).byCardId(req.params.cardId) + .then((card) => { + if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'` }) + else { + // Try and submit the report and update the card + cards(config, db, logger).updateReport(card, {image_url: returnData.url}) + .then((data) => { + console.log(data); + clearCache(); + logger.debug( "s3 signed request: " + returnData.signedRequest); + res.write(JSON.stringify(returnData)); + res.end(); + //res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); + }) + .catch((err) => { + logger.error(err); + next(err); + }) + } + + + }) } }); }); @@ -201,4 +225,4 @@ export default ({ config, db, logger }) => { ); return api; -} \ No newline at end of file +} From b227b88d5cbca164a5d04d7679291daf3790072b Mon Sep 17 00:00:00 2001 From: MaanasaPriyaa Date: Thu, 16 Feb 2017 16:14:55 -0500 Subject: [PATCH 005/160] Added jshintrc file --- .jshintrc | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..78cfecf --- /dev/null +++ b/.jshintrc @@ -0,0 +1,6 @@ +{ + "ignoreDelimiters": [ + { "start": "start-non-standard", "end": "end-non-standard" } + ], + "esversion":6 +} From 49740d04d7133878aed664984a8f85d0eb142f0d Mon Sep 17 00:00:00 2001 From: MaanasaPriyaa Date: Sun, 12 Mar 2017 21:59:53 -0400 Subject: [PATCH 006/160] Removed swp files --- src/api/routes/cards/.index.js.swp | Bin 16384 -> 0 bytes src/api/routes/cards/.model.js.swp | Bin 16384 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/api/routes/cards/.index.js.swp delete mode 100644 src/api/routes/cards/.model.js.swp diff --git a/src/api/routes/cards/.index.js.swp b/src/api/routes/cards/.index.js.swp deleted file mode 100644 index efbc1c51bb4a33ae5e3afe6441d4a73f47b1cfd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 UGi8Nj=ICPi(#ku2eSPVF0yO2T7ytkO diff --git a/src/api/routes/cards/.model.js.swp b/src/api/routes/cards/.model.js.swp deleted file mode 100644 index 9fb8c6b75f132ff58642afac7743d5b80268dfbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI2PmJ7F9mk#UC!LT2Egay2ehnjyM{+#b-H=qf1T;JAhC#D4tMN=g3TWc7-^@Dk z*i--6-C$Jk6XFWufCQKJg2=g2i-d%Pgg|`Xd$u>5Y!U)R zpz4kES=;ab`}+6$y=Sk-=dZRm>5{pk;rC%p`@>gvzxdqECr-Tm0Zntpv9s;Wp5<8& z`ZZ;_zLBXd|LDTc;c?(d-Ch|=f~ou zKv6DFRtKekQa~x76i^B%1(X6x0i}RaKq;UUPzopo{tp#!JWYEN<9eGv$m9S2#qis;2U5Aw7@d>+Xpr6SKvqBdGJke30wqqa2ovXaZP&}d=GpZd<)zF=RpH} z0{r%jru_u;!D;Yke0T6Vcm<^3OP~k3;M3qNcnW+J{N=Q!{Q}$qUjZ3N!C7#-g7Jf2 zgV(?-U>}&^jmI?YyI>Vu01cpl-=D&K!OP%hU=Lga>);7+20R8%fj99Ya;OSf69p|wdAlOC{C_Ds=4oM5zd z3Zm(h#iqx`+4KRUT$5w!MAM=wIv`OC)p;LZ29*Q;ry^&zO4o>gY+~|1Bi}Qq*0;8L z7_l6!T_aJ4*SOm33@w}V>XJd#<@$9)w1W)2tju=snae0$%o!J~QV}Aq()C=0c6ZRD z>b@I=6Mt$Bn(G~Fbfw?4w?>`b6)`L>Yvmwsc^r8QGi7_J0WERr&zvb6WpPl9Nu=&N zDT_xQn?vo-(mqXsDDvd>s;qZsi33`tFbe|N#eGDO6Z^vFtiC1GfiQHLs!PR`4+nCY zTD@k+vRjs3yEEUP=tGIJ_ebKBr z1om)!vppDC8~?*taJ-`yaR3Gni*T90!?>dk`a_HE>%ISqE47;a4f{R;$&DF2#Tz*Y zOEnQ}YB*`}m=@2NhYJyw_&0%kB`||eF@O!pic+dKumigL@gQWDG-)xV4y5)|BKqbZAKG9DJvKXf@LLK&afBZaXf8_)doKMZ3{&%=UESjyrB zUqSDUXM8#55s75j6eYMk+zKNc9G4&ba2hP$!5D`P2(~G`xLa;k43hTqkl=Uk{-Lb6 zCYOYKAH(+ue94fl@)bj@L~T!4Yq@mDY+kt1X(Kk!(skZWCHJ{vGlBZmB~i$FW9yk z8JqG{nq!SSk4P0>M77ewMPhjc_bwN9kyBpDW&B1Gg`;ui2i_JCq- z9dQ+reI!M+*=k}skh6;+i*aBF(zdDF8&G%H>CgaKm|L;DFCg`G+v-@Y0l7s5IH;N7 z6XVdC!6RKWWa(}c-{hx(6HYU1rucagIC;P29TuL78$4dLVAgFf-x9RFN7)Ya<>ot? zGuNN^t}rDUFUa@a9Zr#l;6sf?;4fvC%Rs60#0e61Uq3p!xkik}PEp3|I`yr=u;1m5 zF~7+c($x;b@?rO7&xg0SxVhZmj~L5CLvXnS-eVKewo8kUDm;o^w1hX{fr;-3xpn8E zR_qdau$^Kd5A(>QX>5;gC9!*%I9ZS)IEcpwgS@ekKN*S6^?gnAXQF7nRiz2GtW+E0 z5pL|qUTsa|&;Jgd=YI(J^FJPA#g9M#{}Q|iZh;p7FY^PSeo6tQfKosypcGIFC Date: Sat, 29 Apr 2017 17:14:11 +1000 Subject: [PATCH 007/160] syntax fixes --- src/api/index.js | 2 +- src/api/routes/cards/index.js | 24 ++++++++++++------------ src/api/routes/cards/model.js | 22 +++++++++++----------- src/api/routes/cards/test.js | 2 +- src/api/routes/cities/index.js | 2 +- src/api/routes/cities/model.js | 2 +- src/api/routes/cities/test.js | 2 +- src/api/routes/feeds/index.js | 2 +- src/api/routes/feeds/model.js | 16 ++++++++-------- src/api/routes/feeds/test.js | 2 +- src/api/routes/floodgauges/index.js | 2 +- src/api/routes/floodgauges/model.js | 8 ++++---- src/api/routes/floodgauges/test.js | 2 +- src/api/routes/floods/index.js | 16 ++++++++-------- src/api/routes/floods/model.js | 26 +++++++++++++------------- src/api/routes/infrastructure/index.js | 2 +- src/api/routes/infrastructure/model.js | 4 ++-- src/api/routes/infrastructure/test.js | 2 +- src/api/routes/reports/index.js | 2 +- src/api/routes/reports/model.js | 6 +++--- src/api/routes/reports/test.js | 2 +- src/config.js | 4 ++-- src/index.js | 24 ++++++++++++++++-------- src/lib/cap.js | 4 ++-- src/lib/util.js | 18 +++++++++--------- 25 files changed, 103 insertions(+), 95 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 40e4e80..8e30875 100755 --- a/src/api/index.js +++ b/src/api/index.js @@ -49,4 +49,4 @@ export default ({ config, db, logger }) => { api.use(errorHandler()); return api; -} +}; diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index f594b0d..eb65710 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -20,7 +20,7 @@ const CACHE_GROUP_CARDS = '/cards'; // Function to clear out the cache const clearCache = () => { apicache.clear(CACHE_GROUP_CARDS); -} +}; export default ({ config, db, logger }) => { let api = Router(); @@ -42,7 +42,7 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } ); @@ -58,7 +58,7 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } ); @@ -74,7 +74,7 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } ); @@ -99,10 +99,10 @@ export default ({ config, db, logger }) => { .then((card) => { // If the card does not exist then return an error message if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }) + message: `No card exists with id '${req.params.cardId}'` }); // If the card already has received status then return an error message else if (card && card.received) res.status(409).json({ statusCode: 409, - cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'` }) + cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'` }); // We have a card and it has not yet had a report received else { // Try and submit the report and update the card @@ -115,9 +115,9 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } - }) + }); } catch(err) { logger.error(err); next(err); @@ -141,7 +141,7 @@ export default ({ config, db, logger }) => { .then((card) => { // If the card does not exist then return an error message if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }) + message: `No card exists with id '${req.params.cardId}'` }); // We have a card else { // Try and submit the report and update the card @@ -154,9 +154,9 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } - }) + }); } catch(err) { logger.error(err); next(err); @@ -165,4 +165,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index cda646a..4ee0f33 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -11,13 +11,13 @@ export default (config, db, logger) => ({ VALUES ($1, $2, $3, $4, $5) RETURNING pkey`; // Setup values - let values = [ cardId, body.username, body.network, body.language, false ] + let values = [ cardId, body.username, body.network, body.language, false ]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), // Return specific card by id @@ -36,13 +36,13 @@ export default (config, db, logger) => ({ LIMIT 1`; // Setup values - let values = [ cardId ] + let values = [ cardId ]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), // Add an entry to the reports table and then update the card record accordingly @@ -68,17 +68,17 @@ export default (config, db, logger) => ({ VALUES ($1, $2)`, values: [ card.card_id, 'REPORT SUBMITTED' ] } - ] + ]; // Log queries to debugger for (let query of queries) logger.debug(query.query, query.values); // Execute in a transaction as both INSERT and UPDATE must happen together db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))) + return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), // Update the reports table with new report details @@ -102,17 +102,17 @@ export default (config, db, logger) => ({ VALUES ($1, $2)`, values: [ card.card_id, 'REPORT UPDATES' ] } - ] + ]; // Log queries to debugger for (let query of queries) logger.debug(query.query, query.values); // Execute in a transaction as both INSERT and UPDATE must happen together db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))) + return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) -}); \ No newline at end of file +}); diff --git a/src/api/routes/cards/test.js b/src/api/routes/cards/test.js index 357a544..982fcd2 100644 --- a/src/api/routes/cards/test.js +++ b/src/api/routes/cards/test.js @@ -18,7 +18,7 @@ const tests = [ status: 200 } } -] +]; // Run the tests describe('GET /cards', () => { diff --git a/src/api/routes/cities/index.js b/src/api/routes/cities/index.js index 2bd9601..340e30e 100644 --- a/src/api/routes/cities/index.js +++ b/src/api/routes/cities/index.js @@ -31,4 +31,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/cities/model.js b/src/api/routes/cities/model.js index 1f3dbca..af738ea 100644 --- a/src/api/routes/cities/model.js +++ b/src/api/routes/cities/model.js @@ -13,7 +13,7 @@ export default (config, db, logger) => ({ logger.debug(query); db.any(query).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) }); diff --git a/src/api/routes/cities/test.js b/src/api/routes/cities/test.js index a253383..2738eba 100644 --- a/src/api/routes/cities/test.js +++ b/src/api/routes/cities/test.js @@ -11,7 +11,7 @@ const tests = [ exp: { status: 200 } - }] + }]; // Run the tests describe('GET /cities', () => { diff --git a/src/api/routes/feeds/index.js b/src/api/routes/feeds/index.js index b22cfba..396b8c5 100644 --- a/src/api/routes/feeds/index.js +++ b/src/api/routes/feeds/index.js @@ -64,4 +64,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index 72bce95..b547858 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -12,7 +12,7 @@ export default (config, db, logger) => ({ // Setup values let values = [ body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, - body.title, body.qlue_city, body.location.lng, body.location.lat ] + body.title, body.qlue_city, body.location.lng, body.location.lat ]; // Execute logger.debug(query, values); @@ -20,10 +20,10 @@ export default (config, db, logger) => ({ .then(() => resolve({ post_id: body.post_id, created: true })) .catch((err) => { if (err.constraint === 'reports_post_id_key') - resolve({ post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table` }) + resolve({ post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table` }); else - reject(err) - }) + reject(err); + }); }), // Add a detik report @@ -36,7 +36,7 @@ export default (config, db, logger) => ({ // Setup values let values = [ body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, - body.url, body.image_url, body.location.longitude, body.location.latitude ] + body.url, body.image_url, body.location.longitude, body.location.latitude ]; // Execute logger.debug(query, values); @@ -44,9 +44,9 @@ export default (config, db, logger) => ({ .then(() => resolve({ contribution_id: body.contribution_id, created: true })) .catch((err) => { if (err.constraint === 'reports_contribution_id_key') - resolve({ contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}) + resolve({ contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); else - reject(err) - }) + reject(err); + }); }) }); diff --git a/src/api/routes/feeds/test.js b/src/api/routes/feeds/test.js index 9cd6ad9..942b00c 100644 --- a/src/api/routes/feeds/test.js +++ b/src/api/routes/feeds/test.js @@ -13,7 +13,7 @@ const tests = [ status: 200 } } -] +]; // Run the tests describe('GET /feeds', () => { diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index cdca748..42f4ce5 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -49,4 +49,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/floodgauges/model.js b/src/api/routes/floodgauges/model.js index 589d3e0..6bf67b6 100644 --- a/src/api/routes/floodgauges/model.js +++ b/src/api/routes/floodgauges/model.js @@ -15,13 +15,13 @@ export default (config, db, logger) => ({ // Setup values let timeWindow = (Date.now() / 1000) - config.API_FLOODGAUGE_REPORTS_TIME_WINDOW; - let values = [ timeWindow, city, config.API_FLOODGAUGE_REPORTS_LIMIT ] + let values = [ timeWindow, city, config.API_FLOODGAUGE_REPORTS_LIMIT ]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), @@ -37,13 +37,13 @@ export default (config, db, logger) => ({ GROUP BY gaugeid, the_geom, gaugenameid`; // Setup values - let values = [ id ] + let values = [ id ]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) }); diff --git a/src/api/routes/floodgauges/test.js b/src/api/routes/floodgauges/test.js index a9192de..03946d0 100644 --- a/src/api/routes/floodgauges/test.js +++ b/src/api/routes/floodgauges/test.js @@ -30,7 +30,7 @@ const tests = [ status: 404 } } -] +]; // Run the tests describe('GET /floodgauges', () => { diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index d4b634f..3e3c33c 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -36,13 +36,13 @@ const REM_STATES = { severity: 'Severe', levelDescription: 'FLOODING OF OVER 150 CENTIMETERS' } -} +}; // Function to clear out the cache const clearCache = () => { apicache.clear(CACHE_GROUP_FLOODS); apicache.clear(CACHE_GROUP_FLOODS_STATES); -} +}; export default ({ config, db, logger }) => { let api = Router(); @@ -60,8 +60,8 @@ export default ({ config, db, logger }) => { }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_FLOODS; - if (req.query.geoformat === 'cap' && req.query.format !== 'xml') res.status(400).json({ statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\'' }) - else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') res.status(400).json({ statusCode: 400, message: 'format must be \'json\' when geoformat IN (\'geojson\',\'topojson\')' }) + if (req.query.geoformat === 'cap' && req.query.format !== 'xml') res.status(400).json({ statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\'' }); + else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') res.status(400).json({ statusCode: 400, message: 'format must be \'json\' when geoformat IN (\'geojson\',\'topojson\')' }); else floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? @@ -77,7 +77,7 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } ); @@ -97,7 +97,7 @@ export default ({ config, db, logger }) => { .catch((err) => { logger.error(err); next(err); - }) + }); } ); @@ -134,7 +134,7 @@ export default ({ config, db, logger }) => { (req, res, next) => floods(config, db, logger).clearREMState(req.params.localAreaId, req.query.username) .then(() => { clearCache(); - res.status(200).json({localAreaId: req.params.localAreaId, state: null, updated: true}) + res.status(200).json({localAreaId: req.params.localAreaId, state: null, updated: true}); }) .catch((err) => { logger.error(err); @@ -143,4 +143,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index 0aba4c5..03bac28 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -13,13 +13,13 @@ export default (config, db, logger) => ({ AND ($1 IS NULL OR area.instance_region_code=$1)`; // Setup values - let values = [ city, minimum_state ] + let values = [ city, minimum_state ]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), @@ -34,16 +34,16 @@ export default (config, db, logger) => ({ (SELECT local_area, state, last_updated FROM ${config.TABLE_REM_STATUS} WHERE state IS NOT NULL AND ($2 IS NULL OR state >= $2)) rs ON la.pkey = rs.local_area - WHERE $1 IS NULL OR instance_region_code = $1` + WHERE $1 IS NULL OR instance_region_code = $1`; // Setup values - let values = [ city, minimum_state ] + let values = [ city, minimum_state ]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), @@ -51,7 +51,7 @@ export default (config, db, logger) => ({ updateREMState: (localAreaId, state, username) => new Promise((resolve, reject) => { // Setup a timestamp with current date/time in ISO format - let timestamp = (new Date).toISOString(); + let timestamp = (new Date()).toISOString(); // Setup our queries let queries = [ @@ -69,24 +69,24 @@ export default (config, db, logger) => ({ VALUES ( $1, $2, $3, $4 )`, values: [ localAreaId, state, timestamp, username ] } - ] + ]; // Log queries to debugger for (let query of queries) logger.debug(query.query, query.values); // Execute in a transaction as both INSERT and UPDATE must happen together db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))) + return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), // Remove the REM state record and append to the log clearREMState: (localAreaId, username) => new Promise((resolve, reject) => { // Setup a timestamp with current date/time in ISO format - let timestamp = (new Date).toISOString(); + let timestamp = (new Date()).toISOString(); // Setup our queries let queries = [ @@ -101,17 +101,17 @@ export default (config, db, logger) => ({ VALUES ( $1, $2, $3, $4 )`, values: [ localAreaId, null, timestamp, username ] } - ] + ]; // Log queries to debugger for (let query of queries) logger.debug(query.query, query.values); // Execute in a transaction as both INSERT and UPDATE must happen together db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))) + return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) }); diff --git a/src/api/routes/infrastructure/index.js b/src/api/routes/infrastructure/index.js index f06710c..570cfad 100644 --- a/src/api/routes/infrastructure/index.js +++ b/src/api/routes/infrastructure/index.js @@ -33,4 +33,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index 2f761f1..6b0d30a 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -11,13 +11,13 @@ export default (config, db, logger) => ({ WHERE ($1 IS NULL OR tags->>'instance_region_code'=$1)`; // Setup values - let values = [ city ] + let values = [ city ]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) }); diff --git a/src/api/routes/infrastructure/test.js b/src/api/routes/infrastructure/test.js index baaea4f..2ef7401 100644 --- a/src/api/routes/infrastructure/test.js +++ b/src/api/routes/infrastructure/test.js @@ -18,7 +18,7 @@ const tests = [ status: 200 } } -] +]; // Run the tests describe('GET /infrastructure', () => { diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index 1ac810a..478ec83 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -49,4 +49,4 @@ export default ({ config, db, logger }) => { ); return api; -} +}; diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 34308ad..3414060 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -25,7 +25,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }), // Return specific report by id @@ -38,13 +38,13 @@ export default (config, db, logger) => ({ WHERE pkey = $1`; // Setup values - let values = [ id ] + let values = [ id ]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) - .catch((err) => reject(err)) + .catch((err) => reject(err)); }) }); diff --git a/src/api/routes/reports/test.js b/src/api/routes/reports/test.js index 0fc83ef..c0a3979 100644 --- a/src/api/routes/reports/test.js +++ b/src/api/routes/reports/test.js @@ -30,7 +30,7 @@ const tests = [ status: 404 } } -] +]; // Run the tests describe('GET /reports', () => { diff --git a/src/config.js b/src/config.js index f4d7eda..a348dcb 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,4 @@ -require('dotenv').config({silent:true}) +require('dotenv').config({silent:true}); export default { APP_NAME: process.env.APP_NAME || 'cognicity-server', @@ -59,4 +59,4 @@ export default { TABLE_REM_STATUS: process.env.TABLE_REM_STATUS || 'cognicity.rem_status', TABLE_REM_STATUS_LOG: process.env.TABLE_REM_STATUS_LOG || 'cognicity.rem_status_log', TABLE_REPORTS: process.env.TABLE_REPORTS || 'cognicity.all_reports', -} +}; diff --git a/src/index.js b/src/index.js index 221c783..bafbe4f 100755 --- a/src/index.js +++ b/src/index.js @@ -30,7 +30,9 @@ logger.level = config.LOG_LEVEL; // Check that log file directory can be written to try { - config.LOG_DIR !== '' && fs.accessSync(config.LOG_DIR, fs.W_OK); + if (config.LOG_DIR !== '') { + fs.accessSync(config.LOG_DIR, fs.W_OK); + } logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR : 'current working directory' }`); } catch(e) { // If we cannot write to the desired directory then log in the current directory @@ -45,7 +47,7 @@ logger.add(logger.transports.File, { maxsize: config.LOG_MAX_FILE_SIZE, // Max size of each file maxFiles: config.LOG_MAX_FILES, // Max number of files level: config.LOG_LEVEL // Level of log messages -}) +}); // If we are not in development and console logging has not been requested then remove it if (config.NODE_ENV !== 'development' && !config.LOG_CONSOLE) { @@ -70,13 +72,19 @@ const init = () => new Promise((resolve, reject) => { app.use(morgan('combined', { stream : winstonStream })); // Compress responses if required but only if caching is disabled - config.COMPRESS && !config.CACHE && app.use(compression()); + if (config.COMPRESS && !config.CACHE) { + app.use(compression()); + } // Provide CORS support (not required if behind API gateway) - config.CORS && app.use(cors({ exposedHeaders: config.CORS_HEADERS })); + if (config.CORS) { + app.use(cors({ exposedHeaders: config.CORS_HEADERS })); + } // Provide response time header in response - config.RESPONSE_TIME && app.use(responseTime()); + if (config.RESPONSE_TIME) { + app.use(responseTime()); + } // Parse body messages into json app.use(bodyParser.json({ limit : config.BODY_LIMIT })); @@ -100,14 +108,14 @@ const init = () => new Promise((resolve, reject) => { // We cannot continue without a DB, reject reject(err); - }) -}) + }); +}); // If we exit immediately winston does not get a chance to write the last log message const exitWithStatus = (status) => { logger.info(`Exiting with status ${status}`); setTimeout(() => process.exit(status), 500); -} +}; // Catch kill and interrupt signals and log a clean exit status process diff --git a/src/lib/cap.js b/src/lib/cap.js index 2b96e66..9ed8510 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -1,4 +1,4 @@ -'use strict' +'use strict'; // XML builder used to create XML output import builder from 'xmlbuilder'; @@ -195,4 +195,4 @@ module.exports = class Cap { return area; } -} +}; diff --git a/src/lib/util.js b/src/lib/util.js index a04c0f8..cba903d 100755 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -9,7 +9,7 @@ import config from '../config'; // Caching import apicache from 'apicache'; -apicache.options({ debug: config.LOG_LEVEL === 'debug', statusCodes: { include: [200] } }) +apicache.options({ debug: config.LOG_LEVEL === 'debug', statusCodes: { include: [200] } }); let cache = apicache.middleware; // Cache response if enabled @@ -40,7 +40,7 @@ dbgeo.defaults = { geometryColumn: 'the_geom', geometryType: 'wkb', precision: config.GEO_PRECISION -} +}; // Format the geographic response with the required geo format const formatGeo = (body, outputFormat) => new Promise((resolve, reject) => { @@ -51,8 +51,8 @@ const formatGeo = (body, outputFormat) => new Promise((resolve, reject) => { dbgeo.parse(body, { outputFormat }, (err, formatted) => { if (err) reject(err); resolve(formatted); - }) -}) + }); +}); // Handle a geo response, send back a correctly formatted json object with // status 200 or not found 404, catch and forward any errors in the process @@ -61,16 +61,16 @@ const handleGeoResponse = (data, req, res, next) => { res.status(404).json({ statusCode: 404, found: false, result: null }) : formatGeo(data, req.query.geoformat) .then((formatted) => res.status(200).json({ statusCode: 200, result: formatted })) - .catch((err) => next(err)) -} + .catch((err) => next(err)); +}; // Handle a regular response, send back result or 404 const handleResponse = (data, req, res) => { return !data ? res.status(404).json({ statusCode: 404, found: false, result: null }) : - res.status(200).json({ statusCode: 200, result: data }) -} + res.status(200).json({ statusCode: 200, result: data }); +}; module.exports = { cacheResponse, formatGeo, handleResponse, handleGeoResponse, jwtCheck -} +}; From 910d88caaf7077af269d3ce8942c5d4ccd9540da Mon Sep 17 00:00:00 2001 From: MaanasaPriyaa Date: Mon, 1 May 2017 13:23:04 -0400 Subject: [PATCH 008/160] Added changes for prep decks --- src/api/routes/cards/index.js | 18 ++++++++++++++---- src/api/routes/cards/model.js | 6 +++--- src/config.js | 2 ++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index 2423f8d..e70da68 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -20,7 +20,7 @@ var s3 = new AWS.S3( accessKeyId : process.env.accessKeyId || '' , secretAccessKey : process.env.secretAccessKey || '', signatureVersion: 'v4', - region: 'ap-south-1' + region: 'ap-south-1' }); @@ -94,7 +94,17 @@ export default ({ config, db, logger }) => { api.put('/:cardId', validate({ params: { cardId: Joi.string().min(7).max(14).required() }, body: Joi.object().keys({ - water_depth: Joi.number().integer().min(0).max(200).required(), + disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(), + card_data: Joi.object() + .keys({ + flood_depth: Joi.number(), + report_type: Joi.string().valid(config.REPORT_TYPES).required() + }) + .required() + .when('disaster_type', { + is: 'flood', + then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true + }), text: Joi.string().allow(''), image_url: Joi.string().allow(''), created_at: Joi.date().iso().required(), @@ -156,13 +166,13 @@ export default ({ config, db, logger }) => { signedRequest : data, url: 'https://s3.ap-south-1.amazonaws.com/mapchennai/' + s3params.Key }; - //write the url into the db under image_url for this card + //write the url into the db under image_url for this card cards(config, db, logger).byCardId(req.params.cardId) .then((card) => { if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, message: `No card exists with id '${req.params.cardId}'` }) - else { + else { // Try and submit the report and update the card cards(config, db, logger).updateReport(card, {image_url: returnData.url}) .then((data) => { diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index cda646a..9d6354e 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -54,8 +54,8 @@ export default (config, db, logger) => ({ query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, - values: [ card.card_id, { flood_depth: body.water_depth }, body.text, - body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat ] + values: [ card.card_id, body.card_data, body.text, + body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] }, { query: `UPDATE ${config.TABLE_GRASP_CARDS} @@ -115,4 +115,4 @@ export default (config, db, logger) => ({ .catch((err) => reject(err)) }) -}); \ No newline at end of file +}); diff --git a/src/config.js b/src/config.js index f4d7eda..335eafa 100644 --- a/src/config.js +++ b/src/config.js @@ -23,6 +23,7 @@ export default { COMPRESS: process.env.COMPRESS === 'true' || false, CORS: process.env.CORS === 'true' || false, CORS_HEADERS: process.env.CORS_HEADERS || ['Link'], + DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','), PGHOST: process.env.PGHOST || '127.0.0.1', PGDATABASE: process.env.PGDATABASE || 'cognicity', PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd', @@ -46,6 +47,7 @@ export default { NODE_ENV: process.env.NODE_ENV || 'development', PORT: process.env.PORT || 8001, REGION_CODES: (process.env.REGION_CODES || 'jbd,bdg,sby').split(','), + REPORT_TYPES: (process.env.REPORT_TYPES || 'drain,desilting,canalrepair,treeclearing,flood').split(','), RESPONSE_TIME: process.env.RESPONSE_TIME === 'true' || false, SECURE_AUTH0: process.env.SECURE_AUTH0 === 'true' || false, TABLE_FLOODGAUGE_REPORTS: process.env.TABLE_FLOODGAUGE_REPORTS || 'floodgauge.reports', From a38ed4fb18261e3e8083ad80825d07e33edc01b0 Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 11 May 2017 10:08:42 +1000 Subject: [PATCH 009/160] npm package updates --- package.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index d08f33f..d769b32 100644 --- a/package.json +++ b/package.json @@ -31,36 +31,36 @@ "license": "GPL-3.0", "dependencies": { "api-error-handler": "^1.0.0", - "apicache": "^0.8.3", - "bluebird": "^3.4.7", - "body-parser": "^1.16.0", + "apicache": "^0.8.4", + "bluebird": "^3.5.0", + "body-parser": "^1.17.1", "celebrate": "^4.0.1", "compression": "^1.6.2", - "cors": "^2.8.1", + "cors": "^2.8.3", "dbgeo": "^1.0.1", "dotenv": "^4.0.0", - "express": "^4.14.1", - "express-jwt": "^5.1.0", - "joi": "^10.2.1", + "express": "^4.15.2", + "express-jwt": "^5.3.0", + "joi": "^10.4.1", "jwks-rsa": "^1.1.1", - "moment-timezone": "^0.5.11", - "morgan": "^1.8.0", - "pg-promise": "^5.5.6", + "moment-timezone": "^0.5.13", + "morgan": "^1.8.1", + "pg-promise": "^5.6.8", "response-time": "^2.3.2", - "shortid": "^2.2.6", + "shortid": "^2.2.8", "winston": "^2.3.1", - "xmlbuilder": "^8.2.2" + "xmlbuilder": "^9.0.0" }, "devDependencies": { - "babel-cli": "^6.22.2", - "babel-core": "^6.22.1", - "babel-preset-es2015": "^6.22.0", - "babel-preset-stage-0": "^6.22.0", - "babel-register": "^6.22.0", + "babel-cli": "^6.24.1", + "babel-core": "^6.24.1", + "babel-preset-es2015": "^6.24.1", + "babel-preset-stage-0": "^6.24.1", + "babel-register": "^6.24.1", "chai": "^3.5.0", - "eslint": "^3.15.0", + "eslint": "^3.19.0", "it-each": "^0.3.1", - "mocha": "^3.2.0", + "mocha": "^3.3.0", "nodemon": "^1.11.0", "supertest": "^3.0.0" }, From f445cb73468844eef10b9b41020d73251577d933 Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 18 May 2017 12:44:35 +1000 Subject: [PATCH 010/160] fixes #23 --- src/api/routes/cards/index.js | 6 +++--- src/config.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index e70da68..ee87204 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -153,8 +153,8 @@ export default ({ config, db, logger }) => { }), (req, res, next) => { let s3params = { - Bucket: 'mapchennai', - Key: 'resized/' + req.params.cardId + ".png", + Bucket: config.IMAGE_BUCKET, + Key: 'resized/' + req.params.cardId + ".jpg", ContentType:req.query.file_type }; s3.getSignedUrl('putObject', s3params, (err, data) => { @@ -164,7 +164,7 @@ export default ({ config, db, logger }) => { } else { var returnData = { signedRequest : data, - url: 'https://s3.ap-south-1.amazonaws.com/mapchennai/' + s3params.Key + url: 'https://s3.ap-south-1.amazonaws.com/' config.IMAGE_BUCKET+'/'+ s3params.Key }; //write the url into the db under image_url for this card diff --git a/src/config.js b/src/config.js index 335eafa..2338079 100644 --- a/src/config.js +++ b/src/config.js @@ -24,6 +24,7 @@ export default { CORS: process.env.CORS === 'true' || false, CORS_HEADERS: process.env.CORS_HEADERS || ['Link'], DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','), + IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', PGHOST: process.env.PGHOST || '127.0.0.1', PGDATABASE: process.env.PGDATABASE || 'cognicity', PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd', From 0218f2bf4c0ecf3ebd31be8e70dfc9ffec13e764 Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 18 May 2017 14:40:20 +1000 Subject: [PATCH 011/160] further work on image upload --- src/api/routes/cards/index.js | 6 +++--- src/config.js | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index ee87204..45fc430 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -154,7 +154,7 @@ export default ({ config, db, logger }) => { (req, res, next) => { let s3params = { Bucket: config.IMAGE_BUCKET, - Key: 'resized/' + req.params.cardId + ".jpg", + Key: 'originals/' + req.params.cardId + ".jpg", ContentType:req.query.file_type }; s3.getSignedUrl('putObject', s3params, (err, data) => { @@ -164,7 +164,7 @@ export default ({ config, db, logger }) => { } else { var returnData = { signedRequest : data, - url: 'https://s3.ap-south-1.amazonaws.com/' config.IMAGE_BUCKET+'/'+ s3params.Key + url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/' config.IMAGE_BUCKET+'/'+ s3params.Key }; //write the url into the db under image_url for this card @@ -174,7 +174,7 @@ export default ({ config, db, logger }) => { message: `No card exists with id '${req.params.cardId}'` }) else { // Try and submit the report and update the card - cards(config, db, logger).updateReport(card, {image_url: returnData.url}) + cards(config, db, logger).updateReport(card, {image_url: 'https://'+config.IMAGES_HOST+'/'+req.params.cardId+'.jpg' }) .then((data) => { console.log(data); clearCache(); diff --git a/src/config.js b/src/config.js index 2338079..64d89a4 100644 --- a/src/config.js +++ b/src/config.js @@ -25,6 +25,10 @@ export default { CORS_HEADERS: process.env.CORS_HEADERS || ['Link'], DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','), IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', + IMAGE_HOST: process.env.IMAGE_HOST || + 'images.riskmap.in', + AWS_REGION: process.env.AWS_REGION || + 'ap-south-1', PGHOST: process.env.PGHOST || '127.0.0.1', PGDATABASE: process.env.PGDATABASE || 'cognicity', PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd', From 0b7d25c64a10087168283e17176082a3e7cf344e Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 18 May 2017 15:51:56 +1000 Subject: [PATCH 012/160] syntax bug fix --- src/api/routes/cards/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index 45fc430..13ee540 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -164,7 +164,7 @@ export default ({ config, db, logger }) => { } else { var returnData = { signedRequest : data, - url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/' config.IMAGE_BUCKET+'/'+ s3params.Key + url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/'+ config.IMAGE_BUCKET+'/'+ s3params.Key }; //write the url into the db under image_url for this card From 6939b71f8cc1af0e24dd4bfe621bda1200754aea Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 18 May 2017 17:41:22 +1000 Subject: [PATCH 013/160] fix typo --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index 64d89a4..7193784 100644 --- a/src/config.js +++ b/src/config.js @@ -25,7 +25,7 @@ export default { CORS_HEADERS: process.env.CORS_HEADERS || ['Link'], DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','), IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', - IMAGE_HOST: process.env.IMAGE_HOST || + IMAGES_HOST: process.env.IMAGES_HOST || 'images.riskmap.in', AWS_REGION: process.env.AWS_REGION || 'ap-south-1', From f30448024895e1a690aa313db16a05734515bead Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 10:23:14 -0400 Subject: [PATCH 014/160] added test for reports endpoint --- package.json | 3 ++- src/api/routes/reports/test.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d769b32..d56645e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "babel src -s -D -d dist --presets es2015,stage-0", "start": "node dist", "prestart": "npm run -s build", - "test": "eslint src" + "test": "eslint src & npm run mocha", + "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js" }, "eslintConfig": { "extends": "eslint:recommended", diff --git a/src/api/routes/reports/test.js b/src/api/routes/reports/test.js index c0a3979..397deaf 100644 --- a/src/api/routes/reports/test.js +++ b/src/api/routes/reports/test.js @@ -25,7 +25,7 @@ const tests = [ } }, { - url: '/reports/1', + url: '/reports/0', exp: { status: 404 } From 7ff30e67774a0a8e2e15bf611081c59b09f3d186 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 10:29:58 -0400 Subject: [PATCH 015/160] Added holding strings for auth0 secrets to enable app startup for testing --- src/config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.js b/src/config.js index a348dcb..f408ad0 100644 --- a/src/config.js +++ b/src/config.js @@ -11,9 +11,9 @@ export default { API_FLOODGAUGE_REPORTS_TIME_WINDOW: process.env.API_FLOODGAUGE_REPORTS_TIME_WINDOW || 43200, API_FLOODGAUGE_REPORTS_LIMIT: process.env.API_FLOODGAUGE_REPORTS_LIMIT, AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://data.petabencana.id', - AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID, + AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'client_id', AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com', - AUTH0_SECRET: process.env.AUTH0_SECRET, + AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret', BODY_LIMIT: process.env.BODY_LIMIT || '100kb', CACHE: process.env.CACHE === 'true' || false, CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute', From 75fcb8ef7666b083be32d2b2664bb5b3f6e0454c Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 11:38:36 -0400 Subject: [PATCH 016/160] Added postgres to travis build for intregration testing --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 246e5f3..d5b9c9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,15 +3,18 @@ node_js: - "6.10.0" sudo: false +addons: + postgresql: "9.4" +services: postgresql + branches: only: - master - dev -notifications: - before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi + - git clone git@github.com:urbanriskmap/cognicity-schema.git && bash cognicity-schema/build/run.sh install: - npm install From 2bb5f6c5157f27b8807ad771c86ad9443971cc52 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 11:41:03 -0400 Subject: [PATCH 017/160] Changed travis git clone to https protocol --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d5b9c9b..c9c37e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ branches: before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi - - git clone git@github.com:urbanriskmap/cognicity-schema.git && bash cognicity-schema/build/run.sh + - git clone https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh install: - npm install From 3ab0ab1c24692f3f31566c0c209766a60312ee25 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 11:47:23 -0400 Subject: [PATCH 018/160] moved travis to use dev schema --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c9c37e7..31cc70f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ branches: before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi - - git clone https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh + - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh install: - npm install From 2383a4b124728edff5e13920e07b7ec226795613 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 11:51:14 -0400 Subject: [PATCH 019/160] fixing travis schema build script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 31cc70f..51e828e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ branches: before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi - - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh + - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && cd urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh && cd - install: - npm install From 428fb3bf7b8ab4b5b50b3aab6057f04a8cf040e5 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 11:52:29 -0400 Subject: [PATCH 020/160] fixing travis schema build script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 51e828e..dbca825 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ branches: before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi - - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && cd urbanriskmap/cognicity-schema && bash urbanriskmap/cognicity-schema/build/run.sh && cd - + - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && cd urbanriskmap/cognicity-schema && bash build/run.sh && cd - install: - npm install From 82cc20dddfd5e7bfbb5e1654401f36e7d8953f02 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 23 May 2017 12:28:41 -0400 Subject: [PATCH 021/160] enabled tests for all endpoints --- package.json | 2 +- src/api/routes/cards/test.js | 2 +- src/api/routes/feeds/test.js | 4 ++-- src/api/routes/floodgauges/test.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d56645e..7ab3b58 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "node dist", "prestart": "npm run -s build", "test": "eslint src & npm run mocha", - "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js" + "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js src/api/routes/infrastructure/test.js src/api/routes/infrastructure/floods/test.js src/api/routes/floodgauges/test.js src/api/routes/feeds/test.js src/api/routes/cities/test.js src/api/routes/cards/test.js" }, "eslintConfig": { "extends": "eslint:recommended", diff --git a/src/api/routes/cards/test.js b/src/api/routes/cards/test.js index 982fcd2..de42785 100644 --- a/src/api/routes/cards/test.js +++ b/src/api/routes/cards/test.js @@ -15,7 +15,7 @@ const tests = [ { url: '/cards/1', exp: { - status: 200 + status: 400 } } ]; diff --git a/src/api/routes/feeds/test.js b/src/api/routes/feeds/test.js index 942b00c..c59c768 100644 --- a/src/api/routes/feeds/test.js +++ b/src/api/routes/feeds/test.js @@ -10,7 +10,7 @@ const tests = [ { url: '/feeds/qlue', exp: { - status: 200 + status: 400 } } ]; @@ -20,7 +20,7 @@ describe('GET /feeds', () => { it.each(tests, 'respond with correct response for test', (test, next) => { init().then((app) => { request(app) - .get(test.url) + .post(test.url) .end((err, res) => { if (err) next(err); assert.equal(res.status, test.exp.status); diff --git a/src/api/routes/floodgauges/test.js b/src/api/routes/floodgauges/test.js index 03946d0..22a4c46 100644 --- a/src/api/routes/floodgauges/test.js +++ b/src/api/routes/floodgauges/test.js @@ -25,7 +25,7 @@ const tests = [ } }, { - url: '/floodgauges/1', + url: '/floodgauges/0', exp: { status: 404 } From 5a127ef6cf77b6cbafe9812faae26fa1e4208e05 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 6 Jun 2017 09:36:00 -0400 Subject: [PATCH 022/160] prototype testing framework --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ab3b58..64ae9eb 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "node dist", "prestart": "npm run -s build", "test": "eslint src & npm run mocha", - "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js src/api/routes/infrastructure/test.js src/api/routes/infrastructure/floods/test.js src/api/routes/floodgauges/test.js src/api/routes/feeds/test.js src/api/routes/cities/test.js src/api/routes/cards/test.js" + "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js src/api/routes/infrastructure/test.js src/api/routes/infrastructure/floods/test.js src/api/routes/floodgauges/test.js src/api/routes/feeds/test.js src/api/routes/cities/test.js src/api/" }, "eslintConfig": { "extends": "eslint:recommended", From 1855809ab4d2bce5687810090474da92c3e18d08 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 6 Jun 2017 13:24:08 -0400 Subject: [PATCH 023/160] Refactored server express app for testing --- src/index.js | 76 +++------------------------------------------------ src/server.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 72 deletions(-) create mode 100644 src/server.js diff --git a/src/index.js b/src/index.js index bafbe4f..08f9dec 100755 --- a/src/index.js +++ b/src/index.js @@ -1,17 +1,7 @@ -import Promise from 'bluebird'; - // Import express, fs and http -import express from 'express'; import fs from 'fs'; -import http from 'http'; import path from 'path'; -// Import express middlewares -import bodyParser from 'body-parser'; -import cors from 'cors'; -import compression from 'compression'; -import responseTime from 'response-time'; - // Import config import config from './config'; @@ -21,8 +11,10 @@ import initializeDb from './db'; // Import the routes import routes from './api'; +// Import server +import { init } from './server.js'; + // Import logging libraries -import morgan from 'morgan'; // Express logging import logger from 'winston'; // Application logging // Set the default logging level @@ -54,63 +46,6 @@ if (config.NODE_ENV !== 'development' && !config.LOG_CONSOLE) { logger.remove(logger.transports.Console); } -// Function to start the initialize the api server -const init = () => new Promise((resolve, reject) => { - - // Create the server - let app = express(); - app.server = http.createServer(app); - - // Winston stream function we can plug in to express so we can capture its logs along with our own - const winstonStream = { - write: function(message) { - logger.info(message.slice(0, -1)); - } - }; - - // Setup express logger - app.use(morgan('combined', { stream : winstonStream })); - - // Compress responses if required but only if caching is disabled - if (config.COMPRESS && !config.CACHE) { - app.use(compression()); - } - - // Provide CORS support (not required if behind API gateway) - if (config.CORS) { - app.use(cors({ exposedHeaders: config.CORS_HEADERS })); - } - - // Provide response time header in response - if (config.RESPONSE_TIME) { - app.use(responseTime()); - } - - // Parse body messages into json - app.use(bodyParser.json({ limit : config.BODY_LIMIT })); - - // Try and connect to the db - initializeDb(config, logger) - .then((db) => { - // Log debug message - logger.debug('Successfully connected to DB'); - - // Mount the routes - app.use('/', routes({ config, db, logger })); - - // App is ready to go, resolve the promise - resolve(app); - - }) - .catch((err) => { - logger.error('DB Connection error: ' + err); - logger.error('Fatal error: Application shutting down'); - - // We cannot continue without a DB, reject - reject(err); - }); -}); - // If we exit immediately winston does not get a chance to write the last log message const exitWithStatus = (status) => { logger.info(`Exiting with status ${status}`); @@ -129,7 +64,7 @@ process }); // Try and start the server -init().then((app) => { +init(config, initializeDb, routes, logger).then((app) => { // All good to go, start listening for requests app.server.listen(config.PORT); logger.info(`Application started, listening on port ${app.server.address().port}`); @@ -139,6 +74,3 @@ init().then((app) => { logger.error('Fatal error: Application shutting down'); exitWithStatus(1); }); - -// Export the init function for use externally (e.g. in tests) -module.exports = { init }; diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..a35bff3 --- /dev/null +++ b/src/server.js @@ -0,0 +1,73 @@ +import Promise from 'bluebird'; + +// Express middleware and http +import express from 'express'; +import http from 'http'; + +// Import express middlewares +import bodyParser from 'body-parser'; +import cors from 'cors'; +import compression from 'compression'; +import responseTime from 'response-time'; +import morgan from 'morgan'; // Express logging + +// Function to initialize the api server +const init = (config, initializeDb, routes, logger) => new Promise((resolve, reject) => { + + // Create the server + let app = express(); + app.server = http.createServer(app); + + // Winston stream function we can plug in to express so we can capture its logs along with our own + const winstonStream = { + write: function(message) { + logger.info(message.slice(0, -1)); + } + }; + + // Setup express logger + app.use(morgan('combined', { stream : winstonStream })); + + // Compress responses if required but only if caching is disabled + if (config.COMPRESS && !config.CACHE) { + app.use(compression()); + } + + // Provide CORS support (not required if behind API gateway) + if (config.CORS) { + app.use(cors({ exposedHeaders: config.CORS_HEADERS })); + } + + // Provide response time header in response + if (config.RESPONSE_TIME) { + app.use(responseTime()); + } + + // Parse body messages into json + app.use(bodyParser.json({ limit : config.BODY_LIMIT })); + + // Try and connect to the db + initializeDb(config, logger) + .then((db) => { + // Log debug message + logger.debug('Successfully connected to DB'); + + // Mount the routes + app.use('/', routes({ config, db, logger })); + + // App is ready to go, resolve the promise + resolve(app); + + }) + .catch((err) => { + logger.error('DB Connection error: ' + err); + logger.error('Fatal error: Application shutting down'); + + // We cannot continue without a DB, reject + reject(err); + }) +}) + +// Export the init function for use externally + +module.exports = { init }; From 49f84c136a0ea02d173ac8678febe2d2200afcbc Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 6 Jun 2017 13:24:41 -0400 Subject: [PATCH 024/160] Refactored reports endpoint test --- src/api/routes/reports/test.js | 48 ------------------- src/test/testReports.js | 86 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 48 deletions(-) delete mode 100644 src/api/routes/reports/test.js create mode 100644 src/test/testReports.js diff --git a/src/api/routes/reports/test.js b/src/api/routes/reports/test.js deleted file mode 100644 index 397deaf..0000000 --- a/src/api/routes/reports/test.js +++ /dev/null @@ -1,48 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/reports', - exp: { - status: 200 - } - }, - { - url: '/reports?city=jbd', - exp: { - status: 200 - } - }, - { - url: '/reports?city=xxx', - exp: { - status: 400 - } - }, - { - url: '/reports/0', - exp: { - status: 404 - } - } -]; - -// Run the tests -describe('GET /reports', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/test/testReports.js b/src/test/testReports.js new file mode 100644 index 0000000..fb935ab --- /dev/null +++ b/src/test/testReports.js @@ -0,0 +1,86 @@ +// Testing for CogniCity Server +// Unit tests run together against live app, and database + +// Import Unit.js +const test = require('unit.js'); + +// Import config +import config from '../config'; + +// Import DB initializer +import initializeDb from '../db'; + +// Import the routes +import routes from '../api'; + +// Import server object +import { init } from '../server.js'; + +// Mock logger object for app +const winston = require('winston'); +const logger = new (winston.Logger)({ + transports: [ + new (winston.transports.Console)({ raw: true }), + ] +}); + +// Create a top-level testing harness +describe('Cognicity Server Testing Harness', function() { + + it('Server starts', function(done){ + init(config, initializeDb, routes, logger).then((app) => { + + describe('Reports endpoint', function() { + + // Can get reports + it('Get all reports (GET /reports)', function(done){ + test.httpAgent(app) + .get('/reports') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports by city + it('Get reports by city /reports?city=jbd', function(done){ + test.httpAgent(app) + .get('/reports?city=jbd') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch report by city error + it('Get reports by city /reports?city=xxx', function(done){ + test.httpAgent(app) + .get('/reports?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + return (done()) + }); + }); +}); From 8dccb8d2bb42d51073ae6035b57c093c8cb42c8d Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 6 Jun 2017 15:28:44 -0400 Subject: [PATCH 025/160] Added testing for endpoints --- package.json | 7 +- src/api/routes/cards/test.js | 36 ---- src/api/routes/cities/test.js | 29 --- src/api/routes/feeds/test.js | 31 ---- src/api/routes/floodgauges/test.js | 48 ----- src/api/routes/infrastructure/test.js | 36 ---- src/test/testCognicityServer.js | 248 ++++++++++++++++++++++++++ src/test/testReports.js | 86 --------- 8 files changed, 251 insertions(+), 270 deletions(-) delete mode 100644 src/api/routes/cards/test.js delete mode 100644 src/api/routes/cities/test.js delete mode 100644 src/api/routes/feeds/test.js delete mode 100644 src/api/routes/floodgauges/test.js delete mode 100644 src/api/routes/infrastructure/test.js create mode 100644 src/test/testCognicityServer.js delete mode 100644 src/test/testReports.js diff --git a/package.json b/package.json index 88dd3cb..f667d92 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "node dist", "prestart": "npm run -s build", "test": "eslint src & npm run mocha", - "mocha": "mocha --compilers js:babel-core/register src/api/routes/reports/test.js src/api/routes/infrastructure/test.js src/api/routes/infrastructure/floods/test.js src/api/routes/floodgauges/test.js src/api/routes/feeds/test.js src/api/routes/cities/test.js src/api/" + "mocha": "mocha --compilers js:babel-core/register src/test/" }, "eslintConfig": { "extends": "eslint:recommended", @@ -58,12 +58,11 @@ "babel-preset-es2015": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "babel-register": "^6.24.1", - "chai": "^3.5.0", "eslint": "^3.19.0", - "it-each": "^0.3.1", "mocha": "^3.3.0", "nodemon": "^1.11.0", - "supertest": "^3.0.0" + "supertest": "^3.0.0", + "unit.js": "^2.0.0" }, "engines": { "node": ">=6.9.1" diff --git a/src/api/routes/cards/test.js b/src/api/routes/cards/test.js deleted file mode 100644 index de42785..0000000 --- a/src/api/routes/cards/test.js +++ /dev/null @@ -1,36 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/cards', - exp: { - status: 404 - } - }, - { - url: '/cards/1', - exp: { - status: 400 - } - } -]; - -// Run the tests -describe('GET /cards', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/api/routes/cities/test.js b/src/api/routes/cities/test.js deleted file mode 100644 index 2738eba..0000000 --- a/src/api/routes/cities/test.js +++ /dev/null @@ -1,29 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/cities', - exp: { - status: 200 - } - }]; - -// Run the tests -describe('GET /cities', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/api/routes/feeds/test.js b/src/api/routes/feeds/test.js deleted file mode 100644 index c59c768..0000000 --- a/src/api/routes/feeds/test.js +++ /dev/null @@ -1,31 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -// TODO: Add POST test -const tests = [ - { - url: '/feeds/qlue', - exp: { - status: 400 - } - } -]; - -// Run the tests -describe('GET /feeds', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .post(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/api/routes/floodgauges/test.js b/src/api/routes/floodgauges/test.js deleted file mode 100644 index 22a4c46..0000000 --- a/src/api/routes/floodgauges/test.js +++ /dev/null @@ -1,48 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/floodgauges', - exp: { - status: 200 - } - }, - { - url: '/floodgauges?city=jbd', - exp: { - status: 200 - } - }, - { - url: '/floodgauges?city=xxx', - exp: { - status: 400 - } - }, - { - url: '/floodgauges/0', - exp: { - status: 404 - } - } -]; - -// Run the tests -describe('GET /floodgauges', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/api/routes/infrastructure/test.js b/src/api/routes/infrastructure/test.js deleted file mode 100644 index 2ef7401..0000000 --- a/src/api/routes/infrastructure/test.js +++ /dev/null @@ -1,36 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import { init } from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/infrastructure', - exp: { - status: 404 - } - }, - { - url: '/infrastructure/waterways', - exp: { - status: 200 - } - } -]; - -// Run the tests -describe('GET /infrastructure', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js new file mode 100644 index 0000000..b6af87a --- /dev/null +++ b/src/test/testCognicityServer.js @@ -0,0 +1,248 @@ +/*/reports/0*/ + +// Testing for CogniCity Server +// Unit tests run together against live app, and database + +// Import Unit.js +const test = require('unit.js'); + +// Import config +import config from '../config'; + +// Import DB initializer +import initializeDb from '../db'; + +// Import the routes +import routes from '../api'; + +// Import server object +import { init } from '../server.js'; + +// Mock logger object for app +const winston = require('winston'); +const logger = new (winston.Logger)({ + transports: [ + new (winston.transports.Console)({ raw: true }), + ] +}); + +// Create a top-level testing harness +describe('Cognicity Server Testing Harness', function() { + + it('Server starts', function(done){ + init(config, initializeDb, routes, logger).then((app) => { + + // Reports endpoint + describe('Reports endpoint', function() { + // Can get reports + it('Get all reports (GET /reports)', function(done){ + test.httpAgent(app) + .get('/reports') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports by city + it('Get reports by city /reports?city=jbd', function(done){ + test.httpAgent(app) + .get('/reports?city=jbd') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch report by city error + it('Get reports by city /reports?city=xxx', function(done){ + test.httpAgent(app) + .get('/reports?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Cards endpoint + describe('Cards endpoint', function() { + // Cards + it('Return 404 if card requested without ID (GET /cards)', function(done){ + test.httpAgent(app) + .get('/cards') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports + it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done){ + test.httpAgent(app) + .get('/cards/1') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Cities endpoint + describe('Cities endpoint', function() { + // Can get cities + it('Return 200 for cities list', function(done){ + test.httpAgent(app) + .get('/cities') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Feeds endpoint + describe('Feeds endpoint', function() { + // Can get cities + it('Return 400 for post to feeds/qlue', function(done){ + test.httpAgent(app) + .post('/feeds/qlue') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Flood gauges endpoint + describe('Flood gauges endpoint', function() { + // Can get flood gauge data + it('Return 200 for get /floodgauges', function(done){ + test.httpAgent(app) + .get('/floodgauges') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch invalid city in floodgauge + it('Return 400 for get /floodgauges?city=xxx', function(done){ + test.httpAgent(app) + .get('/floodgauges?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch invalid floodgauge id + it('Return 404 for get /floodgauges/:id', function(done){ + test.httpAgent(app) + .get('/floodgauges/0') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Infrastructure endpoint + describe('Infrastructure endpoint', function() { + // Catch invalid top-level infrastructure endpoint + it('Return 404 for get /infrastructure', function(done){ + test.httpAgent(app) + .get('/infrastructure') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Return waterways infrastructure + it('Return 200 for get /infrastructure/waterways', function(done){ + test.httpAgent(app) + .get('/infrastructure/waterways') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + return (done()) + }); + }); +}); diff --git a/src/test/testReports.js b/src/test/testReports.js deleted file mode 100644 index fb935ab..0000000 --- a/src/test/testReports.js +++ /dev/null @@ -1,86 +0,0 @@ -// Testing for CogniCity Server -// Unit tests run together against live app, and database - -// Import Unit.js -const test = require('unit.js'); - -// Import config -import config from '../config'; - -// Import DB initializer -import initializeDb from '../db'; - -// Import the routes -import routes from '../api'; - -// Import server object -import { init } from '../server.js'; - -// Mock logger object for app -const winston = require('winston'); -const logger = new (winston.Logger)({ - transports: [ - new (winston.transports.Console)({ raw: true }), - ] -}); - -// Create a top-level testing harness -describe('Cognicity Server Testing Harness', function() { - - it('Server starts', function(done){ - init(config, initializeDb, routes, logger).then((app) => { - - describe('Reports endpoint', function() { - - // Can get reports - it('Get all reports (GET /reports)', function(done){ - test.httpAgent(app) - .get('/reports') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports by city - it('Get reports by city /reports?city=jbd', function(done){ - test.httpAgent(app) - .get('/reports?city=jbd') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Catch report by city error - it('Get reports by city /reports?city=xxx', function(done){ - test.httpAgent(app) - .get('/reports?city=xxx') - .expect(400) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - return (done()) - }); - }); -}); From 9fed2a869460029e4fea6fbecbe6b4f402bf8b2c Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 6 Jun 2017 17:36:50 -0400 Subject: [PATCH 026/160] Added further tests --- package.json | 12 ++- src/api/routes/cards/index.js | 13 +++ src/test/testCognicityServer.js | 186 ++++++++++++++++++++++++++++++++ src/test/testDB.js | 27 +++++ 4 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 src/test/testDB.js diff --git a/package.json b/package.json index f667d92..ff5ef61 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,15 @@ "build": "babel src -s -D -d dist --presets es2015,stage-0", "start": "node dist", "prestart": "npm run -s build", - "test": "eslint src & npm run mocha", - "mocha": "mocha --compilers js:babel-core/register src/test/" + "mocha": "nyc mocha --compilers js:babel-core/register src/test/", + "coverage": "nyc report --reporter=text-lcov | coveralls", + "test": "eslint src & npm run mocha" + }, + "nyc": { + "exclude": [ + "**/test/**", + "build" + ] }, "eslintConfig": { "extends": "eslint:recommended", @@ -61,6 +68,7 @@ "eslint": "^3.19.0", "mocha": "^3.3.0", "nodemon": "^1.11.0", + "nyc": "^11.0.2", "supertest": "^3.0.0", "unit.js": "^2.0.0" }, diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index eb65710..32fcbe9 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -40,7 +40,9 @@ export default ({ config, db, logger }) => { .then((data) => data ? res.status(200).json({ cardId: cardId, created: true }) : next(new Error('Failed to create card'))) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } @@ -56,7 +58,9 @@ export default ({ config, db, logger }) => { cards(config, db, logger).byCardId(req.params.cardId) .then((data) => data ? res.status(200).end() : res.status(404).end()) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } @@ -72,7 +76,9 @@ export default ({ config, db, logger }) => { cards(config, db, logger).byCardId(req.params.cardId) .then((data) => handleResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } @@ -113,13 +119,17 @@ export default ({ config, db, logger }) => { res.status(200).json({ statusCode: 200, cardId: req.params.cardId, created: true }); }) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } }); } catch(err) { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); } } @@ -153,12 +163,15 @@ export default ({ config, db, logger }) => { }) .catch((err) => { logger.error(err); + /* istanbul ignore next */ next(err); }); } }); } catch(err) { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); } } diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index b6af87a..03f98b4 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -32,6 +32,38 @@ describe('Cognicity Server Testing Harness', function() { it('Server starts', function(done){ init(config, initializeDb, routes, logger).then((app) => { + describe('Top level API endpoint', function(){ + it('Gets current API version', function(done){ + test.httpAgent(app) + .get('/') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + it('Can handle unknown routes', function(done){ + test.httpAgent(app) + .get('/moon') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + // Reports endpoint describe('Reports endpoint', function() { // Can get reports @@ -242,6 +274,160 @@ describe('Cognicity Server Testing Harness', function() { }); }); + // Floods endpoint + describe('Flood areas endpoint', function(){ + + // Put a flood + /*it ('Put a flood (PUT /floods/:id)', function(done){ + let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } + test.httpAgent(app) + .put('/floods/5') + .set(auth) + .send({ + "state": "2" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + });*/ + + // Get floods + it('Get floods (GET /floods)', function(done){ + test.httpAgent(app) + .get('/floods') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports in CAP format + it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + test.httpAgent(app) + .get('/floods?format=xml&geoformat=cap') + .expect(200) + .expect('Content-Type', /text/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Cards end to end test + describe('End-to-end card test', function() { + + let cardId = '0'; + + // Request a card, submit and get resulting card detailsß + it('Get card one time link', function(done){ + test.httpAgent(app) + .post('/cards') + .send({ + "username": "testuser", + "network": "twitter", + "language": "en" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + cardId = res.body.cardId + test.value(res.body.created).is(true); + done(); + } + }); + }); + + // Request a card, submit and get resulting report + it('Put card data', function(done){ + test.httpAgent(app) + .put('/cards/'+cardId) + .send({ + "water_depth": 20, + "text": "big flood", + "created_at": "2017-02-21T07:00:00+0700", + "location": { + "lat": -6.4, + "lng": 106.6 + } + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + console.log(res.body); + done(); + } + }); + }); + + // Request a card, submit and get resulting report + it('Put card data', function(done){ + test.httpAgent(app) + .get('/cards/'+cardId) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.result.card_id).is(cardId); + test.value(res.body.result.username).is('testuser'); + test.value(res.body.result.network).is('twitter') + test.value(res.body.result.language).is('en') + test.value(res.body.result.report.text).is('big flood') + done(); + } + }); + }); + + // Update a card + it('Update card data', function(done){ + test.httpAgent(app) + .patch('/cards/'+cardId) + .send({ + "water_depth": 20, + "text": "big flood", + "image_url": "dummy image url" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + return (done()) }); }); diff --git a/src/test/testDB.js b/src/test/testDB.js new file mode 100644 index 0000000..2f43b06 --- /dev/null +++ b/src/test/testDB.js @@ -0,0 +1,27 @@ +// Import Unit.js +const test = require('unit.js'); + +import initializeDb from '../db'; + +describe('Test CogniCity Server Database Module', function() { + it('Catches errors on startup', function(done){ + // Try and connect to the db + let config = {}; + let logger = {}; + logger.error =function(err){ + console.log(err); + } + logger.debug =function(err){ + console.log(err); + } + initializeDb(config, logger) + .then((db) => { + test.value(db).is(null); + //done(); do nothing here, an error should be forced by empty config + }) + .catch((err) => { + console.log(err); + done(); + }); + }); +}); From 9e03359cc43e666f030dac2acb4dba7a36f737e3 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 11:26:39 -0400 Subject: [PATCH 027/160] further test coverage --- src/api/routes/reports/index.js | 4 ++ src/api/routes/reports/model.js | 2 + src/server.js | 4 +- src/test/testCognicityServer.js | 78 +++++++++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index 478ec83..d86d783 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -26,7 +26,9 @@ export default ({ config, db, logger }) => { (req, res, next) => reports(config, db, logger).all(req.query.timeperiod, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); @@ -43,7 +45,9 @@ export default ({ config, db, logger }) => { (req, res, next) => reports(config, db, logger).byId(req.params.id) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 3414060..a35cbf9 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -25,6 +25,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }), @@ -44,6 +45,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }) diff --git a/src/server.js b/src/server.js index a35bff3..7e4cd50 100644 --- a/src/server.js +++ b/src/server.js @@ -60,9 +60,11 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej }) .catch((err) => { + /* istanbul ignore next */ logger.error('DB Connection error: ' + err); + /* istanbul ignore next */ logger.error('Fatal error: Application shutting down'); - + /* istanbul ignore next */ // We cannot continue without a DB, reject reject(err); }) diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 03f98b4..6a21cb9 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -1,5 +1,3 @@ -/*/reports/0*/ - // Testing for CogniCity Server // Unit tests run together against live app, and database @@ -26,10 +24,19 @@ const logger = new (winston.Logger)({ ] }); +// need to put in dummy data to database. + // Create a top-level testing harness describe('Cognicity Server Testing Harness', function() { it('Server starts', function(done){ + + // Test optional server params + config.COMPRESS = true; + config.CORS = true; + config.RESPONSE_TIME = true; + + // Initialise init(config, initializeDb, routes, logger).then((app) => { describe('Top level API endpoint', function(){ @@ -98,11 +105,27 @@ describe('Cognicity Server Testing Harness', function() { }); }); - // Catch report by city error - it('Get reports by city /reports?city=xxx', function(done){ + // Catch report by city error + it('Get reports by city /reports?city=xxx', function(done){ + test.httpAgent(app) + .get('/reports?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports + it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ test.httpAgent(app) - .get('/reports?city=xxx') - .expect(400) + .get('/reports/1') + .expect(200) .expect('Content-Type', /json/) .end(function(err, res){ if (err) { @@ -113,6 +136,7 @@ describe('Cognicity Server Testing Harness', function() { } }); }); + }); // Cards endpoint @@ -277,6 +301,44 @@ describe('Cognicity Server Testing Harness', function() { // Floods endpoint describe('Flood areas endpoint', function(){ + // Test put flood auth + it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + test.httpAgent(app) + .put('/floods/5') + .send({ + "state": "2" + }) + .expect(401) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Test delete flood auth + it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + test.httpAgent(app) + .delete('/floods/5') + .send({ + "username": "testing" + }) + .expect(401) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + // Put a flood /*it ('Put a flood (PUT /floods/:id)', function(done){ let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } @@ -315,7 +377,7 @@ describe('Cognicity Server Testing Harness', function() { }); // Can get reports in CAP format - it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + /*it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) @@ -328,7 +390,7 @@ describe('Cognicity Server Testing Harness', function() { done(); } }); - }); + });*/ }); // Cards end to end test From bf1199376ff9e8277f549e4c521b0a10b850af70 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 12:58:21 -0400 Subject: [PATCH 028/160] Added dummy data inserts for testing --- src/test/testCognicityServer.js | 214 +++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 73 deletions(-) diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 6a21cb9..605c9c1 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -24,11 +24,39 @@ const logger = new (winston.Logger)({ ] }); -// need to put in dummy data to database. +// Put some sample data in the database +const pg = require('pg'); + +// need to put in dummy data to database? // Create a top-level testing harness describe('Cognicity Server Testing Harness', function() { + // Test pg config + let reportid = 0; + let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; + + before ('Insert dummy data', function(done){ + + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query("INSERT INTO cognicity.all_reports (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey", function(err, result){ + reportid = result.rows[0].pkey; + pgDone(); + }); + }); + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query("INSERT INTO cognicity.rem_status (local_area, state, last_updated) VALUES (1, 1, now())", function(err){ + if (err){ + console.log(err); + } + else { + done(); + pgDone(); + } + }); + }); + }); + it('Server starts', function(done){ // Test optional server params @@ -71,74 +99,6 @@ describe('Cognicity Server Testing Harness', function() { }); }); - // Reports endpoint - describe('Reports endpoint', function() { - // Can get reports - it('Get all reports (GET /reports)', function(done){ - test.httpAgent(app) - .get('/reports') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports by city - it('Get reports by city /reports?city=jbd', function(done){ - test.httpAgent(app) - .get('/reports?city=jbd') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Catch report by city error - it('Get reports by city /reports?city=xxx', function(done){ - test.httpAgent(app) - .get('/reports?city=xxx') - .expect(400) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports - it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ - test.httpAgent(app) - .get('/reports/1') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - }); - // Cards endpoint describe('Cards endpoint', function() { // Cards @@ -340,7 +300,8 @@ describe('Cognicity Server Testing Harness', function() { }); // Put a flood - /*it ('Put a flood (PUT /floods/:id)', function(done){ + /* + it ('Put a flood (PUT /floods/:id)', function(done){ let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } test.httpAgent(app) .put('/floods/5') @@ -377,7 +338,7 @@ describe('Cognicity Server Testing Harness', function() { }); // Can get reports in CAP format - /*it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) @@ -390,7 +351,7 @@ describe('Cognicity Server Testing Harness', function() { done(); } }); - });*/ + }); }); // Cards end to end test @@ -490,7 +451,114 @@ describe('Cognicity Server Testing Harness', function() { }); }); - return (done()) + // Reports endpoint + describe('Reports endpoint', function() { + // Can get reports + it('Get all reports (GET /reports)', function(done){ + test.httpAgent(app) + .get('/reports') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports by city + it('Get reports by city /reports?city=jbd', function(done){ + test.httpAgent(app) + .get('/reports?city=jbd') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch report by city error + it('Get reports by city /reports?city=xxx', function(done){ + test.httpAgent(app) + .get('/reports?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports + it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ + test.httpAgent(app) + .get('/reports/'+reportid) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + //return (done()) + }); + + after ('Remove dummy data', function(done){ + let queryObject = { + text: "DELETE FROM cognicity.all_reports WHERE pkey = $1;", + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + done(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + after ('Remove dummy data', function(done){ + let queryObject = { + text: "DELETE FROM cognicity.rem_status WHERE local_area = 1", + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + done(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + return (done()) }); }); }); From d9d57156645c22a890cf8332390d3e36d3d0f103 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:26:34 -0400 Subject: [PATCH 029/160] working on test coverage of endpoints --- src/api/routes/cards/model.js | 2 + src/api/routes/feeds/index.js | 8 +- src/api/routes/feeds/model.js | 96 ++++++++-------- src/api/routes/floodgauges/index.js | 4 + src/config.js | 2 +- src/lib/cap.js | 3 + src/test/testCognicityServer.js | 165 +++++++++++++++++++++++++--- 7 files changed, 213 insertions(+), 67 deletions(-) diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index 4ee0f33..d41f4f9 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -17,6 +17,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }), @@ -42,6 +43,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }), diff --git a/src/api/routes/feeds/index.js b/src/api/routes/feeds/index.js index 396b8c5..18e4898 100644 --- a/src/api/routes/feeds/index.js +++ b/src/api/routes/feeds/index.js @@ -19,7 +19,7 @@ export default ({ config, db, logger }) => { post_id: Joi.number().integer().required(), created_at: Joi.date().iso().required(), title: Joi.string().allow(''), - text: Joi.string().allow(''), + text: Joi.string().allow('').required(), image_url: Joi.string(), qlue_city: Joi.string().valid(config.API_FEEDS_QLUE_CITIES).required(), disaster_type: Joi.string().valid(config.API_FEEDS_QLUE_DISASTER_TYPES).required(), @@ -32,7 +32,9 @@ export default ({ config, db, logger }) => { (req, res, next) => feeds(config, db, logger).addQlueReport(req.body) .then((data) => res.json(data)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); @@ -45,7 +47,7 @@ export default ({ config, db, logger }) => { contribution_id: Joi.number().integer().required(), created_at: Joi.date().iso().required(), title: Joi.string().allow(''), - text: Joi.string().allow(''), + text: Joi.string().allow('').required(), url: Joi.string().allow(''), image_url: Joi.string(), disaster_type: Joi.string().valid(config.API_FEEDS_DETIK_DISASTER_TYPES).required(), @@ -58,7 +60,9 @@ export default ({ config, db, logger }) => { (req, res, next) => feeds(config, db, logger).addDetikReport(req.body) .then((data) => res.json(data)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index b547858..2831a5e 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -2,51 +2,53 @@ import Promise from 'bluebird'; export default (config, db, logger) => ({ - // Add a new qlue report - addQlueReport: (body) => new Promise((resolve, reject) => { - - // Setup query - let query = `INSERT INTO ${config.TABLE_FEEDS_QLUE} - (post_id, created_at, disaster_type, text, image_url, title, qlue_city, the_geom) - VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_Point($8,$9),4326))`; - - // Setup values - let values = [ body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, - body.title, body.qlue_city, body.location.lng, body.location.lat ]; - - // Execute - logger.debug(query, values); - db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then(() => resolve({ post_id: body.post_id, created: true })) - .catch((err) => { - if (err.constraint === 'reports_post_id_key') - resolve({ post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table` }); - else - reject(err); - }); - }), - - // Add a detik report - addDetikReport: (body) => new Promise((resolve, reject) => { - - // Setup query - let query = `INSERT INTO ${config.TABLE_FEEDS_DETIK} - (contribution_id, created_at, disaster_type, title, text, url, image_url, the_geom) - VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_POINT($8, $9),4326))`; - - // Setup values - let values = [ body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, - body.url, body.image_url, body.location.longitude, body.location.latitude ]; - - // Execute - logger.debug(query, values); - db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then(() => resolve({ contribution_id: body.contribution_id, created: true })) - .catch((err) => { - if (err.constraint === 'reports_contribution_id_key') - resolve({ contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); - else - reject(err); - }); - }) + // Add a new qlue report + addQlueReport: (body) => new Promise((resolve, reject) => { + + // Setup query + let query = `INSERT INTO ${config.TABLE_FEEDS_QLUE} + (post_id, created_at, disaster_type, text, image_url, title, qlue_city, the_geom) + VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_Point($8,$9),4326))`; + + // Setup values + let values = [ body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, + body.title, body.qlue_city, body.location.lng, body.location.lat ]; + + // Execute + logger.debug(query, values); + db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + .then(() => resolve({ post_id: body.post_id, created: true })) + .catch((err) => { + if (err.constraint === 'reports_post_id_key') + resolve({ post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table` }); + else + /* istanbul ignore next */ + reject(err); + }); + }), + + // Add a detik report + addDetikReport: (body) => new Promise((resolve, reject) => { + + // Setup query + let query = `INSERT INTO ${config.TABLE_FEEDS_DETIK} + (contribution_id, created_at, disaster_type, title, text, url, image_url, the_geom) + VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_POINT($8, $9),4326))`; + + // Setup values + let values = [ body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, + body.url, body.image_url, body.location.longitude, body.location.latitude ]; + + // Execute + logger.debug(query, values); + db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + .then(() => resolve({ contribution_id: body.contribution_id, created: true })) + .catch((err) => { + if (err.constraint === 'reports_contribution_id_key') + resolve({ contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); + else + /* istanbul ignore next */ + reject(err); + }); + }) }); diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index 42f4ce5..21afe6e 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -26,7 +26,9 @@ export default ({ config, db, logger }) => { (req, res, next) => floodgauges(config, db, logger).all() .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); @@ -43,7 +45,9 @@ export default ({ config, db, logger }) => { (req, res, next) => floodgauges(config, db, logger).byId(req.params.id) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/config.js b/src/config.js index f408ad0..c2c0588 100644 --- a/src/config.js +++ b/src/config.js @@ -13,7 +13,7 @@ export default { AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://data.petabencana.id', AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'client_id', AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com', - AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret', + AUTH0_SECRET: process.env.AUTH0_SECRET || '', BODY_LIMIT: process.env.BODY_LIMIT || '100kb', CACHE: process.env.CACHE === 'true' || false, CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute', diff --git a/src/lib/cap.js b/src/lib/cap.js index 9ed8510..18807df 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -168,6 +168,7 @@ module.exports = class Cap { featurePolygons = feature.geometry.coordinates; } else { self.logger.error( "Cap: createInfo(): Geometry type '" + feature.geometry.type + "' not supported" ); + /* istanbul ignore next */ return; } @@ -178,7 +179,9 @@ module.exports = class Cap { for (let polygonIndex=0; polygonIndex 1 ) { + /* istanbul ignore next */ self.logger.error( "Cap: createInfo(): Polygon with interior rings is not supported" ); + /* istanbul ignore next */ return; } diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 605c9c1..062a115 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -36,16 +36,20 @@ describe('Cognicity Server Testing Harness', function() { let reportid = 0; let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; +// Insert some dummy data before ('Insert dummy data', function(done){ + let queryObject = { + text: `INSERT INTO ${config.TABLE_REPORTS} (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey` + }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query("INSERT INTO cognicity.all_reports (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey", function(err, result){ + client.query(queryObject, function(err, result){ reportid = result.rows[0].pkey; pgDone(); }); }); pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query("INSERT INTO cognicity.rem_status (local_area, state, last_updated) VALUES (1, 1, now())", function(err){ + client.query(`INSERT INTO ${config.TABLE_REM_STATUS} (local_area, state, last_updated) VALUES (1, 1, now())`, function(err){ if (err){ console.log(err); } @@ -58,7 +62,6 @@ describe('Cognicity Server Testing Harness', function() { }); it('Server starts', function(done){ - // Test optional server params config.COMPRESS = true; config.CORS = true; @@ -155,11 +158,47 @@ describe('Cognicity Server Testing Harness', function() { // Feeds endpoint describe('Feeds endpoint', function() { - // Can get cities - it('Return 400 for post to feeds/qlue', function(done){ + // Can post to qlue + it('Return 200 for post to feeds/qlue', function(done){ test.httpAgent(app) .post('/feeds/qlue') - .expect(400) + .send({ + "post_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "qlue_city": "surabaya", + "disaster_type": "flood", + "text": "test report", + "location":{ + "lat":45, + "lng":45 + } + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + // Can post to detik + it('Return 200 for post to feeds/detik', function(done){ + test.httpAgent(app) + .post('/feeds/detik') + .send({ + "contribution_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "disaster_type": "flood", + "location":{ + "latitude":45, + "longitude":45 + }, + "text":"test report" + }) + .expect(200) .expect('Content-Type', /json/) .end(function(err, res){ if (err) { @@ -302,7 +341,7 @@ describe('Cognicity Server Testing Harness', function() { // Put a flood /* it ('Put a flood (PUT /floods/:id)', function(done){ - let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } + //let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } test.httpAgent(app) .put('/floods/5') .set(auth) @@ -337,6 +376,22 @@ describe('Cognicity Server Testing Harness', function() { }); }); + // Get floods + it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ + test.httpAgent(app) + .get('/floods/?format=json&geoformat=geojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + // Can get reports in CAP format it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ test.httpAgent(app) @@ -389,7 +444,7 @@ describe('Cognicity Server Testing Harness', function() { .send({ "water_depth": 20, "text": "big flood", - "created_at": "2017-02-21T07:00:00+0700", + "created_at": "2017-06-07T07:00:00+0700", "location": { "lat": -6.4, "lng": 106.6 @@ -516,21 +571,78 @@ describe('Cognicity Server Testing Harness', function() { } }); }); - //return (done()) }); - after ('Remove dummy data', function(done){ - let queryObject = { - text: "DELETE FROM cognicity.all_reports WHERE pkey = $1;", - values: [ reportid ] + // Removes dummy data + describe('Cleans up', function() { + it ('Removes dummy report data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE pkey = $1;`, + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from reports table + it ('Removes dummy flood data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 1`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from qlue table + it ('Removes dummy qlue data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ client.query(queryObject, function(){ if (err) { console.log(err.message) pgDone(); + } + else { + pgDone(); done(); } + }); + }); + }); + + // Remove dummy qlue data from all reports table + it ('Removes dummy qlue data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'qlue'`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } else { pgDone(); done(); @@ -539,18 +651,36 @@ describe('Cognicity Server Testing Harness', function() { }); }); - after ('Remove dummy data', function(done){ - let queryObject = { - text: "DELETE FROM cognicity.rem_status WHERE local_area = 1", - values: [ reportid ] + // Remove dummy data from detik table + it ('Removes dummy detik data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ client.query(queryObject, function(){ if (err) { console.log(err.message) pgDone(); + } + else { + pgDone(); done(); } + }); + }); + }); + + // Remove dummy detik data from all reports table + it ('Removes dummy detik data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'detik'`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } else { pgDone(); done(); @@ -560,5 +690,6 @@ describe('Cognicity Server Testing Harness', function() { }); return (done()) }); + }); }); }); From f5425a1c4f0eacaea8d83dd9ac9694d621238f8a Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:27:41 -0400 Subject: [PATCH 030/160] fixed github url refs in package --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ff5ef61..a5adf15 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "no-unused-vars": 1 } }, - "repository": "https://github.com/urbanriskmap/cognicity-server-v3", - "issues": "https://github.com/urbanriskmap/cognicity-server-v3/issues", + "repository": "https://github.com/urbanriskmap/cognicity-server", + "issues": "https://github.com/urbanriskmap/cognicity-server/issues", "author": "Tomas Holderness et al., MIT Urban Risk Lab", "license": "GPL-3.0", "dependencies": { From 2702b9952cf1892030037da9e663f6afbaed6a1d Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:28:23 -0400 Subject: [PATCH 031/160] added coverage to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ae8b33d..b4d8283 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ .idea .swp +# nyc test coverage +.nyc_output + /coverage /dist /node_modules From 7f7a1f8c81ca3ec6539627e0e635bb4984c97b6b Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:30:43 -0400 Subject: [PATCH 032/160] changed to use travis beta w/ pg 9.6 --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dbca825..855dc30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,18 @@ +dist: trusty language: node_js node_js: - "6.10.0" sudo: false addons: - postgresql: "9.4" + postgresql: "9.6" services: postgresql branches: only: - master - dev + - server-object-refactor before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi From 7a395fc3d80fae2ba3393b121b8faf7ddc7b752d Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:43:51 -0400 Subject: [PATCH 033/160] removed broken auth test --- src/test/testCognicityServer.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 062a115..ea76dc2 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -338,28 +338,6 @@ describe('Cognicity Server Testing Harness', function() { }); }); - // Put a flood - /* - it ('Put a flood (PUT /floods/:id)', function(done){ - //let auth = { headers: { 'Authorization': 'Bearer ' + config.AUTH0_SECRET } } - test.httpAgent(app) - .put('/floods/5') - .set(auth) - .send({ - "state": "2" - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - });*/ - // Get floods it('Get floods (GET /floods)', function(done){ test.httpAgent(app) From 50bf6c99f07b0e4810b4deac6e44d2f3a78d464f Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:52:23 -0400 Subject: [PATCH 034/160] updated readme with info about testing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ed004b..602320b 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,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). Currently tests only run eslint. +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. See src/test/ for scripts. ### Issue Tracking Issues are tracked using [GitHub](https://github.com/urbanriskmap/cognicity-server/issues) From d6e057493e45047c4535ce436de53ba6aebfca92 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 15:59:29 -0400 Subject: [PATCH 035/160] added longer time outs to mocha tests to help travis --- src/test/testCognicityServer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index ea76dc2..2310965 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -340,6 +340,7 @@ describe('Cognicity Server Testing Harness', function() { // Get floods it('Get floods (GET /floods)', function(done){ + this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods') .expect(200) @@ -356,6 +357,7 @@ describe('Cognicity Server Testing Harness', function() { // Get floods it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ + this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods/?format=json&geoformat=geojson') .expect(200) @@ -372,6 +374,7 @@ describe('Cognicity Server Testing Harness', function() { // Can get reports in CAP format it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) From 6540d1e5243e0085d49122a1a9fcbf64ffb1b208 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 16:06:43 -0400 Subject: [PATCH 036/160] added coveralls support --- .travis.yml | 2 ++ package.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 855dc30..676f1cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,5 @@ before_install: install: - npm install + +after_success: npm run coverage diff --git a/package.json b/package.json index a5adf15..8f9efc4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "eslint src & npm run mocha" }, - "nyc": { + "nyc": { "exclude": [ "**/test/**", "build" @@ -65,6 +65,7 @@ "babel-preset-es2015": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "babel-register": "^6.24.1", + "coveralls": "^2.13.1", "eslint": "^3.19.0", "mocha": "^3.3.0", "nodemon": "^1.11.0", From 7ba7cd10e7e0d755948e5ccb659db3c081460432 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 16:33:48 -0400 Subject: [PATCH 037/160] added coverage badge --- README.md | 2 ++ src/test/testCognicityServer.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 602320b..9fb9a70 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ API Server for CogniCity [![Build Status](https://travis-ci.org/urbanriskmap/cognicity-server.svg?branch=master)](https://travis-ci.org/urbanriskmap/cognicity-server) +[![Coverage Status](https://coveralls.io/repos/github/urbanriskmap/cognicity-server/badge.svg?branch=server-object-refactor)](https://coveralls.io/github/urbanriskmap/cognicity-server?branch=server-object-refactor) + ### Summary This is the NodeJS server which runs the CogniCity Data API used by Urban Risk Map instances, such as [PetaBencana.id](https://petabencana.id) site. diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 2310965..851ffa4 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -395,7 +395,7 @@ describe('Cognicity Server Testing Harness', function() { let cardId = '0'; - // Request a card, submit and get resulting card detailsß + // Request a card, submit and get resulting card details it('Get card one time link', function(done){ test.httpAgent(app) .post('/cards') From bcf3e04199e6bae5b620045afbbce80fce402155 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 16:50:54 -0400 Subject: [PATCH 038/160] updated tests for new PUT /cards endpoint --- src/test/testCognicityServer.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js index 851ffa4..1794d07 100644 --- a/src/test/testCognicityServer.js +++ b/src/test/testCognicityServer.js @@ -423,7 +423,11 @@ describe('Cognicity Server Testing Harness', function() { test.httpAgent(app) .put('/cards/'+cardId) .send({ - "water_depth": 20, + "disaster_type":"flood", + "card_data":{ + "flood_depth": 20, + "report_type": "flood" + }, "text": "big flood", "created_at": "2017-06-07T07:00:00+0700", "location": { From 6df8afa2c2440bc062f7da16bd797e18b4635ee8 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 17:23:35 -0400 Subject: [PATCH 039/160] updated node version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 676f1cf..cf4dfb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ dist: trusty language: node_js node_js: - - "6.10.0" + - "7" sudo: false addons: From d817e3457ee3422131af4cb7e48a8d8983154238 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 7 Jun 2017 17:36:30 -0400 Subject: [PATCH 040/160] fixed tabbing --- src/api/routes/floodgauges/index.js | 78 ++++++++++++++--------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index 21afe6e..d3e8b95 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -12,45 +12,45 @@ import validate from 'celebrate'; export default ({ config, db, logger }) => { - let api = Router(); - - // Get a list of all flood gauge reports - api.get('/', cacheResponse('1 minute'), - validate({ - query: { - city: Joi.any().valid(config.REGION_CODES), - format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } - }), - (req, res, next) => floodgauges(config, db, logger).all() - .then((data) => handleGeoResponse(data, req, res, next)) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }) + let api = Router(); + + // Get a list of all flood gauge reports + api.get('/', cacheResponse('1 minute'), + validate({ + query: { + city: Joi.any().valid(config.REGION_CODES), + format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) + } + }), + (req, res, next) => floodgauges(config, db, logger).all() + .then((data) => handleGeoResponse(data, req, res, next)) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }) ); - // Get a single flood gauge report - api.get('/:id', cacheResponse('1 minute'), - validate({ - params: { id: Joi.number().integer().required() } , - query: { - format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } - }), - (req, res, next) => floodgauges(config, db, logger).byId(req.params.id) - .then((data) => handleGeoResponse(data, req, res, next)) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }) - ); - - return api; + // Get a single flood gauge report + api.get('/:id', cacheResponse('1 minute'), + validate({ + params: { id: Joi.number().integer().required() } , + query: { + format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) + } + }), + (req, res, next) => floodgauges(config, db, logger).byId(req.params.id) + .then((data) => handleGeoResponse(data, req, res, next)) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }) + ); + + return api; }; From f98089ed90329ffb027948b0ba4d61158b2111f4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 10:34:22 -0400 Subject: [PATCH 041/160] manually reverted updates to card data structure for petabencana.id compatibility --- src/api/routes/cards/index.js | 6 ++++++ src/api/routes/cards/model.js | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index 9392e37..ca7e6fb 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -94,6 +94,11 @@ export default ({ config, db, logger }) => { api.put('/:cardId', validate({ params: { cardId: Joi.string().min(7).max(14).required() }, body: Joi.object().keys({ + + water_depth: Joi.number().integer().min(0).max(200).required(), + /*** TODO - re-enable this new card data structure when PetaBencana.id client-code is ready. + ... and remove hard-coded water_depth parameter above + disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(), card_data: Joi.object() .keys({ @@ -105,6 +110,7 @@ export default ({ config, db, logger }) => { is: 'flood', then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true }), + ***/ text: Joi.string().allow(''), image_url: Joi.string().allow(''), created_at: Joi.date().iso().required(), diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index e4e0fc4..e233b88 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -50,6 +50,14 @@ export default (config, db, logger) => ({ // Setup our queries let queries = [ + { + query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} + (card_id, card_data, text, created_at, disaster_type, status, the_geom) + VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, + values: [ card.card_id, { flood_depth: body.water_depth }, body.text, + body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat ] + }, + /*** TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) @@ -57,6 +65,7 @@ export default (config, db, logger) => ({ values: [ card.card_id, body.card_data, body.text, body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] }, + ***/ { query: `UPDATE ${config.TABLE_GRASP_CARDS} SET received = TRUE WHERE card_id = $1`, From 040570ccc05e71daaad18e25136f08cc4dff1301 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 13:01:07 -0400 Subject: [PATCH 042/160] Refactored tests into separate files --- src/db.js | 1 - src/test/index.js | 210 ++++++++++ src/test/testCards.js | 139 +++++++ src/test/testCities.js | 22 ++ src/test/testCognicityServer.js | 680 -------------------------------- src/test/testFeeds.js | 58 +++ src/test/testFloodgauges.js | 54 +++ src/test/testFloods.js | 96 +++++ src/test/testInfrastructure.js | 38 ++ src/test/testReports.js | 70 ++++ src/test/testServer.js | 35 ++ 11 files changed, 722 insertions(+), 681 deletions(-) create mode 100644 src/test/index.js create mode 100644 src/test/testCards.js create mode 100644 src/test/testCities.js delete mode 100644 src/test/testCognicityServer.js create mode 100644 src/test/testFeeds.js create mode 100644 src/test/testFloodgauges.js create mode 100644 src/test/testFloods.js create mode 100644 src/test/testInfrastructure.js create mode 100644 src/test/testReports.js create mode 100644 src/test/testServer.js diff --git a/src/db.js b/src/db.js index 816fc87..1da9d31 100755 --- a/src/db.js +++ b/src/db.js @@ -22,5 +22,4 @@ export default (config, logger) => new Promise((resolve, reject) => { logger.error(err); reject(err); }); - }); diff --git a/src/test/index.js b/src/test/index.js new file mode 100644 index 0000000..ee6c2d4 --- /dev/null +++ b/src/test/index.js @@ -0,0 +1,210 @@ +// Testing for CogniCity Server +// Unit tests run together against live app, and database + +// Import config +import config from '../config'; + +// Import DB initializer +import initializeDb from '../db'; + +// Import the routes +import routes from '../api'; + +// Import server object +import { init } from '../server.js'; + +// Mock logger object for app +const winston = require('winston'); +const logger = new (winston.Logger)({ + transports: [ + new (winston.transports.Console)({ raw: true }), + ] +}); + +// Import tests +import testServer from './testServer.js'; +import testCards from './testCards.js'; +import testCities from './testCities.js'; +import testFeeds from './testFeeds.js'; +import testFloodgauges from './testFloodgauges.js'; +import testInfrastructure from './testInfrastructure.js'; +import testFloods from './testFloods.js'; +import testReports from './testReports.js'; + +// Put some sample data in the database +const pg = require('pg'); + +// need to put in dummy data to database? + +// Create a top-level testing harness +describe('Cognicity Server Testing Harness', function() { + +// PG config string for dummy data inserts +let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; + +// Gloal report value +let reportid = 0; + +// Insert some dummy data + before ('Insert dummy data', function(done){ + let queryObject = { + text: `INSERT INTO ${config.TABLE_REPORTS} (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey` + }; + + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(err, result){ + reportid = result.rows[0].pkey; + pgDone(); + }); + }); + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(`INSERT INTO ${config.TABLE_REM_STATUS} (local_area, state, last_updated) VALUES (1, 1, now())`, function(err){ + if (err){ + console.log(err); + } + else { + done(); + pgDone(); + } + }); + }); + }); + + it('Server starts', function(done){ + // Test optional server params + config.COMPRESS = true; + config.CORS = true; + config.RESPONSE_TIME = true; + + // Initialise + init(config, initializeDb, routes, logger).then((app) => { + + testServer(app); + testCards(app); + testCities(app); + testFeeds(app); + testFloodgauges(app); + testInfrastructure(app); + testFloods(app); + testReports(app, reportid); + + // Removes dummy data + describe('Cleans up', function() { + it ('Removes dummy report data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE pkey = $1;`, + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from reports table + it ('Removes dummy flood data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 1`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from qlue table + it ('Removes dummy qlue data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy qlue data from all reports table + it ('Removes dummy qlue data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'qlue'`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from detik table + it ('Removes dummy detik data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy detik data from all reports table + it ('Removes dummy detik data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'detik'`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + return (done()) + }); + }); + }); +}); diff --git a/src/test/testCards.js b/src/test/testCards.js new file mode 100644 index 0000000..f226231 --- /dev/null +++ b/src/test/testCards.js @@ -0,0 +1,139 @@ +const test = require('unit.js'); + +export default function (app){ + // Cards endpoint + describe('Cards endpoint', function() { + // Cards + it('Return 404 if card requested without ID (GET /cards)', function(done){ + test.httpAgent(app) + .get('/cards') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports + it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done){ + test.httpAgent(app) + .get('/cards/1') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); + + // Cards end to end test + describe('End-to-end card test', function() { + + let cardId = '0'; + + // Request a card, submit and get resulting card details + it('Get card one time link', function(done){ + test.httpAgent(app) + .post('/cards') + .send({ + "username": "testuser", + "network": "twitter", + "language": "en" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + cardId = res.body.cardId + test.value(res.body.created).is(true); + done(); + } + }); + }); + + // Request a card, submit and get resulting report + 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": "big flood", + "created_at": "2017-06-07T07:00:00+0700", + "location": { + "lat": -6.4, + "lng": 106.6 + } + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + console.log(res.body); + done(); + } + }); + }); + + // Request a card, submit and get resulting report + it('Put card data', function(done){ + test.httpAgent(app) + .get('/cards/'+cardId) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.result.card_id).is(cardId); + test.value(res.body.result.username).is('testuser'); + test.value(res.body.result.network).is('twitter') + test.value(res.body.result.language).is('en') + test.value(res.body.result.report.text).is('big flood') + done(); + } + }); + }); + + // Update a card + it('Update card data', function(done){ + test.httpAgent(app) + .patch('/cards/'+cardId) + .send({ + "water_depth": 20, + "text": "big flood", + "image_url": "dummy image url" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testCities.js b/src/test/testCities.js new file mode 100644 index 0000000..4270262 --- /dev/null +++ b/src/test/testCities.js @@ -0,0 +1,22 @@ +const test = require('unit.js'); + +export default function (app){ + // Cities endpoint + describe('Cities endpoint', function() { + // Can get cities + it('Return 200 for cities list', function(done){ + test.httpAgent(app) + .get('/cities') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testCognicityServer.js b/src/test/testCognicityServer.js deleted file mode 100644 index 1794d07..0000000 --- a/src/test/testCognicityServer.js +++ /dev/null @@ -1,680 +0,0 @@ -// Testing for CogniCity Server -// Unit tests run together against live app, and database - -// Import Unit.js -const test = require('unit.js'); - -// Import config -import config from '../config'; - -// Import DB initializer -import initializeDb from '../db'; - -// Import the routes -import routes from '../api'; - -// Import server object -import { init } from '../server.js'; - -// Mock logger object for app -const winston = require('winston'); -const logger = new (winston.Logger)({ - transports: [ - new (winston.transports.Console)({ raw: true }), - ] -}); - -// Put some sample data in the database -const pg = require('pg'); - -// need to put in dummy data to database? - -// Create a top-level testing harness -describe('Cognicity Server Testing Harness', function() { - - // Test pg config - let reportid = 0; - let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; - -// Insert some dummy data - before ('Insert dummy data', function(done){ - let queryObject = { - text: `INSERT INTO ${config.TABLE_REPORTS} (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey` - }; - - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(err, result){ - reportid = result.rows[0].pkey; - pgDone(); - }); - }); - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(`INSERT INTO ${config.TABLE_REM_STATUS} (local_area, state, last_updated) VALUES (1, 1, now())`, function(err){ - if (err){ - console.log(err); - } - else { - done(); - pgDone(); - } - }); - }); - }); - - it('Server starts', function(done){ - // Test optional server params - config.COMPRESS = true; - config.CORS = true; - config.RESPONSE_TIME = true; - - // Initialise - init(config, initializeDb, routes, logger).then((app) => { - - describe('Top level API endpoint', function(){ - it('Gets current API version', function(done){ - test.httpAgent(app) - .get('/') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - it('Can handle unknown routes', function(done){ - test.httpAgent(app) - .get('/moon') - .expect(404) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Cards endpoint - describe('Cards endpoint', function() { - // Cards - it('Return 404 if card requested without ID (GET /cards)', function(done){ - test.httpAgent(app) - .get('/cards') - .expect(404) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports - it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done){ - test.httpAgent(app) - .get('/cards/1') - .expect(400) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Cities endpoint - describe('Cities endpoint', function() { - // Can get cities - it('Return 200 for cities list', function(done){ - test.httpAgent(app) - .get('/cities') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Feeds endpoint - describe('Feeds endpoint', function() { - // Can post to qlue - it('Return 200 for post to feeds/qlue', function(done){ - test.httpAgent(app) - .post('/feeds/qlue') - .send({ - "post_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "qlue_city": "surabaya", - "disaster_type": "flood", - "text": "test report", - "location":{ - "lat":45, - "lng":45 - } - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - // Can post to detik - it('Return 200 for post to feeds/detik', function(done){ - test.httpAgent(app) - .post('/feeds/detik') - .send({ - "contribution_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "disaster_type": "flood", - "location":{ - "latitude":45, - "longitude":45 - }, - "text":"test report" - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Flood gauges endpoint - describe('Flood gauges endpoint', function() { - // Can get flood gauge data - it('Return 200 for get /floodgauges', function(done){ - test.httpAgent(app) - .get('/floodgauges') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Catch invalid city in floodgauge - it('Return 400 for get /floodgauges?city=xxx', function(done){ - test.httpAgent(app) - .get('/floodgauges?city=xxx') - .expect(400) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Catch invalid floodgauge id - it('Return 404 for get /floodgauges/:id', function(done){ - test.httpAgent(app) - .get('/floodgauges/0') - .expect(404) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Infrastructure endpoint - describe('Infrastructure endpoint', function() { - // Catch invalid top-level infrastructure endpoint - it('Return 404 for get /infrastructure', function(done){ - test.httpAgent(app) - .get('/infrastructure') - .expect(404) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Return waterways infrastructure - it('Return 200 for get /infrastructure/waterways', function(done){ - test.httpAgent(app) - .get('/infrastructure/waterways') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Floods endpoint - describe('Flood areas endpoint', function(){ - - // Test put flood auth - it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ - test.httpAgent(app) - .put('/floods/5') - .send({ - "state": "2" - }) - .expect(401) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Test delete flood auth - it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ - test.httpAgent(app) - .delete('/floods/5') - .send({ - "username": "testing" - }) - .expect(401) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Get floods - it('Get floods (GET /floods)', function(done){ - this.timeout(15000); // a lot of data is returned - test.httpAgent(app) - .get('/floods') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Get floods - it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ - this.timeout(15000); // a lot of data is returned - test.httpAgent(app) - .get('/floods/?format=json&geoformat=geojson') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports in CAP format - it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ - this.timeout(15000); // a lot of data is returned - test.httpAgent(app) - .get('/floods?format=xml&geoformat=cap') - .expect(200) - .expect('Content-Type', /text/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Cards end to end test - describe('End-to-end card test', function() { - - let cardId = '0'; - - // Request a card, submit and get resulting card details - it('Get card one time link', function(done){ - test.httpAgent(app) - .post('/cards') - .send({ - "username": "testuser", - "network": "twitter", - "language": "en" - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - cardId = res.body.cardId - test.value(res.body.created).is(true); - done(); - } - }); - }); - - // Request a card, submit and get resulting report - 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": "big flood", - "created_at": "2017-06-07T07:00:00+0700", - "location": { - "lat": -6.4, - "lng": 106.6 - } - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - console.log(res.body); - done(); - } - }); - }); - - // Request a card, submit and get resulting report - it('Put card data', function(done){ - test.httpAgent(app) - .get('/cards/'+cardId) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - test.value(res.body.result.card_id).is(cardId); - test.value(res.body.result.username).is('testuser'); - test.value(res.body.result.network).is('twitter') - test.value(res.body.result.language).is('en') - test.value(res.body.result.report.text).is('big flood') - done(); - } - }); - }); - - // Update a card - it('Update card data', function(done){ - test.httpAgent(app) - .patch('/cards/'+cardId) - .send({ - "water_depth": 20, - "text": "big flood", - "image_url": "dummy image url" - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Reports endpoint - describe('Reports endpoint', function() { - // Can get reports - it('Get all reports (GET /reports)', function(done){ - test.httpAgent(app) - .get('/reports') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports by city - it('Get reports by city /reports?city=jbd', function(done){ - test.httpAgent(app) - .get('/reports?city=jbd') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Catch report by city error - it('Get reports by city /reports?city=xxx', function(done){ - test.httpAgent(app) - .get('/reports?city=xxx') - .expect(400) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - - // Can get reports - it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ - test.httpAgent(app) - .get('/reports/'+reportid) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res){ - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - done(); - } - }); - }); - }); - - // Removes dummy data - describe('Cleans up', function() { - it ('Removes dummy report data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE pkey = $1;`, - values: [ reportid ] - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - - // Remove dummy data from reports table - it ('Removes dummy flood data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 1`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - - // Remove dummy data from qlue table - it ('Removes dummy qlue data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - - // Remove dummy qlue data from all reports table - it ('Removes dummy qlue data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'qlue'`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - - // Remove dummy data from detik table - it ('Removes dummy detik data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - - // Remove dummy detik data from all reports table - it ('Removes dummy detik data', function(done){ - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'detik'`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ - if (err) { - console.log(err.message) - pgDone(); - } - else { - pgDone(); - done(); - } - }); - }); - }); - return (done()) - }); - }); - }); -}); diff --git a/src/test/testFeeds.js b/src/test/testFeeds.js new file mode 100644 index 0000000..65175ca --- /dev/null +++ b/src/test/testFeeds.js @@ -0,0 +1,58 @@ +const test = require('unit.js'); + +export default function (app){ + // Feeds endpoint + describe('Feeds endpoint', function() { + // Can post to qlue + it('Return 200 for post to feeds/qlue', function(done){ + test.httpAgent(app) + .post('/feeds/qlue') + .send({ + "post_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "qlue_city": "surabaya", + "disaster_type": "flood", + "text": "test report", + "location":{ + "lat":45, + "lng":45 + } + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + // Can post to detik + it('Return 200 for post to feeds/detik', function(done){ + test.httpAgent(app) + .post('/feeds/detik') + .send({ + "contribution_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "disaster_type": "flood", + "location":{ + "latitude":45, + "longitude":45 + }, + "text":"test report" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testFloodgauges.js b/src/test/testFloodgauges.js new file mode 100644 index 0000000..6ecf6e9 --- /dev/null +++ b/src/test/testFloodgauges.js @@ -0,0 +1,54 @@ +const test = require('unit.js'); + +export default function (app){ + // Flood gauges endpoint + describe('Flood gauges endpoint', function() { + // Can get flood gauge data + it('Return 200 for get /floodgauges', function(done){ + test.httpAgent(app) + .get('/floodgauges') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch invalid city in floodgauge + it('Return 400 for get /floodgauges?city=xxx', function(done){ + test.httpAgent(app) + .get('/floodgauges?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch invalid floodgauge id + it('Return 404 for get /floodgauges/:id', function(done){ + test.httpAgent(app) + .get('/floodgauges/0') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testFloods.js b/src/test/testFloods.js new file mode 100644 index 0000000..cf29a89 --- /dev/null +++ b/src/test/testFloods.js @@ -0,0 +1,96 @@ +const test = require('unit.js'); + +export default function (app){ + +// Floods endpoint +describe('Flood areas endpoint', function(){ + // Test put flood auth + it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + test.httpAgent(app) + .put('/floods/5') + .send({ + "state": "2" + }) + .expect(401) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Test delete flood auth + it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + test.httpAgent(app) + .delete('/floods/5') + .send({ + "username": "testing" + }) + .expect(401) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Get floods + it('Get floods (GET /floods)', function(done){ + this.timeout(15000); // a lot of data is returned + test.httpAgent(app) + .get('/floods') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Get floods + it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ + this.timeout(15000); // a lot of data is returned + test.httpAgent(app) + .get('/floods/?format=json&geoformat=geojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports in CAP format + it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + this.timeout(15000); // a lot of data is returned + test.httpAgent(app) + .get('/floods?format=xml&geoformat=cap') + .expect(200) + .expect('Content-Type', /text/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testInfrastructure.js b/src/test/testInfrastructure.js new file mode 100644 index 0000000..8d85674 --- /dev/null +++ b/src/test/testInfrastructure.js @@ -0,0 +1,38 @@ +const test = require('unit.js'); + +export default function (app){ + // Infrastructure endpoint + describe('Infrastructure endpoint', function() { + // Catch invalid top-level infrastructure endpoint + it('Return 404 for get /infrastructure', function(done){ + test.httpAgent(app) + .get('/infrastructure') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Return waterways infrastructure + it('Return 200 for get /infrastructure/waterways', function(done){ + test.httpAgent(app) + .get('/infrastructure/waterways') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testReports.js b/src/test/testReports.js new file mode 100644 index 0000000..880b12f --- /dev/null +++ b/src/test/testReports.js @@ -0,0 +1,70 @@ +const test = require('unit.js'); + +export default function (app, reportid){ + // Reports endpoint + describe('Reports endpoint', function() { + // Can get reports + it('Get all reports (GET /reports)', function(done){ + test.httpAgent(app) + .get('/reports') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports by city + it('Get reports by city /reports?city=jbd', function(done){ + test.httpAgent(app) + .get('/reports?city=jbd') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Catch report by city error + it('Get reports by city /reports?city=xxx', function(done){ + test.httpAgent(app) + .get('/reports?city=xxx') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can get reports + it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ + test.httpAgent(app) + .get('/reports/'+reportid) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} diff --git a/src/test/testServer.js b/src/test/testServer.js new file mode 100644 index 0000000..49b752a --- /dev/null +++ b/src/test/testServer.js @@ -0,0 +1,35 @@ +const test = require('unit.js'); + +export default function (app){ + describe('Top level API endpoint', function(){ + it('Gets current API version', function(done){ + test.httpAgent(app) + .get('/') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + it('Can handle unknown routes', function(done){ + test.httpAgent(app) + .get('/moon') + .expect(404) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} From 5856e75516a5f60bf7b21d2e2b04fbe0e262c9a4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 13:01:26 -0400 Subject: [PATCH 043/160] removed old test file --- src/api/routes/floods/test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/api/routes/floods/test.js diff --git a/src/api/routes/floods/test.js b/src/api/routes/floods/test.js deleted file mode 100644 index e69de29..0000000 From 1affbd1237c9195ceed4848e05d6734c29588f92 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 16:31:55 -0400 Subject: [PATCH 044/160] working tests of protected endpoints --- package.json | 1 + src/config.js | 4 ++-- src/lib/cap.js | 2 +- src/test/index.js | 10 +++++++--- src/test/testFloods.js | 42 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f717a20..711442c 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "babel-register": "^6.24.1", "coveralls": "^2.13.1", "eslint": "^3.19.0", + "jsonwebtoken": "^7.4.1", "mocha": "^3.3.0", "nodemon": "^1.11.0", "nyc": "^11.0.2", diff --git a/src/config.js b/src/config.js index f1c24f7..936157b 100644 --- a/src/config.js +++ b/src/config.js @@ -11,9 +11,9 @@ export default { API_FLOODGAUGE_REPORTS_TIME_WINDOW: process.env.API_FLOODGAUGE_REPORTS_TIME_WINDOW || 43200, API_FLOODGAUGE_REPORTS_LIMIT: process.env.API_FLOODGAUGE_REPORTS_LIMIT, AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://data.petabencana.id', - AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'client_id', + AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'auth0_client_id', AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com', - AUTH0_SECRET: process.env.AUTH0_SECRET || '', + AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret', BODY_LIMIT: process.env.BODY_LIMIT || '100kb', CACHE: process.env.CACHE === 'true' || false, CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute', diff --git a/src/lib/cap.js b/src/lib/cap.js index 18807df..ba25285 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -122,7 +122,7 @@ module.exports = class Cap { severity = "Severe"; levelDescription = "FLOODING OF OVER 150 CENTIMETERS"; } else { - self.logger.error("Cap: createInfo(): State " + feature.properties.state + " cannot be resolved to a severity"); + self.logger.silly("Cap: createInfo(): State " + feature.properties.state + " cannot be resolved to a severity"); return; } info.severity = severity; diff --git a/src/test/index.js b/src/test/index.js index ee6c2d4..dbdf07c 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -21,6 +21,8 @@ const logger = new (winston.Logger)({ ] }); + + // Import tests import testServer from './testServer.js'; import testCards from './testCards.js'; @@ -34,8 +36,6 @@ import testReports from './testReports.js'; // Put some sample data in the database const pg = require('pg'); -// need to put in dummy data to database? - // Create a top-level testing harness describe('Cognicity Server Testing Harness', function() { @@ -45,6 +45,10 @@ let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config. // Gloal report value let reportid = 0; +// Auth JWT support +const jwt = require('jsonwebtoken'); +let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_CLIENT_ID}); + // Insert some dummy data before ('Insert dummy data', function(done){ let queryObject = { @@ -85,7 +89,7 @@ let reportid = 0; testFeeds(app); testFloodgauges(app); testInfrastructure(app); - testFloods(app); + testFloods(app, token); testReports(app, reportid); // Removes dummy data diff --git a/src/test/testFloods.js b/src/test/testFloods.js index cf29a89..4f5497e 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -1,9 +1,10 @@ const test = require('unit.js'); -export default function (app){ +export default function (app, jwt){ // Floods endpoint describe('Flood areas endpoint', function(){ + // Test put flood auth it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ test.httpAgent(app) @@ -24,7 +25,7 @@ describe('Flood areas endpoint', function(){ }); // Test delete flood auth - it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + it ('Catch bad auth for delete a flood (PUT /floods/:id)', function(done){ test.httpAgent(app) .delete('/floods/5') .send({ @@ -42,6 +43,43 @@ describe('Flood areas endpoint', function(){ }); }); + // Test put flood auth + it ('Put a flood with auth(PUT /floods/:id)', function(done){ + test.httpAgent(app) + .put('/floods/5?username=testing') + .set({ 'Authorization': 'Bearer ' + jwt}) + .send({ + "state": "2" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Test delete flood auth + it ('Delete a flood (PUT /floods/:id)', function(done){ + test.httpAgent(app) + .delete('/floods/5?username=testing') + .set({ 'Authorization': 'Bearer ' + jwt}) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err){ + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + // Get floods it('Get floods (GET /floods)', function(done){ this.timeout(15000); // a lot of data is returned From 3a95d1333166cba669cc046142117e86104340d6 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 17:11:39 -0400 Subject: [PATCH 045/160] improved test coverage --- src/api/routes/cards/index.js | 5 ++++ src/api/routes/cards/model.js | 14 +++++++-- src/api/routes/cities/index.js | 2 ++ src/api/routes/floodgauges/model.js | 8 ++++- src/api/routes/floods/index.js | 10 +++++++ src/api/routes/floods/model.js | 41 +++++++++++++++++++------- src/api/routes/infrastructure/index.js | 2 ++ src/api/routes/infrastructure/model.js | 1 + src/api/routes/reports/model.js | 5 +++- src/lib/cap.js | 1 + src/lib/util.js | 6 +++- src/test/testCards.js | 18 ++++++++++- src/test/testFloods.js | 33 +++++++++++++++++++++ 13 files changed, 130 insertions(+), 16 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index b9b3303..af17e4c 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -169,7 +169,9 @@ export default ({ config, db, logger }) => { }; s3.getSignedUrl('putObject', s3params, (err, data) => { if (err){ + /* istanbul ignore next */ logger.error('could not get signed url from S3'); + /* istanbul ignore next */ logger.error(err); } else { var returnData = { @@ -194,7 +196,9 @@ export default ({ config, db, logger }) => { //res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); }) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) } @@ -232,6 +236,7 @@ export default ({ config, db, logger }) => { res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); }) .catch((err) => { + /* istanbul ignore next */ logger.error(err); /* istanbul ignore next */ next(err); diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index dbc53b0..12cb840 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -18,7 +18,10 @@ export default (config, db, logger) => ({ db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err)} + ); }), // Return specific card by id @@ -44,7 +47,10 @@ export default (config, db, logger) => ({ db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }) }), // Add an entry to the reports table and then update the card record accordingly @@ -79,7 +85,9 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) + /* istanbul ignore next */ .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }), @@ -113,7 +121,9 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) + /* istanbul ignore next */ .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }) diff --git a/src/api/routes/cities/index.js b/src/api/routes/cities/index.js index 340e30e..f08a687 100644 --- a/src/api/routes/cities/index.js +++ b/src/api/routes/cities/index.js @@ -25,7 +25,9 @@ export default ({ config, db, logger }) => { (req, res, next) => cities(config, db, logger).all() .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/api/routes/floodgauges/model.js b/src/api/routes/floodgauges/model.js index 6bf67b6..b114bc8 100644 --- a/src/api/routes/floodgauges/model.js +++ b/src/api/routes/floodgauges/model.js @@ -21,6 +21,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }), @@ -42,8 +43,13 @@ export default (config, db, logger) => ({ // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + /* istanbul ignore next */ .then((data) => resolve(data)) - .catch((err) => reject(err)); + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }) }); diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index 3e3c33c..1b7a9e8 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -75,7 +75,9 @@ export default ({ config, db, logger }) => { .catch((err) => next(err)) ) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } @@ -95,7 +97,9 @@ export default ({ config, db, logger }) => { floods(config, db, logger).all(req.query.city, req.query.minimum_state) .then((data) => res.status(200).json({statusCode: 200, result: data})) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }); } @@ -117,8 +121,11 @@ export default ({ config, db, logger }) => { clearCache(); res.status(200).json({localAreaId: req.params.localAreaId, state: req.body.state, updated: true}); }) + /* istanbul ignore next */ .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); @@ -136,8 +143,11 @@ export default ({ config, db, logger }) => { clearCache(); res.status(200).json({localAreaId: req.params.localAreaId, state: null, updated: true}); }) + /* istanbul ignore next */ .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index 03bac28..691004a 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -18,9 +18,13 @@ export default (config, db, logger) => ({ // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - .catch((err) => reject(err)); - + .then((data) => { + resolve(data) + }) + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }), // Get all flood reports for a given city @@ -42,9 +46,14 @@ export default (config, db, logger) => ({ // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - .catch((err) => reject(err)); - + .then((data) => { + resolve(data) + }) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }), // Update the REM state and append to the log @@ -78,8 +87,14 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - .catch((err) => reject(err)); + .then((data) => { + resolve(data) + }) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }), // Remove the REM state record and append to the log @@ -110,8 +125,14 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - .catch((err) => reject(err)); + .then((data) => { + resolve(data) + }) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err); + }) }) }); diff --git a/src/api/routes/infrastructure/index.js b/src/api/routes/infrastructure/index.js index 570cfad..71f0391 100644 --- a/src/api/routes/infrastructure/index.js +++ b/src/api/routes/infrastructure/index.js @@ -27,7 +27,9 @@ export default ({ config, db, logger }) => { (req, res, next) => infrastructure(config, db, logger).all(req.query.city, req.params.type) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index 6b0d30a..29d33f5 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -17,6 +17,7 @@ export default (config, db, logger) => ({ logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) + /* istanbul ignore next */ .catch((err) => reject(err)); }) diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index a35cbf9..4237342 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -26,7 +26,10 @@ export default (config, db, logger) => ({ db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }), // Return specific report by id diff --git a/src/lib/cap.js b/src/lib/cap.js index ba25285..f39b17a 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -167,6 +167,7 @@ module.exports = class Cap { } else if ( feature.geometry.type === "MultiPolygon" ) { featurePolygons = feature.geometry.coordinates; } else { + /* istanbul ignore next */ self.logger.error( "Cap: createInfo(): Geometry type '" + feature.geometry.type + "' not supported" ); /* istanbul ignore next */ return; diff --git a/src/lib/util.js b/src/lib/util.js index cba903d..ac7f687 100755 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -61,7 +61,11 @@ const handleGeoResponse = (data, req, res, next) => { res.status(404).json({ statusCode: 404, found: false, result: null }) : formatGeo(data, req.query.geoformat) .then((formatted) => res.status(200).json({ statusCode: 200, result: formatted })) - .catch((err) => next(err)); + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + next(err) + }); }; // Handle a regular response, send back result or 404 diff --git a/src/test/testCards.js b/src/test/testCards.js index f226231..88bced9 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -94,8 +94,24 @@ export default function (app){ }); }); + // Get signed URL for card image + it('Get card image link', function(done){ + test.httpAgent(app) + .get('/cards/'+cardId+'/images') + .expect(200) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + console.log(res.body); + done(); + } + }); + }); + // Request a card, submit and get resulting report - it('Put card data', function(done){ + it('Get card data', function(done){ test.httpAgent(app) .get('/cards/'+cardId) .expect(200) diff --git a/src/test/testFloods.js b/src/test/testFloods.js index 4f5497e..5ef4f01 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -98,6 +98,39 @@ describe('Flood areas endpoint', function(){ }); // Get floods + it('Get severe floods (GET /floods?minimum_state=3)', function(done){ + this.timeout(15000); // a lot of data is returned + test.httpAgent(app) + .get('/floods?minimum_state=3') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Just get flood states + it('Get flood states without geo (GET /floods/states)', function(done){ + test.httpAgent(app) + .get('/floods/states') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Get geographic floods it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ this.timeout(15000); // a lot of data is returned test.httpAgent(app) From 0a82ff24bb212d518f123238bd1008372fd0a2f7 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 17:17:18 -0400 Subject: [PATCH 046/160] added longer timeout for travis --- src/test/testCards.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/testCards.js b/src/test/testCards.js index 88bced9..9a61467 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -96,6 +96,7 @@ export default function (app){ // Get signed URL for card image it('Get card image link', function(done){ + this.timeout(15000); // nested call test.httpAgent(app) .get('/cards/'+cardId+'/images') .expect(200) From 57c159638f4a40ab14996a0bb64d2e801dc71680 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 8 Jun 2017 17:32:03 -0400 Subject: [PATCH 047/160] commented AWS s3 image bucket link due to auth issue on travis --- src/test/testCards.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/testCards.js b/src/test/testCards.js index 9a61467..84bc452 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -94,6 +94,7 @@ export default function (app){ }); }); + /* commented until we can get this to run via Travis // Get signed URL for card image it('Get card image link', function(done){ this.timeout(15000); // nested call @@ -109,7 +110,7 @@ export default function (app){ done(); } }); - }); + });*/ // Request a card, submit and get resulting report it('Get card data', function(done){ From 3b2b86ce84136348a174b9b5359aa84e46129b25 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 09:37:31 -0400 Subject: [PATCH 048/160] Organised config params to alphabetical order --- src/config.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/config.js b/src/config.js index 936157b..a2f70d9 100644 --- a/src/config.js +++ b/src/config.js @@ -14,6 +14,7 @@ export default { AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'auth0_client_id', AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com', AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret', + AWS_REGION: process.env.AWS_REGION || 'ap-south-1', BODY_LIMIT: process.env.BODY_LIMIT || '100kb', CACHE: process.env.CACHE === 'true' || false, CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute', @@ -24,23 +25,13 @@ export default { CORS: process.env.CORS === 'true' || false, CORS_HEADERS: process.env.CORS_HEADERS || ['Link'], DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','), - IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', - IMAGES_HOST: process.env.IMAGES_HOST || - 'images.riskmap.in', - AWS_REGION: process.env.AWS_REGION || - 'ap-south-1', - PGHOST: process.env.PGHOST || '127.0.0.1', - PGDATABASE: process.env.PGDATABASE || 'cognicity', - PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd', - PGPORT: process.env.PGPORT || 5432, - PGSSL: process.env.PGSSL === 'true' || false, - PGTIMEOUT: process.env.PGTIMEOUT || 10000, - PGUSER: process.env.PGUSER || 'postgres', FORMAT_DEFAULT: process.env.FORMAT_DEFAULT || 'json', FORMATS: (process.env.FORMATS || 'json').split(','), GEO_FORMAT_DEFAULT: process.env.GEO_FORMAT_DEFAULT || 'topojson', GEO_FORMATS: (process.env.GEO_FORMATS || 'geojson,topojson').split(','), GEO_PRECISION: process.env.GEO_PRECISION || 10, + IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', + IMAGES_HOST: process.env.IMAGES_HOST || 'images.riskmap.in', INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,waterways').split(','), LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','), LOG_CONSOLE: process.env.LOG_CONSOLE === 'true' || false, @@ -50,6 +41,13 @@ export default { LOG_MAX_FILE_SIZE: process.env.LOG_MAX_FILE_SIZE || 1024 * 1024 * 100, LOG_MAX_FILES: process.env.LOG_MAX_FILES || 10, NODE_ENV: process.env.NODE_ENV || 'development', + PGHOST: process.env.PGHOST || '127.0.0.1', + PGDATABASE: process.env.PGDATABASE || 'cognicity', + PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd', + PGPORT: process.env.PGPORT || 5432, + PGSSL: process.env.PGSSL === 'true' || false, + PGTIMEOUT: process.env.PGTIMEOUT || 10000, + PGUSER: process.env.PGUSER || 'postgres', PORT: process.env.PORT || 8001, REGION_CODES: (process.env.REGION_CODES || 'jbd,bdg,sby').split(','), REPORT_TYPES: (process.env.REPORT_TYPES || 'drain,desilting,canalrepair,treeclearing,flood').split(','), From 101477233111fde0496eed7646c622f3272809f8 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 09:56:00 -0400 Subject: [PATCH 049/160] Added space for AWS S3 credentials in config, re-enabled card image signed url test --- src/api/routes/cards/index.js | 20 +++++++++++--------- src/config.js | 3 +++ src/test/testCards.js | 3 +-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index af17e4c..ec39760 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -15,15 +15,6 @@ import shortid from 'shortid'; // Import image upload capabilities import AWS from 'aws-sdk'; -var s3 = new AWS.S3( - { - accessKeyId : process.env.accessKeyId || '' , - secretAccessKey : process.env.secretAccessKey || '', - signatureVersion: 'v4', - region: 'ap-south-1' - }); - - // Caching import apicache from 'apicache'; @@ -35,8 +26,19 @@ const clearCache = () => { }; export default ({ config, db, logger }) => { + + // Router let api = Router(); + // Create an S3 object + let s3 = new AWS.S3( + { + accessKeyId : config.AWS_S3_ACCESS_KEY_ID , + secretAccessKey : config.AWS_S3_SECRET_ACCESS_KEY, + signatureVersion: config.AWS_S3_SIGNATURE_VERSION, + region: config.AWS_REGION + }); + // Create a new card and if successful return generated cardId api.post('/', validate({ diff --git a/src/config.js b/src/config.js index a2f70d9..a1426d6 100644 --- a/src/config.js +++ b/src/config.js @@ -15,6 +15,9 @@ export default { AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com', AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret', AWS_REGION: process.env.AWS_REGION || 'ap-south-1', + AWS_S3_ACCESS_KEY_ID: process.env.AWS_S3_ACCESS_KEY_ID || '', + AWS_S3_SECRET_ACCESS_KEY: process.env.AWS_S3_SECRET_ACCESS_KEY || '', + AWS_S3_SIGNATURE_VERSION: process.env.AWS_SIGNATURE_VERSION || 'v4', BODY_LIMIT: process.env.BODY_LIMIT || '100kb', CACHE: process.env.CACHE === 'true' || false, CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute', diff --git a/src/test/testCards.js b/src/test/testCards.js index 84bc452..9a61467 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -94,7 +94,6 @@ export default function (app){ }); }); - /* commented until we can get this to run via Travis // Get signed URL for card image it('Get card image link', function(done){ this.timeout(15000); // nested call @@ -110,7 +109,7 @@ export default function (app){ done(); } }); - });*/ + }); // Request a card, submit and get resulting report it('Get card data', function(done){ From 5a4361b9b075b9bc265207ed22c153e62c843843 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 10:11:36 -0400 Subject: [PATCH 050/160] changed default image bucket to test bucket --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index a1426d6..ad04e31 100644 --- a/src/config.js +++ b/src/config.js @@ -33,7 +33,7 @@ export default { GEO_FORMAT_DEFAULT: process.env.GEO_FORMAT_DEFAULT || 'topojson', GEO_FORMATS: (process.env.GEO_FORMATS || 'geojson,topojson').split(','), GEO_PRECISION: process.env.GEO_PRECISION || 10, - IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'riskmap-image-uploads', + IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'testing-riskmap-image-uploads', IMAGES_HOST: process.env.IMAGES_HOST || 'images.riskmap.in', INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,waterways').split(','), LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','), From 2366ec854d18081914c6b58d79cc63166c037baf Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:14:41 -0400 Subject: [PATCH 051/160] Added tests for CAP module --- src/test/index.js | 5 +-- src/test/testCAP.js | 85 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/test/testCAP.js diff --git a/src/test/index.js b/src/test/index.js index dbdf07c..c6cdad2 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -17,12 +17,11 @@ import { init } from '../server.js'; const winston = require('winston'); const logger = new (winston.Logger)({ transports: [ - new (winston.transports.Console)({ raw: true }), + new (winston.transports.Console)({ raw: true, level: 'debug' }), ] }); - // Import tests import testServer from './testServer.js'; import testCards from './testCards.js'; @@ -32,6 +31,7 @@ import testFloodgauges from './testFloodgauges.js'; import testInfrastructure from './testInfrastructure.js'; import testFloods from './testFloods.js'; import testReports from './testReports.js'; +import testCAP from './testCAP.js'; // Put some sample data in the database const pg = require('pg'); @@ -91,6 +91,7 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ testInfrastructure(app); testFloods(app, token); testReports(app, reportid); + testCAP(logger); // Removes dummy data describe('Cleans up', function() { diff --git a/src/test/testCAP.js b/src/test/testCAP.js new file mode 100644 index 0000000..82eab21 --- /dev/null +++ b/src/test/testCAP.js @@ -0,0 +1,85 @@ +const test = require('unit.js'); + +// Cap formatter helper +import Cap from '../lib/cap'; + + + +export default function (logger){ + + describe('CAP Utility', function(){ + + const cap = new Cap(logger); // Setup our cap formatter + + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + + // Test geometry string creation + it('Can create suitable polygon strings from geojson features', function(done){ + cap.createArea(feature); + let result = cap.createArea(feature); + test.value(typeof(result.polygon[0])).is('string') + done(); + }); + + // dummy data (multipolygon) + feature = {"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + + // Test multigeometry string creation + it('Will operate on multipolygon objects', function(done){ + let result = cap.createArea(feature); + test.value(typeof(result.polygon[0])).is('string') + done(); + }); + + // Test level severe + it('Create the correct severity levels', function(done){ + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + + let result = cap.createInfo(feature); + test.value(result.severity).is("Severe") + done(); + }); + + // Test level moderate + it('Create the correct severity levels', function(done){ + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":3,"last_updated":"2017-03-31T02:45:52.574Z"}} + + let result = cap.createInfo(feature); + test.value(result.severity).is("Moderate") + done(); + }); + + // Test level minor + it('Create the correct severity levels', function(done){ + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":2,"last_updated":"2017-03-31T02:45:52.574Z"}} + + let result = cap.createInfo(feature); + test.value(result.severity).is("Minor") + done(); + }); + + // Test level Unknown + it('Create the correct severity levels', function(done){ + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} + + let result = cap.createInfo(feature); + test.value(result.severity).is("Unknown") + done(); + }); + + // Test bad geometry will bubble up internall within CAP module + it('Test bad geometry', function(done){ + // dummy data (polygon) + let feature = {"type":"Feature","geometry":{"type":"Point","coordinates":[106.8367359996,-6.2305420003]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} + + let result = cap.createInfo(feature); + test.value(result).is(undefined) + done(); + }); + }); +} From 98d3732944b64eaebf013b153b112519077b7f9a Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:22:10 -0400 Subject: [PATCH 052/160] Added test for complex polygon geometries --- src/test/testCAP.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/testCAP.js b/src/test/testCAP.js index 82eab21..7951460 100644 --- a/src/test/testCAP.js +++ b/src/test/testCAP.js @@ -73,9 +73,9 @@ export default function (logger){ }); // Test bad geometry will bubble up internall within CAP module - it('Test bad geometry', function(done){ + it('Test unsupported complex polygon geometry (interior rings)', function(done){ // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Point","coordinates":[106.8367359996,-6.2305420003]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates" : [[ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ], [ [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 2 ] , [ 2 , 2 ] ]]}, "properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} let result = cap.createInfo(feature); test.value(result).is(undefined) From ae42d3ab598bbeb76293aacbc0dd65f651b7a48d Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:28:11 -0400 Subject: [PATCH 053/160] Moved DB tests into index --- src/test/index.js | 2 ++ src/test/testCAP.js | 3 +-- src/test/testDB.js | 47 ++++++++++++++++++++++++--------------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/test/index.js b/src/test/index.js index c6cdad2..b432187 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -32,6 +32,7 @@ import testInfrastructure from './testInfrastructure.js'; import testFloods from './testFloods.js'; import testReports from './testReports.js'; import testCAP from './testCAP.js'; +import testDB from './testDB.js'; // Put some sample data in the database const pg = require('pg'); @@ -92,6 +93,7 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ testFloods(app, token); testReports(app, reportid); testCAP(logger); + testDB(); // Removes dummy data describe('Cleans up', function() { diff --git a/src/test/testCAP.js b/src/test/testCAP.js index 7951460..70d267d 100644 --- a/src/test/testCAP.js +++ b/src/test/testCAP.js @@ -1,10 +1,9 @@ +// Unit.js const test = require('unit.js'); // Cap formatter helper import Cap from '../lib/cap'; - - export default function (logger){ describe('CAP Utility', function(){ diff --git a/src/test/testDB.js b/src/test/testDB.js index 2f43b06..682db55 100644 --- a/src/test/testDB.js +++ b/src/test/testDB.js @@ -1,27 +1,30 @@ // Import Unit.js const test = require('unit.js'); +// Database utility import initializeDb from '../db'; -describe('Test CogniCity Server Database Module', function() { - it('Catches errors on startup', function(done){ - // Try and connect to the db - let config = {}; - let logger = {}; - logger.error =function(err){ - console.log(err); - } - logger.debug =function(err){ - console.log(err); - } - initializeDb(config, logger) - .then((db) => { - test.value(db).is(null); - //done(); do nothing here, an error should be forced by empty config - }) - .catch((err) => { - console.log(err); - done(); - }); - }); -}); +export default function(){ + describe('Test CogniCity Server Database Module', function() { + it('Catches errors on startup', function(done){ + // Try and connect to the db + let config = {}; + let logger = {}; + logger.error =function(err){ + console.log(err); + } + logger.debug =function(err){ + console.log(err); + } + initializeDb(config, logger) + .then((db) => { + test.value(db).is(null); + //done(); do nothing here, an error should be forced by empty config + }) + .catch((err) => { + console.log(err); + done(); + }); + }); + }); +} From c17ba613d085237e42dc912742c1616a0e717667 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:36:03 -0400 Subject: [PATCH 054/160] further test coverage --- src/api/routes/cards/model.js | 12 ++++++++---- src/api/routes/floods/index.js | 2 ++ src/api/routes/infrastructure/model.js | 5 ++++- src/api/routes/reports/model.js | 5 ++++- src/test/testCards.js | 15 +++++++++++++++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index 12cb840..df47a9b 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -85,10 +85,12 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) - /* istanbul ignore next */ .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }), // Update the reports table with new report details @@ -121,10 +123,12 @@ export default (config, db, logger) => ({ db.tx((t) => { return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) - /* istanbul ignore next */ .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }) }); diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index 1b7a9e8..59dc237 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -68,10 +68,12 @@ export default ({ config, db, logger }) => { // If CAP format has been required first convert to geojson then to CAP formatGeo(data, 'geojson') .then((formatted) => res.status(200).set('Content-Type', 'text/xml').send(cap.geoJsonToAtomCap(formatted.features))) + /* istanbul ignore next */ .catch((err) => next(err)) : // Otherwise hand off to geo formatter formatGeo(data, req.query.geoformat) .then((formatted) => res.status(200).json({ statusCode: 200, result: formatted })) + /* istanbul ignore next */ .catch((err) => next(err)) ) .catch((err) => { diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index 29d33f5..cb5dff0 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -18,7 +18,10 @@ export default (config, db, logger) => ({ db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }) }); diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 4237342..0efe68c 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -49,7 +49,10 @@ export default (config, db, logger) => ({ db.oneOrNone(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) /* istanbul ignore next */ - .catch((err) => reject(err)); + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); }) }); diff --git a/src/test/testCards.js b/src/test/testCards.js index 9a61467..8dfe3c7 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -64,6 +64,21 @@ export default function (app){ }); }); + // Check HEAD request on cardId + it('Check HEAD request on cardId', function(done){ + test.httpAgent(app) + .head('/cards/'+cardId) + .expect(200) + .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) From 6da88f3fc5bfa95cc5b73b8e01af4ff231bad036 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:48:20 -0400 Subject: [PATCH 055/160] changed default images endpoint to petabencana.id --- README.md | 19 +++++++++++++------ src/config.js | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9fb9a70..7dedeb5 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Server configuration parameters are stored in a configuration file which is pars * `AUTH0_AUDIENCE`: Data API to be authenticated * `AUTH0_CLIENT_ID`: Auth0 client ID (NOTE: this is mandatory and no default value) * `AUTH0_ISSUER`: Web address of Auth0 instance +* `AWS_REGION`: Region for AWS Infrastructure +* `AWS_S3_ACCESS_KEY_ID`: Access key ID for AWS S3 bucket +* `AWS_S3_SECRET_ACCESS_KEY`: Access key secret for AWS S3 bucket +* `AWS_S3_SIGNATURE_VERSION`: Version of AWS S3 signature to use * `AUTH0_SECRET`: Auth0 secret (NOTE: this is mandatory and no default value) * `BODY_LIMIT`: Maximum body size POST/PUT/PATCH (default: `100kb`) * `CACHE`: Should caching be enabled? (default: `false`) @@ -43,6 +47,14 @@ Server configuration parameters are stored in a configuration file which is pars * `COMPRESS`: Should the server gzip compress results? (default: `false`) * `CORS`: Should Cross Object Resource Sharing (CORS) be enabled (default: `false`) * `CORS_HEADERS`: CORS headers to use (default: `[Link]`) +* `DISASTER_TYPES`: Disaster type keywords for report classification (default: `flood,prep`) +* `FORMAT_DEFAULT`: Which format to return results in by default (default: `json`) +* `FORMATS`: Formats supported by the system (as comma separated list) (default: `json,xml`) +* `GEO_FORMAT_DEFAULT`: Which format to return geographic results in by default (default: `topojson`) +* `GEO_FORMATS`: Geographic formats supported by the system (as comma separated list) (default: `topojson,geojson,cap`) +* `GEO_PRECISION`: Precision to use when rounding geographic coordinates (default: `10`) +* `IMAGE_BUCKET`: AWS S3 bucket for image uploads (default: `testing-riskmap-image-uploads`) +* `IMAGES_HOST`: Endpoint for image hosting (default: `images.petabencana.id`), * `PGHOST`: Postgres DB hostname (default: `127.0.0.1`) * `PGDATABASE`: Postgres DB database name (default: `cognicity`) * `PGPASSWORD`: Postgres DB password (default: `p@ssw0rd`) @@ -50,11 +62,6 @@ Server configuration parameters are stored in a configuration file which is pars * `PGSSL`: SSL enabled on Postgres DB connection? (default: `false`) * `PGTIMEOUT`: Max duration on DB calls before timeout (in milliseconds) (default: `5000` i.e. 5 seconds) * `PGUSER`: Postgres DB username (default: `postgres`) -* `FORMAT_DEFAULT`: Which format to return results in by default (default: `json`) -* `FORMATS`: Formats supported by the system (as comma separated list) (default: `json,xml`) -* `GEO_FORMAT_DEFAULT`: Which format to return geographic results in by default (default: `topojson`) -* `GEO_FORMATS`: Geographic formats supported by the system (as comma separated list) (default: `topojson,geojson,cap`) -* `GEO_PRECISION`: Precision to use when rounding geographic coordinates (default: `10`) * `INFRASTRUCTURE_TYPES`: Infrastructure types supported (as comma separated list) (default: `floodgates,pumps,waterways`) * `LANGUAGES`: Supported languages * `LOG_CONSOLE`: In development mode we log to the console by default, in other environments this must be enabled if required by setting this parameter to `true` (default: `false`) @@ -88,7 +95,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. See src/test/ for scripts. +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. ### Issue Tracking Issues are tracked using [GitHub](https://github.com/urbanriskmap/cognicity-server/issues) diff --git a/src/config.js b/src/config.js index ad04e31..951ebd0 100644 --- a/src/config.js +++ b/src/config.js @@ -34,7 +34,7 @@ export default { GEO_FORMATS: (process.env.GEO_FORMATS || 'geojson,topojson').split(','), GEO_PRECISION: process.env.GEO_PRECISION || 10, IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'testing-riskmap-image-uploads', - IMAGES_HOST: process.env.IMAGES_HOST || 'images.riskmap.in', + IMAGES_HOST: process.env.IMAGES_HOST || 'images.petabencana.in', INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,waterways').split(','), LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','), LOG_CONSOLE: process.env.LOG_CONSOLE === 'true' || false, From a38b06dccb1fda3446722471b36834deb1620e95 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 11:50:45 -0400 Subject: [PATCH 056/160] Updated README for new config variables --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7dedeb5..56ccff6 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,6 @@ Server configuration parameters are stored in a configuration file which is pars * `GEO_PRECISION`: Precision to use when rounding geographic coordinates (default: `10`) * `IMAGE_BUCKET`: AWS S3 bucket for image uploads (default: `testing-riskmap-image-uploads`) * `IMAGES_HOST`: Endpoint for image hosting (default: `images.petabencana.id`), -* `PGHOST`: Postgres DB hostname (default: `127.0.0.1`) -* `PGDATABASE`: Postgres DB database name (default: `cognicity`) -* `PGPASSWORD`: Postgres DB password (default: `p@ssw0rd`) -* `PGPORT`: Postgres DB port (default: `5432`) -* `PGSSL`: SSL enabled on Postgres DB connection? (default: `false`) -* `PGTIMEOUT`: Max duration on DB calls before timeout (in milliseconds) (default: `5000` i.e. 5 seconds) -* `PGUSER`: Postgres DB username (default: `postgres`) * `INFRASTRUCTURE_TYPES`: Infrastructure types supported (as comma separated list) (default: `floodgates,pumps,waterways`) * `LANGUAGES`: Supported languages * `LOG_CONSOLE`: In development mode we log to the console by default, in other environments this must be enabled if required by setting this parameter to `true` (default: `false`) @@ -71,8 +64,16 @@ Server configuration parameters are stored in a configuration file which is pars * `LOG_MAX_FILE_SIZE`: Maximum size of log file in bytes before rotating (default: `1024 * 1024 * 100` i.e. `100mb`) * `LOG_MAX_FILES`: Maximum number of log files before rotation (default: `10`) * `NODE_ENV`: Which environment are we in. Environments are: development, test, staging, production (default: `development`) +* `PGHOST`: Postgres DB hostname (default: `127.0.0.1`) +* `PGDATABASE`: Postgres DB database name (default: `cognicity`) +* `PGPASSWORD`: Postgres DB password (default: `p@ssw0rd`) +* `PGPORT`: Postgres DB port (default: `5432`) +* `PGSSL`: SSL enabled on Postgres DB connection? (default: `false`) +* `PGTIMEOUT`: Max duration on DB calls before timeout (in milliseconds) (default: `5000` i.e. 5 seconds) +* `PGUSER`: Postgres DB username (default: `postgres`) * `PORT`: Which port should the application run on (default: `8001`) * `REGION_CODES`: Which region codes are supported (as comma separated list) (default: `jbd,bdg,sby`) +* `REPORT_TYPES`: Classifiers for report types (default: `drain,desilting,canalrepair,treeclearing,flood`) * `RESPONSE_TIME`: Should the server return an `X-Response-Time` header detailing the time taken to process the request. This is useful for both development to identify latency impact on testing and production for performance / health monitoring (default: `false`) * `SECURE_AUTH0`: Whether Auth0 JWT token security should be applied to secure routes (default: `false`) * `TABLE_FLOODGAUGE_REPORTS`: Postgres table name for flood-gauge reports From 59efeb0df644abd3705f8b764857098dd471dc78 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 13:01:47 -0400 Subject: [PATCH 057/160] Removed dummy data inserts --- src/test/index.js | 90 +++++++++++++++++++++++++++++-------------- src/test/testCards.js | 8 ++-- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/test/index.js b/src/test/index.js index b432187..56c3f25 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -44,37 +44,12 @@ describe('Cognicity Server Testing Harness', function() { let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; // Gloal report value -let reportid = 0; +let reportid = 1; // Auth JWT support const jwt = require('jsonwebtoken'); let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_CLIENT_ID}); -// Insert some dummy data - before ('Insert dummy data', function(done){ - let queryObject = { - text: `INSERT INTO ${config.TABLE_REPORTS} (fkey, created_at, text, source, status, disaster_type, lang, url, image_url, title, the_geom) VALUES (1, now(), 'test report', 'testing', 'confirmed', 'flood', 'en', 'no_url', 'no_url', 'no_title', ST_GeomFromText('POINT(106.816667 -6.2)', 4326)) RETURNING pkey` - }; - - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(err, result){ - reportid = result.rows[0].pkey; - pgDone(); - }); - }); - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(`INSERT INTO ${config.TABLE_REM_STATUS} (local_area, state, last_updated) VALUES (1, 1, now())`, function(err){ - if (err){ - console.log(err); - } - else { - done(); - pgDone(); - } - }); - }); - }); - it('Server starts', function(done){ // Test optional server params config.COMPRESS = true; @@ -99,7 +74,26 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ describe('Cleans up', function() { it ('Removes dummy report data', function(done){ let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE pkey = $1;`, + text: `DELETE FROM ${config.TABLE_REPORTS} WHERE source = 'grasp' AND text = 'integration testing';`, + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + it ('Removes dummy cards data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_GRASP_CARDS} WHERE username = 'testuser' AND network = 'test network';`, values: [ reportid ] }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ @@ -116,10 +110,48 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); }); - // Remove dummy data from reports table + it ('Removes dummy cards data', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_GRASP_REPORTS} WHERE text = 'integration testing';`, + values: [ reportid ] + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from REM floods table it ('Removes dummy flood data', function(done){ let queryObject = { - text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 1`, + text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 5`, + }; + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ + client.query(queryObject, function(){ + if (err) { + console.log(err.message) + pgDone(); + } + else { + pgDone(); + done(); + } + }); + }); + }); + + // Remove dummy data from REM floods table + it ('Removes dummy flood data from log', function(done){ + let queryObject = { + text: `DELETE FROM ${config.TABLE_REM_STATUS_LOG} WHERE username = testing`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ client.query(queryObject, function(){ diff --git a/src/test/testCards.js b/src/test/testCards.js index 8dfe3c7..a782b30 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -47,7 +47,7 @@ export default function (app){ .post('/cards') .send({ "username": "testuser", - "network": "twitter", + "network": "test network", "language": "en" }) .expect(200) @@ -89,7 +89,7 @@ export default function (app){ "flood_depth": 20, "report_type": "flood" }, - "text": "big flood", + "text": "integration testing", "created_at": "2017-06-07T07:00:00+0700", "location": { "lat": -6.4, @@ -139,9 +139,9 @@ export default function (app){ else { test.value(res.body.result.card_id).is(cardId); test.value(res.body.result.username).is('testuser'); - test.value(res.body.result.network).is('twitter') + test.value(res.body.result.network).is('test network') test.value(res.body.result.language).is('en') - test.value(res.body.result.report.text).is('big flood') + test.value(res.body.result.report.text).is('integration testing') done(); } }); From 997ea305bcde73ab02c57d552d9b87c4db116679 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 13:03:30 -0400 Subject: [PATCH 058/160] Added note on integration tests to readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56ccff6..8d3bfba 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Server configuration parameters are stored in a configuration file which is pars * `PGUSER`: Postgres DB username (default: `postgres`) * `PORT`: Which port should the application run on (default: `8001`) * `REGION_CODES`: Which region codes are supported (as comma separated list) (default: `jbd,bdg,sby`) -* `REPORT_TYPES`: Classifiers for report types (default: `drain,desilting,canalrepair,treeclearing,flood`) +* `REPORT_TYPES`: Classifiers for report types (default: `drain,desilting,canalrepair,treeclearing,flood`) * `RESPONSE_TIME`: Should the server return an `X-Response-Time` header detailing the time taken to process the request. This is useful for both development to identify latency impact on testing and production for performance / health monitoring (default: `false`) * `SECURE_AUTH0`: Whether Auth0 JWT token security should be applied to secure routes (default: `false`) * `TABLE_FLOODGAUGE_REPORTS`: Postgres table name for flood-gauge reports @@ -96,7 +96,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. +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. ### Issue Tracking Issues are tracked using [GitHub](https://github.com/urbanriskmap/cognicity-server/issues) From e72c9ec626774431c4e77e4057ecc9cb3a82c572 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 13:18:20 -0400 Subject: [PATCH 059/160] Fix #21 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d3bfba..e8b8301 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Server configuration parameters are stored in a configuration file which is pars * `CACHE_DURATION_FLOODS`: How long should floods be cached for? (default: '1 hour') * `CACHE_DURATION_FLOODS_STATES`: How long should flood states be cached for? (default: '1 hour') * `CACHE_DURATION_INFRASTRUCTURE`: How long should infrastructure be cached for? (default: '1 hour') -* `COMPRESS`: Should the server gzip compress results? (default: `false`) +* `COMPRESS`: Should the server gzip compress results? Only works if CACHE is disabled. (default: `false`) * `CORS`: Should Cross Object Resource Sharing (CORS) be enabled (default: `false`) * `CORS_HEADERS`: CORS headers to use (default: `[Link]`) * `DISASTER_TYPES`: Disaster type keywords for report classification (default: `flood,prep`) From 1ac27e196998d9cd131d1c05421f939f3cc5b695 Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 9 Jun 2017 15:05:50 -0400 Subject: [PATCH 060/160] Added tests for duplicate inputs to feeds --- src/test/testFeeds.js | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/test/testFeeds.js b/src/test/testFeeds.js index 65175ca..c2baaab 100644 --- a/src/test/testFeeds.js +++ b/src/test/testFeeds.js @@ -29,6 +29,36 @@ export default function (app){ } }); }); + + // Catch duplicate post to qlue + it('Catch duplicate entry to feeds/qlue', function(done){ + test.httpAgent(app) + .post('/feeds/qlue') + .send({ + "post_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "qlue_city": "surabaya", + "disaster_type": "flood", + "text": "test report", + "location":{ + "lat":45, + "lng":45 + } + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.created).is(false) + done(); + } + + }); + }); + // Can post to detik it('Return 200 for post to feeds/detik', function(done){ test.httpAgent(app) @@ -54,5 +84,32 @@ export default function (app){ } }); }); + + // Catch duplicate post to detik + it('Return 200 for post to feeds/detik', function(done){ + test.httpAgent(app) + .post('/feeds/detik') + .send({ + "contribution_id": "9999", + "created_at": "2017-06-07T14:32+0700", + "disaster_type": "flood", + "location":{ + "latitude":45, + "longitude":45 + }, + "text":"test report" + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.created).is(false); + done(); + } + }); + }); }); } From 9777d1343f286fb313614916ff17bf695f546fe0 Mon Sep 17 00:00:00 2001 From: abrahamq Date: Mon, 19 Jun 2017 13:32:14 -0400 Subject: [PATCH 061/160] all reports link without timebounds --- .gitignore | 2 +- src/api/routes/reports/archive/index.js | 35 +++++++++++++++++ src/api/routes/reports/archive/model.js | 50 +++++++++++++++++++++++++ src/api/routes/reports/archive/test.js | 48 ++++++++++++++++++++++++ src/api/routes/reports/index.js | 6 +++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/api/routes/reports/archive/index.js create mode 100644 src/api/routes/reports/archive/model.js create mode 100644 src/api/routes/reports/archive/test.js diff --git a/.gitignore b/.gitignore index ae8b33d..8c9849a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .DS_Store .env .idea -.swp +*.swp /coverage /dist diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js new file mode 100644 index 0000000..a1064d4 --- /dev/null +++ b/src/api/routes/reports/archive/index.js @@ -0,0 +1,35 @@ +import { Router } from 'express'; + +// Import our data model +import archive from './model'; + +// Import any required utility functions +import { cacheResponse, handleGeoResponse } from '../../../../lib/util'; + +// Import validation dependencies +import Joi from 'joi'; +import validate from 'celebrate'; + +export default ({ config, db, logger }) => { + let api = Router(); + + // Get a list of all reports + api.get('/', cacheResponse('1 minute'), + validate({ + query: { + city: Joi.any().valid(config.REGION_CODES), + timeperiod: Joi.number().integer().positive().max(config.API_REPORTS_TIME_WINDOW_MAX).default(config.API_REPORTS_TIME_WINDOW), + format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) + } + }), + (req, res, next) => archive(config, db, logger).all(req.query.timeperiod, req.query.city) + .then((data) => handleGeoResponse(data, req, res, next)) + .catch((err) => { + logger.error(err); + next(err); + }) + ); + + return api; +}; diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js new file mode 100644 index 0000000..3414060 --- /dev/null +++ b/src/api/routes/reports/archive/model.js @@ -0,0 +1,50 @@ +import Promise from 'bluebird'; + +export default (config, db, logger) => ({ + + /** + * Return all reports within a defined time period, and optionally city + * @param {integer} timeperiod Length of time period in seconds + * @param {string} city Optional, instance region code (e.g. 'jbd') + */ + all: (timeperiod, city) => new Promise((resolve, reject) => { + + // Setup query + let query = `SELECT pkey, created_at, source, + status, url, image_url, disaster_type, report_data, tags, title, text, the_geom + FROM ${config.TABLE_REPORTS} + WHERE created_at >= to_timestamp($1) + AND ($2 IS NULL OR tags->>'instance_region_code'=$2) + ORDER BY created_at DESC LIMIT $3`; + + var timeWindow = (Date.now() / 1000) - timeperiod; + + let values = [ timeWindow, city, config.API_REPORTS_LIMIT ]; + + // Execute + logger.debug(query, values); + db.any(query, values).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + .catch((err) => reject(err)); + }), + + // Return specific report by id + byId: (id) => new Promise((resolve, reject) => { + + // Setup query + let query = `SELECT pkey, created_at, source, + status, url, image_url, disaster_type, report_data, tags, title, text, the_geom + FROM ${config.TABLE_REPORTS} + WHERE pkey = $1`; + + // Setup values + let values = [ id ]; + + // Execute + logger.debug(query, values); + db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + .catch((err) => reject(err)); + }) + +}); diff --git a/src/api/routes/reports/archive/test.js b/src/api/routes/reports/archive/test.js new file mode 100644 index 0000000..397deaf --- /dev/null +++ b/src/api/routes/reports/archive/test.js @@ -0,0 +1,48 @@ +const request = require('supertest'); +const assert = require('chai').assert; +require('it-each')(); + +import { init } from '../../..'; + +// Setup an array of tests to run +const tests = [ + { + url: '/reports', + exp: { + status: 200 + } + }, + { + url: '/reports?city=jbd', + exp: { + status: 200 + } + }, + { + url: '/reports?city=xxx', + exp: { + status: 400 + } + }, + { + url: '/reports/0', + exp: { + status: 404 + } + } +]; + +// Run the tests +describe('GET /reports', () => { + it.each(tests, 'respond with correct response for test', (test, next) => { + init().then((app) => { + request(app) + .get(test.url) + .end((err, res) => { + if (err) next(err); + assert.equal(res.status, test.exp.status); + return next(); + }); + }); + }); +}); diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index 478ec83..03e65cc 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -3,6 +3,8 @@ import { Router } from 'express'; // Import our data model import reports from './model'; +import archive from './archive'; + // Import any required utility functions import { cacheResponse, handleGeoResponse } from '../../../lib/util'; @@ -31,6 +33,9 @@ export default ({ config, db, logger }) => { }) ); + // to get all reports between two dates + api.use('/archive', archive({config, db, logger})); + // Get a single report api.get('/:id', cacheResponse('1 minute'), validate({ @@ -48,5 +53,6 @@ export default ({ config, db, logger }) => { }) ); + return api; }; From 4bc085527dad9a634be6e30298518c2ffdee601f Mon Sep 17 00:00:00 2001 From: abrahamq Date: Tue, 20 Jun 2017 12:04:28 -0400 Subject: [PATCH 062/160] moment/joi date validation bug --- package.json | 2 ++ src/api/routes/reports/archive/index.js | 12 +++++++++--- src/api/routes/reports/archive/model.js | 13 +++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 7ab3b58..2472a3f 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,9 @@ "express": "^4.15.2", "express-jwt": "^5.3.0", "joi": "^10.4.1", + "joi-date-extensions": "^1.0.2", "jwks-rsa": "^1.1.1", + "moment": "^2.18.1", "moment-timezone": "^0.5.13", "morgan": "^1.8.1", "pg-promise": "^5.6.8", diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index a1064d4..8b8acd2 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -7,7 +7,10 @@ import archive from './model'; import { cacheResponse, handleGeoResponse } from '../../../../lib/util'; // 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'; export default ({ config, db, logger }) => { @@ -18,12 +21,15 @@ export default ({ config, db, logger }) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), - timeperiod: Joi.number().integer().positive().max(config.API_REPORTS_TIME_WINDOW_MAX).default(config.API_REPORTS_TIME_WINDOW), + //start: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DDTHH:mm:ss+Z']), + //end: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DDTHH:mm:ss+Z']), + start: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ']).options({convert: true}), + end: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ']).options({convert: true}), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) } }), - (req, res, next) => archive(config, db, logger).all(req.query.timeperiod, req.query.city) + (req, res, next) => archive(config, db, logger).all(req.query.start, req.query.end, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { logger.error(err); diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js index 3414060..986b933 100644 --- a/src/api/routes/reports/archive/model.js +++ b/src/api/routes/reports/archive/model.js @@ -7,19 +7,20 @@ export default (config, db, logger) => ({ * @param {integer} timeperiod Length of time period in seconds * @param {string} city Optional, instance region code (e.g. 'jbd') */ - all: (timeperiod, city) => new Promise((resolve, reject) => { + all: (start, end, city) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, status, url, image_url, disaster_type, report_data, tags, title, text, the_geom FROM ${config.TABLE_REPORTS} - WHERE created_at >= to_timestamp($1) - AND ($2 IS NULL OR tags->>'instance_region_code'=$2) - ORDER BY created_at DESC LIMIT $3`; + WHERE created_at >= $1::timestamp with time zone + AND created_at <= $2::timestamp with time zone + AND ($3 IS NULL OR tags->>'instance_region_code'=$3) + ORDER BY created_at DESC LIMIT $4`; - var timeWindow = (Date.now() / 1000) - timeperiod; + //var timeWindow = (Date.now() / 1000) - timeperiod; - let values = [ timeWindow, city, config.API_REPORTS_LIMIT ]; + let values = [ start, end, city, config.API_REPORTS_LIMIT ]; // Execute logger.debug(query, values); From 2e9ec182d440d4d2621be7946eef06f7609f76fe Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 14:56:23 -0400 Subject: [PATCH 063/160] Fixed joi validation of timestamps --- src/api/routes/reports/archive/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 8b8acd2..17a7f73 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -18,13 +18,12 @@ export default ({ config, db, logger }) => { // Get a list of all reports api.get('/', cacheResponse('1 minute'), + validate({ query: { city: Joi.any().valid(config.REGION_CODES), - //start: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DDTHH:mm:ss+Z']), - //end: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DDTHH:mm:ss+Z']), - start: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ']).options({convert: true}), - end: Joi.date().format(['YYYY-MM-DDTHH:mm:ssZ']).options({convert: true}), + start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ'), + end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').options({convert: true}), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) } From f953e0dd876786a62662c1c502e7d891f8fbcfd8 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 14:57:34 -0400 Subject: [PATCH 064/160] Added nyc_output to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8c9849a..f11ba4c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /node_modules *.log +.nyc_output/ From e76601456ad8862d30a63fdc1f071bd2d35d0158 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 15:54:23 -0400 Subject: [PATCH 065/160] Made start and end objects required --- src/api/routes/reports/archive/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 17a7f73..8c53563 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -22,8 +22,9 @@ export default ({ config, db, logger }) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), - start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ'), - end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').options({convert: true}), + start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), + end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), + // TODO we should restrict output to geo/topojson only. CAP format doesn't make sense for historic data. format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) } From 0c4a552cc4241b91f978be24098305297a2b238b Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 15:54:44 -0400 Subject: [PATCH 066/160] Added tests for reports archive endpoint --- src/test/index.js | 2 + src/test/testReportsArchive.js | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/test/testReportsArchive.js diff --git a/src/test/index.js b/src/test/index.js index 56c3f25..54a5164 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -31,6 +31,7 @@ import testFloodgauges from './testFloodgauges.js'; import testInfrastructure from './testInfrastructure.js'; import testFloods from './testFloods.js'; import testReports from './testReports.js'; +import testReportsArchive from './testReportsArchive'; import testCAP from './testCAP.js'; import testDB from './testDB.js'; @@ -67,6 +68,7 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ testInfrastructure(app); testFloods(app, token); testReports(app, reportid); + testReportsArchive(app); testCAP(logger); testDB(); diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js new file mode 100644 index 0000000..afe16d6 --- /dev/null +++ b/src/test/testReportsArchive.js @@ -0,0 +1,102 @@ +const test = require('unit.js'); +// TODO - test against an actual time in the database? Entered through cards.js (or add a new call to cards here) +export default function (app){ + // Reports endpoint + describe('Reports Archive Endpoint', function() { + // Can get reports between given timestamps + it('Can get reports between given timestamps', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can catch no start parameter + it('Required start parameter by default', function(done){ + test.httpAgent(app) + .get('/reports/archive?end=2017-02-22T07:00:00%2B0700') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can catch no end parameter + it('Required end parameter by default', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-02-22T07:00:00%2B0700') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can catch no UTC offset in end parameter + it('Required end parameter to have a UTC offset', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-02-21T07:00:00%2B0700&end=2017-02-22T07:00:00') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can catch no UTC offset in start parameter + it('Required start parameter to have a UTC offset', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-02-21T07:00:00') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + + // Can catch no UTC offset in start parameter + it('Catches badly formed time stamp', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-02-21') + .expect(400) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + done(); + } + }); + }); + }); +} From 44f09e107f1b8570188035e6a4f3645a1c9e8f4a Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 16:08:23 -0400 Subject: [PATCH 067/160] added todo re timestamps --- src/api/routes/reports/archive/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 8c53563..afe0072 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -22,6 +22,7 @@ export default ({ config, db, logger }) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), + // TODO - does it matter than end time can be "before" start time? start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), // TODO we should restrict output to geo/topojson only. CAP format doesn't make sense for historic data. From 3eea878695606275f7646128fb0f5ca2bd717483 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 20 Jun 2017 16:10:57 -0400 Subject: [PATCH 068/160] added note about timestamp validation --- src/api/routes/reports/archive/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index afe0072..2a8948b 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -22,7 +22,7 @@ export default ({ config, db, logger }) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), - // TODO - does it matter than end time can be "before" start time? + // TODO - does it matter than end time can be "before" start time? Can reference arguments using Joi.ref('start') start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), // TODO we should restrict output to geo/topojson only. CAP format doesn't make sense for historic data. From c826afb1cd1f065e07fcc37d4498b86188fcbbeb Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 26 Jun 2017 13:31:15 -0400 Subject: [PATCH 069/160] Rolled travis back to 9.5 as postgis broken in 9.6 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index cf4dfb8..0940800 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ -dist: trusty +dist: precise language: node_js node_js: - "7" -sudo: false +sudo: true addons: - postgresql: "9.6" + postgresql: "9.5" services: postgresql branches: From 6310ffa0ff7d7dfcb5d3f60ff5e28423561ab68e Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 27 Jun 2017 09:30:23 -0400 Subject: [PATCH 070/160] testing travis with custon postgis build --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index cf4dfb8..0cc9c42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ sudo: false addons: postgresql: "9.6" + apt: + packages: + - postgresql-9.6-postgis-2.3 services: postgresql branches: From 4e4462119dae2bebea539296b851f51077976262 Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 27 Jun 2017 09:34:10 -0400 Subject: [PATCH 071/160] mend testing travis with custom postgis build --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0cc9c42..a3899ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ addons: postgresql: "9.6" apt: packages: - - postgresql-9.6-postgis-2.3 + - postgresql-9.6-postgis- services: postgresql branches: @@ -16,6 +16,7 @@ branches: - master - dev - server-object-refactor + - report-archive before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi From f21bccc83a5e8dfcb75aebad9839c17aaccba92a Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 27 Jun 2017 09:55:24 -0400 Subject: [PATCH 072/160] fixed postgis version --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a3899ac..ad210d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,7 @@ addons: postgresql: "9.6" apt: packages: - - postgresql-9.6-postgis- -services: postgresql + - postgresql-9.6-postgis-2.3 branches: only: From 57f6cf096d86f4c05e5a81b2b10e13672c6ef748 Mon Sep 17 00:00:00 2001 From: abrahamq Date: Tue, 27 Jun 2017 16:43:26 -0400 Subject: [PATCH 073/160] updating dbgeo to fix #36 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6777be..3c029e3 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "celebrate": "^4.0.1", "compression": "^1.6.2", "cors": "^2.8.3", - "dbgeo": "^1.0.1", + "dbgeo": "^1.1.0", "dotenv": "^4.0.0", "express": "^4.15.2", "express-jwt": "^5.3.0", From 953be3f5723dea46ccbd6eee47b2a17ebcf60f89 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 11:47:46 -0400 Subject: [PATCH 074/160] fixed typo --- src/test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/index.js b/src/test/index.js index 54a5164..c7f829f 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -44,7 +44,7 @@ describe('Cognicity Server Testing Harness', function() { // PG config string for dummy data inserts let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; -// Gloal report value +// Global report value let reportid = 1; // Auth JWT support From d5115e7105b05de026af45524a8ee0548d9fab3c Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 11:57:30 -0400 Subject: [PATCH 075/160] fixed travis container spec and node version --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8230977..c319e8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ -dist: precise +dist: trusty language: node_js node_js: - - "7" -sudo: true + - "6.10.0" +sudo: false addons: postgresql: "9.6" From 4b08ff4956b8b6fab41628effab01421dfbde078 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 12:21:22 -0400 Subject: [PATCH 076/160] Added tests for data types --- src/test/testReportsArchive.js | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index afe16d6..e4a69ac 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -19,6 +19,55 @@ export default function (app){ }); }); + it('Can get reports between given timestamps as geojson', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.result.type).is("FeatureCollection"); + done(); + } + }); + }); + + it('Can get any reports by ID', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + test.value(res.body.result.type).is("FeatureCollection"); + done(); + } + }); + }); + + it('Can get reports between given timestamps as topojson', function(done){ + test.httpAgent(app) + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=topojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res){ + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } + else { + + test.value(res.body.result.type).is("Topology"); + done(); + } + }); + }); + // Can catch no start parameter it('Required start parameter by default', function(done){ test.httpAgent(app) From e9b9dce8913311f6c402795a6e910827f59600a4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 12:21:59 -0400 Subject: [PATCH 077/160] removed unused byId function --- src/api/routes/reports/archive/model.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js index 986b933..ae3e9e6 100644 --- a/src/api/routes/reports/archive/model.js +++ b/src/api/routes/reports/archive/model.js @@ -27,25 +27,5 @@ export default (config, db, logger) => ({ db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) .catch((err) => reject(err)); - }), - - // Return specific report by id - byId: (id) => new Promise((resolve, reject) => { - - // Setup query - let query = `SELECT pkey, created_at, source, - status, url, image_url, disaster_type, report_data, tags, title, text, the_geom - FROM ${config.TABLE_REPORTS} - WHERE pkey = $1`; - - // Setup values - let values = [ id ]; - - // Execute - logger.debug(query, values); - db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - .catch((err) => reject(err)); }) - }); From 4abb1f26b011b63e8b19d53db0962368b0271950 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 12:22:18 -0400 Subject: [PATCH 078/160] added istanbull comments for error handling --- src/api/routes/reports/archive/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 2a8948b..2bf706a 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -33,7 +33,9 @@ export default ({ config, db, logger }) => { (req, res, next) => archive(config, db, logger).all(req.query.start, req.query.end, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { + /* istanbul ignore next */ logger.error(err); + /* istanbul ignore next */ next(err); }) ); From bac81af9f5699c337d3e70d48068b66e0602a0e6 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 13:19:28 -0400 Subject: [PATCH 079/160] Changed to new card data format --- src/api/routes/cards/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index c891e7a..ec39760 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -102,11 +102,6 @@ export default ({ config, db, logger }) => { api.put('/:cardId', validate({ params: { cardId: Joi.string().min(7).max(14).required() }, body: Joi.object().keys({ - - water_depth: Joi.number().integer().min(0).max(200).required(), - /*** TODO - re-enable this new card data structure when PetaBencana.id client-code is ready. - ... and remove hard-coded water_depth parameter above - disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(), card_data: Joi.object() .keys({ @@ -118,7 +113,6 @@ export default ({ config, db, logger }) => { is: 'flood', then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true }), - ***/ text: Joi.string().allow(''), image_url: Joi.string().allow(''), created_at: Joi.date().iso().required(), From 023346b420d0371e6f45b33169916643846736c4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 13:32:46 -0400 Subject: [PATCH 080/160] Removed water depth and text objects from card patch endpoint, leaving image_url only --- src/api/routes/cards/index.js | 4 +--- src/api/routes/cards/model.js | 8 ++------ src/test/testCards.js | 2 -- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index ec39760..e98ae1d 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -215,9 +215,7 @@ export default ({ config, db, logger }) => { api.patch('/:cardId', validate({ params: { cardId: Joi.string().min(7).max(14).required() }, body: Joi.object().keys({ - water_depth: Joi.number().integer().min(0).max(200), - text: Joi.string().allow(''), - image_url: Joi.string() + image_url: Joi.string().required() }) }), (req, res, next) => { diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index e79694c..54caf61 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -109,13 +109,9 @@ export default (config, db, logger) => ({ let queries = [ { query: `UPDATE ${config.TABLE_GRASP_REPORTS} SET - card_data = COALESCE($2, card_data), - text = COALESCE($3, text), - image_url = COALESCE($4, image_url) + image_url = COALESCE($2, image_url) WHERE card_id = $1`, - values: [ card.card_id, - body.water_depth ? { flood_depth: body.water_depth } : null, - body.text, body.image_url ] + values: [ card.card_id, body.image_url ] }, { query: `INSERT INTO ${config.TABLE_GRASP_LOG} diff --git a/src/test/testCards.js b/src/test/testCards.js index a782b30..130c7e8 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -152,8 +152,6 @@ export default function (app){ test.httpAgent(app) .patch('/cards/'+cardId) .send({ - "water_depth": 20, - "text": "big flood", "image_url": "dummy image url" }) .expect(200) From b8eeebe8ec4069e5ea2cc6b4ca9b6bb1ea2eff71 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 13:44:23 -0400 Subject: [PATCH 081/160] Added notes on travis ci schema integration testing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8b8301..5981be5 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,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. +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. ### Issue Tracking Issues are tracked using [GitHub](https://github.com/urbanriskmap/cognicity-server/issues) From 90c2e5de4448bad8245cbb0bb57dc7702821eb4c Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 14:26:12 -0400 Subject: [PATCH 082/160] removed chennai from sample.env --- sample.env | 3 --- 1 file changed, 3 deletions(-) diff --git a/sample.env b/sample.env index 6589d8c..2e22d0d 100644 --- a/sample.env +++ b/sample.env @@ -7,6 +7,3 @@ PGPASSWORD=PGPASSWORD_goes_here PGDATABASE=cognicity LOG_DIR=/tmp/cognicity LOG_LEVEL=debug - -#chennai changes: -REGION_CODES=chn From 758d23f7e8d9ab177bc3cbac4eb16e9536a3445b Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 14:26:59 -0400 Subject: [PATCH 083/160] removed vim swap file --- src/.index.js.swp | Bin 20480 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/.index.js.swp diff --git a/src/.index.js.swp b/src/.index.js.swp deleted file mode 100644 index e63985206679acd3894e58c287e873c3f0991509..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI4dyHIF8NhEXudz~j_=kzFQzh&y?#{N90x2oD-JQ~UOIE{ z^xiw&F3Xl6@`z82iN+X>q(-A5K~Yqqe~A8JP=h8Y0fdMO_=iyvO~eNVf8V+1&de^Y z*cf9lcavXt?>*-`=R4nd-Sd4Xt!}=0VoF_7+UoFgmgC&N@#bsX_nx!v*ha^3tC73l zwhl?szxo+X^ToqcZp{ypFkUD%!xEl3i{LxnzDb>X&bv>p zj68R4f!qSQ1#%1I7RW7-TOhYUZh_nauWAb0gPqELT{k>sL z`g;3(w*7r%O}c%|f8`d)Es$Fvw?J-z+yc1;atq`Z$SsgtAh$qnf!qTB0}HsG;|vn( zzYQe*pPm1oe!b)T621#}!-pV->)=|r2;K>f3%V7hY3g3U7<6IB>;3b?6pM^W&cDM~%up7>RM_%hVpMdLO z2nL}D4;Ro4YT&{=48sd&IL@EpY4{<09*)5ka6S~^H>YC<_ys%x55gUAJB-0;a4P)Z zG{?Caj>0~88@v@>Jk@c&2M@wAxD8@>4_p9)a1Q(m2g}oNAKVR}gHOX9Z~)#6Z-U?B zXn6`AgP+6C;3!-R7r=`+a{dN?g$LlP@G0y34|J1)*iRA68r5%Me9mmedh#GT~+JZXhH=D{0JjK-$9YvjX zqGG=hxXswSrFqI#wWf9hCAz8DDCRgTP9fIGtl!dMCn=_lu!WZ-V=>J*IHb01Hm&G4 z)O?v7Vv|LhYKv6oEvF(Y^EA2^2619)>{&zYP+6&|$!NqxT`lTVGr!f+p6@2QxkR~$ zV|*{vu?oUOHFP5FSa5@yR!OK9BR}Df1+AJc6-hH&*u`yVnkF4#{wYNI23<>v7{+u& zff^5JRdWL=6CG(pN1Ik)1u7WdIiyk<#(%ft5D%l`z%Dm&o60=AQtz!59p9BV!&^=O)c%59=~>O za$;&?*0z*_jeru-OutwrQmCW|3Qv_O?JrkmruSCLbF&juWp)1fDytNmw;AbL=26j@ zPYR8~!@j4bLrkJdNL35;6-M@h?)zT(W7GR9{l(V8Ncu=0X@{})b{omK+Yre;x4S$# zUPdbIw7yzRS#hV`4x>aBWt>O)P{)tx8^5lqdVy!vm)uv@J1G(GcG5fCDG^|+f)mvq5g#p8amo>MB1`Qd#o*%o_ruO+N7_}Pl0J7^UeQUXEyfbH>|mzM6h{ zp|ODs*-XQjM6=>M!_+Y*-V(U6g~w^dt}PvrkFn2F#8qiA!TB6E-#4GCb_`FYTQk zFVB_tUadCm*r5hIeONccw%nN~~&dKR3=f;?r)01WCCls78`-zK9W#nV`KGAFm zk=o2Kk#V@-rW;IEYnhpcIyyGX7)un7qB6ac?kP>tqB{$v?#c4i)SSz|Sh_=5SV+^s=?f3m0C zTHVu8KA^71_NvSd+tol;$IBWrpv;$6ws>bEX2O_RQjbfn*`q5Q={;B> z!6<3(Ea=)n8s?@SR5B*|>?W&Om5(L9yb{cigihA&W@6B1eVu4##%0pwuk^Em2FJ@2 zYgV~r)HL9D>GJMGvM0*F>ivm?|C5UQW%uYM_AL8Ny{hKE2T{V2=bknvETg1@!=<&U z>ZY5RE2apg((b0>39nbR5$jJe5^BiEEIWtYNX1*}J=p<94^&E(twqOgna>9vRh>;i z4Y-T(aO@o%xJ*f(PNd0xi|=jKX*WaHW8Amhs^8>Gj!$+@z-bm)O>7bJCzIMH$UbHh z$0um z6eCiC1C>x3Av8SJMO|u?#A!yIUHpy(itGxFlq3~R=9{s^cz zdbO0}<2egNEgXo(L2%x(L_$&}OL+gp17930Nm}Zh>Pt?gGe6YK^!{0^@+c9^Mi`pd zkp!|*$8xVS=@#pmjUDMRY7kFc(YU_5p~Ngm)bmScPG}-&7-wy-NmNtL|Bo>ezalYV z&i|numA~eU{~b66H^F|`2k(MSpx|_Pj`RMr@Kg9Yd<{Mc4aocfXYu#*_yYa_--i1^ zd=4979o$Q7e=l4L7sEDq3;czc{dnpjqTU%_04$nPr<*D}vz5=(&jBCKtb+^+rHzCZO z5_8F+8&OUV%Qm2FFNP5u=K$V+a+X{im$R66x?5PG^dXhOBhkIPCiyAGhS+Th9KHEp z<|0DtT@BKUnYOUf2$!x_#lz)#N_KbChTMDCw8pDlIj_1(j1RwFwy0ZKy6<;|(#KVT z>8q{)&FNl@v|`z-VYv>8tktUTKB3Ln_ohEvlwAyv+mXG5-n~yNE-!}L<|gPW4ebp9 O)$%>B$<0UfasCPS@Hw&o From 076bcfe84473d58773b71f2c799d1790c5d90ec9 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 14:29:47 -0400 Subject: [PATCH 084/160] changed tabs to spaces --- src/api/routes/cards/index.js | 296 +++++++++++++++++----------------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index e98ae1d..9c53953 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -22,13 +22,13 @@ const CACHE_GROUP_CARDS = '/cards'; // Function to clear out the cache const clearCache = () => { - apicache.clear(CACHE_GROUP_CARDS); + apicache.clear(CACHE_GROUP_CARDS); }; export default ({ config, db, logger }) => { // Router - let api = Router(); + let api = Router(); // Create an S3 object let s3 = new AWS.S3( @@ -39,69 +39,69 @@ export default ({ config, db, logger }) => { region: config.AWS_REGION }); - // Create a new card and if successful return generated cardId - api.post('/', - validate({ - body: Joi.object().keys({ - username: Joi.string().required(), - network: Joi.string().required(), - language: Joi.string().valid(config.LANGUAGES).required() - }) - }), - (req, res, next) => { - let cardId = shortid.generate(); - cards(config, db, logger).create(cardId, req.body) - .then((data) => data ? res.status(200).json({ cardId: cardId, created: true }) : - next(new Error('Failed to create card'))) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }); - } - ); + // Create a new card and if successful return generated cardId + api.post('/', + validate({ + body: Joi.object().keys({ + username: Joi.string().required(), + network: Joi.string().required(), + language: Joi.string().valid(config.LANGUAGES).required() + }) + }), + (req, res, next) => { + let cardId = shortid.generate(); + cards(config, db, logger).create(cardId, req.body) + .then((data) => data ? res.status(200).json({ cardId: cardId, created: true }) : + next(new Error('Failed to create card'))) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }); + } + ); - // Check for the existence of a card - api.head('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), - validate({ - params: { cardId: Joi.string().required() } - }), - (req, res, next) => { - req.apicacheGroup = CACHE_GROUP_CARDS; - cards(config, db, logger).byCardId(req.params.cardId) - .then((data) => data ? res.status(200).end() : res.status(404).end()) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }); - } - ); + // Check for the existence of a card + api.head('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), + validate({ + params: { cardId: Joi.string().required() } + }), + (req, res, next) => { + req.apicacheGroup = CACHE_GROUP_CARDS; + cards(config, db, logger).byCardId(req.params.cardId) + .then((data) => data ? res.status(200).end() : res.status(404).end()) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }); + } + ); - // Return a card - api.get('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), - validate({ - params: { cardId: Joi.string().min(7).max(14).required() } - }), - (req, res, next) => { - req.apicacheGroup = CACHE_GROUP_CARDS; - cards(config, db, logger).byCardId(req.params.cardId) - .then((data) => handleResponse(data, req, res, next)) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }); - } - ); + // Return a card + api.get('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), + validate({ + params: { cardId: Joi.string().min(7).max(14).required() } + }), + (req, res, next) => { + req.apicacheGroup = CACHE_GROUP_CARDS; + cards(config, db, logger).byCardId(req.params.cardId) + .then((data) => handleResponse(data, req, res, next)) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }); + } + ); - // Update a card record with a report - api.put('/:cardId', validate({ - params: { cardId: Joi.string().min(7).max(14).required() }, - body: Joi.object().keys({ + // Update a card record with a report + api.put('/:cardId', validate({ + params: { cardId: Joi.string().min(7).max(14).required() }, + body: Joi.object().keys({ disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(), card_data: Joi.object() .keys({ @@ -111,53 +111,53 @@ export default ({ config, db, logger }) => { .required() .when('disaster_type', { is: 'flood', - then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true + then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true }), - text: Joi.string().allow(''), - image_url: Joi.string().allow(''), - created_at: Joi.date().iso().required(), - location: Joi.object().required().keys({ - lat: Joi.number().min(-90).max(90).required(), - lng: Joi.number().min(-180).max(180).required() - }) - }) - }), - (req, res, next) => { - try { - // First get the card we wish to update - cards(config, db, logger).byCardId(req.params.cardId) - .then((card) => { - // If the card does not exist then return an error message - if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }); - // If the card already has received status then return an error message - else if (card && card.received) res.status(409).json({ statusCode: 409, - cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'` }); - // We have a card and it has not yet had a report received - else { - // Try and submit the report and update the card - cards(config, db, logger).submitReport(card, req.body) - .then((data) => { - console.log(data); - clearCache(); - res.status(200).json({ statusCode: 200, cardId: req.params.cardId, created: true }); - }) - .catch((err) => { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }); - } - }); - } catch(err) { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - } - } - ); + text: Joi.string().allow(''), + image_url: Joi.string().allow(''), + created_at: Joi.date().iso().required(), + location: Joi.object().required().keys({ + lat: Joi.number().min(-90).max(90).required(), + lng: Joi.number().min(-180).max(180).required() + }) + }) + }), + (req, res, next) => { + try { + // First get the card we wish to update + cards(config, db, logger).byCardId(req.params.cardId) + .then((card) => { + // If the card does not exist then return an error message + if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'` }); + // If the card already has received status then return an error message + else if (card && card.received) res.status(409).json({ statusCode: 409, + cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'` }); + // We have a card and it has not yet had a report received + else { + // Try and submit the report and update the card + cards(config, db, logger).submitReport(card, req.body) + .then((data) => { + console.log(data); + clearCache(); + res.status(200).json({ statusCode: 200, cardId: req.params.cardId, created: true }); + }) + .catch((err) => { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + }); + } + }); + } catch(err) { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + } + } + ); //Gives an s3 signed url for the frontend to upload an image to api.get('/:cardId/images', validate({ @@ -211,46 +211,46 @@ export default ({ config, db, logger }) => { }); }); - // Update a card report with new details including the image URL - api.patch('/:cardId', validate({ - params: { cardId: Joi.string().min(7).max(14).required() }, - body: Joi.object().keys({ - image_url: Joi.string().required() - }) - }), - (req, res, next) => { - try { - // First get the card we wish to update - cards(config, db, logger).byCardId(req.params.cardId) - .then((card) => { - // If the card does not exist then return an error message - if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }); - // We have a card - else { - // Try and submit the report and update the card - cards(config, db, logger).updateReport(card, req.body) - .then((data) => { - console.log(data); - clearCache(); - res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); - }) - .catch((err) => { + // Update a card report with new details including the image URL + api.patch('/:cardId', validate({ + params: { cardId: Joi.string().min(7).max(14).required() }, + body: Joi.object().keys({ + image_url: Joi.string().required() + }) + }), + (req, res, next) => { + try { + // First get the card we wish to update + cards(config, db, logger).byCardId(req.params.cardId) + .then((card) => { + // If the card does not exist then return an error message + if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'` }); + // We have a card + else { + // Try and submit the report and update the card + cards(config, db, logger).updateReport(card, req.body) + .then((data) => { + console.log(data); + clearCache(); + res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); + }) + .catch((err) => { /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - }); - } - }); - } catch(err) { - /* istanbul ignore next */ - logger.error(err); - /* istanbul ignore next */ - next(err); - } - } - ); + logger.error(err); + /* istanbul ignore next */ + next(err); + }); + } + }); + } catch(err) { + /* istanbul ignore next */ + logger.error(err); + /* istanbul ignore next */ + next(err); + } + } + ); - return api; + return api; }; From b98620b7b8c38d598b8cdf93fe9759854071a5a1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 14:36:59 -0400 Subject: [PATCH 085/160] changed tabs to spaces --- src/api/routes/cards/model.js | 266 +++++++++++++++++----------------- 1 file changed, 133 insertions(+), 133 deletions(-) diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index 54caf61..e376f20 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -2,138 +2,138 @@ import Promise from 'bluebird'; export default (config, db, logger) => ({ - // Create a new card entry with the given cardId - create: (cardId, body) => new Promise((resolve, reject) => { - - // Setup query - let query = `INSERT INTO ${config.TABLE_GRASP_CARDS} - (card_id, username, network, language, received) - VALUES ($1, $2, $3, $4, $5) RETURNING pkey`; - - // Setup values - let values = [ cardId, body.username, body.network, body.language, false ]; - - // Execute - logger.debug(query, values); - db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - /* istanbul ignore next */ - .catch((err) => { - /* istanbul ignore next */ - reject(err)} - ); - }), - - // Return specific card by id - byCardId: (cardId) => new Promise((resolve, reject) => { - - // Setup query - let query = `SELECT c.card_id, c.username, c.network, c.language, c.received, - CASE WHEN r.card_id IS NOT NULL THEN - json_build_object('created_at', r.created_at, 'disaster_type', r.disaster_type, - 'text', r.text, 'card_data', r.card_data, 'image_url', r.image_url, - 'status', r.status) - ELSE null END AS report - FROM ${config.TABLE_GRASP_CARDS} c - LEFT JOIN ${config.TABLE_GRASP_REPORTS} r USING (card_id) - WHERE c.card_id = $1 - LIMIT 1`; - - // Setup values - let values = [ cardId ]; - - // Execute - logger.debug(query, values); - db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - /* istanbul ignore next */ - .catch((err) => { - /* istanbul ignore next */ - reject(err) - }) - }), - - // Add an entry to the reports table and then update the card record accordingly - submitReport: (card, body) => new Promise((resolve, reject) => { - - // Setup our queries - let queries = [ - { - query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} - (card_id, card_data, text, created_at, disaster_type, status, the_geom) - VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, - values: [ card.card_id, { flood_depth: body.water_depth }, body.text, - body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat ] - }, - /*** TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready - { - query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} - (card_id, card_data, text, created_at, disaster_type, status, the_geom) - VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, - values: [ card.card_id, body.card_data, body.text, - body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] - }, - ***/ - { - query: `UPDATE ${config.TABLE_GRASP_CARDS} - SET received = TRUE WHERE card_id = $1`, - values: [ card.card_id ] - }, - { - query: `INSERT INTO ${config.TABLE_GRASP_LOG} - (card_id, event_type) - VALUES ($1, $2)`, - values: [ card.card_id, 'REPORT SUBMITTED' ] - } - ]; - - // Log queries to debugger - for (let query of queries) logger.debug(query.query, query.values); - - // Execute in a transaction as both INSERT and UPDATE must happen together - db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))); - }).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - /* istanbul ignore next */ - .catch((err) => { - /* istanbul ignore next */ - reject(err) - }); - }), - - // Update the reports table with new report details - updateReport: (card, body) => new Promise((resolve, reject) => { - - // Setup our queries - let queries = [ - { - query: `UPDATE ${config.TABLE_GRASP_REPORTS} SET - image_url = COALESCE($2, image_url) - WHERE card_id = $1`, - values: [ card.card_id, body.image_url ] - }, - { - query: `INSERT INTO ${config.TABLE_GRASP_LOG} - (card_id, event_type) - VALUES ($1, $2)`, - values: [ card.card_id, 'REPORT UPDATES' ] - } - ]; - - // Log queries to debugger - for (let query of queries) logger.debug(query.query, query.values); - - // Execute in a transaction as both INSERT and UPDATE must happen together - db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))); - }).timeout(config.PGTIMEOUT) - .then((data) => resolve(data)) - /* istanbul ignore next */ - .catch((err) => { - /* istanbul ignore next */ - reject(err) - }); - }) + // Create a new card entry with the given cardId + create: (cardId, body) => new Promise((resolve, reject) => { + + // Setup query + let query = `INSERT INTO ${config.TABLE_GRASP_CARDS} + (card_id, username, network, language, received) + VALUES ($1, $2, $3, $4, $5) RETURNING pkey`; + + // Setup values + let values = [ cardId, body.username, body.network, body.language, false ]; + + // Execute + logger.debug(query, values); + db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err)} + ); + }), + + // Return specific card by id + byCardId: (cardId) => new Promise((resolve, reject) => { + + // Setup query + let query = `SELECT c.card_id, c.username, c.network, c.language, c.received, + CASE WHEN r.card_id IS NOT NULL THEN + json_build_object('created_at', r.created_at, 'disaster_type', r.disaster_type, + 'text', r.text, 'card_data', r.card_data, 'image_url', r.image_url, + 'status', r.status) + ELSE null END AS report + FROM ${config.TABLE_GRASP_CARDS} c + LEFT JOIN ${config.TABLE_GRASP_REPORTS} r USING (card_id) + WHERE c.card_id = $1 + LIMIT 1`; + + // Setup values + let values = [ cardId ]; + + // Execute + logger.debug(query, values); + db.oneOrNone(query, values).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }) + }), + + // Add an entry to the reports table and then update the card record accordingly + submitReport: (card, body) => new Promise((resolve, reject) => { + + // Setup our queries + let queries = [ + { + query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} + (card_id, card_data, text, created_at, disaster_type, status, the_geom) + VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, + values: [ card.card_id, { flood_depth: body.water_depth }, body.text, + body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat ] + }, + /*** TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready + { + query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} + (card_id, card_data, text, created_at, disaster_type, status, the_geom) + VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, + values: [ card.card_id, body.card_data, body.text, + body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] + }, + ***/ + { + query: `UPDATE ${config.TABLE_GRASP_CARDS} + SET received = TRUE WHERE card_id = $1`, + values: [ card.card_id ] + }, + { + query: `INSERT INTO ${config.TABLE_GRASP_LOG} + (card_id, event_type) + VALUES ($1, $2)`, + values: [ card.card_id, 'REPORT SUBMITTED' ] + } + ]; + + // Log queries to debugger + for (let query of queries) logger.debug(query.query, query.values); + + // Execute in a transaction as both INSERT and UPDATE must happen together + db.tx((t) => { + return t.batch(queries.map((query) => t.none(query.query, query.values))); + }).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); + }), + + // Update the reports table with new report details + updateReport: (card, body) => new Promise((resolve, reject) => { + + // Setup our queries + let queries = [ + { + query: `UPDATE ${config.TABLE_GRASP_REPORTS} SET + image_url = COALESCE($2, image_url) + WHERE card_id = $1`, + values: [ card.card_id, body.image_url ] + }, + { + query: `INSERT INTO ${config.TABLE_GRASP_LOG} + (card_id, event_type) + VALUES ($1, $2)`, + values: [ card.card_id, 'REPORT UPDATES' ] + } + ]; + + // Log queries to debugger + for (let query of queries) logger.debug(query.query, query.values); + + // Execute in a transaction as both INSERT and UPDATE must happen together + db.tx((t) => { + return t.batch(queries.map((query) => t.none(query.query, query.values))); + }).timeout(config.PGTIMEOUT) + .then((data) => resolve(data)) + /* istanbul ignore next */ + .catch((err) => { + /* istanbul ignore next */ + reject(err) + }); + }) }); From eb82df6acee1c1c4711faa17478bd411e69e50b1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 17:13:52 -0400 Subject: [PATCH 086/160] fixed #39 - added eslint with google style --- .eslintrc.js | 5 +++++ package.json | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..9b76622 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + "extends": ["eslint:recommended", "google"], + "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, + "env": { "es6": true } +}; diff --git a/package.json b/package.json index 3c029e3..74441d3 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "prestart": "npm run -s build", "mocha": "nyc mocha --compilers js:babel-core/register src/test/", "coverage": "nyc report --reporter=text-lcov | coveralls", - "test": "eslint src & npm run mocha" + "lint": "eslint src", + "test": "npm run lint & npm run mocha" }, "nyc": { "exclude": [ @@ -56,6 +57,7 @@ "moment": "^2.18.1", "moment-timezone": "^0.5.13", "morgan": "^1.8.1", + "node-pre-gyp": "^0.6.36", "pg-promise": "^5.6.8", "response-time": "^2.3.2", "shortid": "^2.2.8", @@ -70,6 +72,9 @@ "babel-register": "^6.24.1", "coveralls": "^2.13.1", "eslint": "^3.19.0", + "eslint-config-google": "^0.8.0", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", "jsonwebtoken": "^7.4.1", "mocha": "^3.3.0", "nodemon": "^1.11.0", From ade671607caa6434a5ab791700227a7660048b1f Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 17:30:37 -0400 Subject: [PATCH 087/160] Added DOI for current release --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 5981be5..9521d43 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,13 @@ API Server for CogniCity [![Coverage Status](https://coveralls.io/repos/github/urbanriskmap/cognicity-server/badge.svg?branch=server-object-refactor)](https://coveralls.io/github/urbanriskmap/cognicity-server?branch=server-object-refactor) +DOI for current stable release [v3.0.0](https://github.com/urbanriskmap/cognicity-server/releases/tag/v3.0.0) + + +[![DOI](https://zenodo.org/badge/73803254.svg)](https://zenodo.org/badge/latestdoi/73803254) + + + ### Summary This is the NodeJS server which runs the CogniCity Data API used by Urban Risk Map instances, such as [PetaBencana.id](https://petabencana.id) site. From 7b841dd4c5c093a961c122602c0e51e3f40ccf1f Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:13:28 -0400 Subject: [PATCH 088/160] Ran eslint --fix --- src/api/index.js | 26 +++--- src/api/routes/cards/index.js | 104 +++++++++++---------- src/api/routes/cards/model.js | 39 ++++---- src/api/routes/cities/index.js | 10 +- src/api/routes/cities/model.js | 3 +- src/api/routes/feeds/index.js | 16 ++-- src/api/routes/feeds/model.js | 36 +++---- src/api/routes/floodgauges/index.js | 16 ++-- src/api/routes/floodgauges/model.js | 10 +- src/api/routes/floods/index.js | 48 +++++----- src/api/routes/floods/model.js | 38 ++++---- src/api/routes/infrastructure/index.js | 12 +-- src/api/routes/infrastructure/model.js | 7 +- src/api/routes/reports/archive/index.js | 10 +- src/api/routes/reports/archive/model.js | 7 +- src/api/routes/reports/archive/test.js | 20 ++-- src/api/routes/reports/index.js | 16 ++-- src/api/routes/reports/model.js | 14 ++- src/config.js | 2 +- src/db.js | 5 +- src/index.js | 6 +- src/lib/cap.js | 97 ++++++++++--------- src/lib/util.js | 24 ++--- src/server.js | 18 ++-- src/test/index.js | 119 +++++++++++------------- src/test/testCAP.js | 48 +++++----- src/test/testCards.js | 95 +++++++++---------- src/test/testCities.js | 9 +- src/test/testDB.js | 14 +-- src/test/testFeeds.js | 97 +++++++++---------- src/test/testFloodgauges.js | 23 ++--- src/test/testFloods.js | 87 ++++++++--------- src/test/testInfrastructure.js | 16 ++-- src/test/testReports.js | 30 +++--- src/test/testReportsArchive.js | 72 ++++++-------- src/test/testServer.js | 18 ++-- 36 files changed, 571 insertions(+), 641 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 8e30875..8739534 100755 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,4 +1,4 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import the dependencies we need to handle the request import errorHandler from 'api-error-handler'; @@ -7,7 +7,7 @@ import errorHandler from 'api-error-handler'; import validate from 'celebrate'; // Get the current version -import { version } from '../../package.json'; +import {version} from '../../package.json'; // Import our routes import cards from './routes/cards'; @@ -19,30 +19,30 @@ import infrastructure from './routes/infrastructure'; import reports from './routes/reports'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Return the API version api.get('/', (req, res) => { - res.status(200).json({ version }); + res.status(200).json({version}); }); // Mount the various endpoints - //api.use('/areas', cards({ config, db, logger }));// TODO: local_areas - api.use('/cards', cards({ config, db, logger })); - api.use('/cities', cities({ config, db, logger })); - api.use('/feeds', feeds({ config, db, logger })); - api.use('/floodgauges', floodgauges({ config, db, logger })); - api.use('/floods', floods({ config, db, logger })); - api.use('/infrastructure', infrastructure({ config, db, logger })); - api.use('/reports', reports({ config, db, logger })); + // api.use('/areas', cards({ config, db, logger }));// TODO: local_areas + api.use('/cards', cards({config, db, logger})); + api.use('/cities', cities({config, db, logger})); + api.use('/feeds', feeds({config, db, logger})); + api.use('/floodgauges', floodgauges({config, db, logger})); + api.use('/floods', floods({config, db, logger})); + api.use('/infrastructure', infrastructure({config, db, logger})); + api.use('/reports', reports({config, db, logger})); // Handle validation errors (wording of messages can be overridden using err.isJoi) api.use(validate.errors()); // Handle not found errors api.use((req, res) => { - res.status(404).json({ message: 'URL not found', url: req.url }); + res.status(404).json({message: 'URL not found', url: req.url}); }); // Handle errors gracefully returning nicely formatted json diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index 9c53953..f8da28e 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -1,10 +1,10 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import cards from './model'; // Import any required utility functions -import { cacheResponse, handleResponse } from '../../../lib/util'; +import {cacheResponse, handleResponse} from '../../../lib/util'; // Import validation dependencies import Joi from 'joi'; @@ -25,18 +25,17 @@ const clearCache = () => { apicache.clear(CACHE_GROUP_CARDS); }; -export default ({ config, db, logger }) => { - +export default ({config, db, logger}) => { // Router let api = Router(); // Create an S3 object let s3 = new AWS.S3( { - accessKeyId : config.AWS_S3_ACCESS_KEY_ID , - secretAccessKey : config.AWS_S3_SECRET_ACCESS_KEY, + accessKeyId: config.AWS_S3_ACCESS_KEY_ID, + secretAccessKey: config.AWS_S3_SECRET_ACCESS_KEY, signatureVersion: config.AWS_S3_SIGNATURE_VERSION, - region: config.AWS_REGION + region: config.AWS_REGION, }); // Create a new card and if successful return generated cardId @@ -45,13 +44,13 @@ export default ({ config, db, logger }) => { body: Joi.object().keys({ username: Joi.string().required(), network: Joi.string().required(), - language: Joi.string().valid(config.LANGUAGES).required() - }) + language: Joi.string().valid(config.LANGUAGES).required(), + }), }), (req, res, next) => { let cardId = shortid.generate(); cards(config, db, logger).create(cardId, req.body) - .then((data) => data ? res.status(200).json({ cardId: cardId, created: true }) : + .then((data) => data ? res.status(200).json({cardId: cardId, created: true}) : next(new Error('Failed to create card'))) .catch((err) => { /* istanbul ignore next */ @@ -65,7 +64,7 @@ export default ({ config, db, logger }) => { // Check for the existence of a card api.head('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), validate({ - params: { cardId: Joi.string().required() } + params: {cardId: Joi.string().required()}, }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_CARDS; @@ -83,7 +82,7 @@ export default ({ config, db, logger }) => { // Return a card api.get('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS), validate({ - params: { cardId: Joi.string().min(7).max(14).required() } + params: {cardId: Joi.string().min(7).max(14).required()}, }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_CARDS; @@ -100,27 +99,27 @@ export default ({ config, db, logger }) => { // Update a card record with a report api.put('/:cardId', validate({ - params: { cardId: Joi.string().min(7).max(14).required() }, + params: {cardId: Joi.string().min(7).max(14).required()}, body: Joi.object().keys({ disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(), card_data: Joi.object() .keys({ flood_depth: Joi.number(), - report_type: Joi.string().valid(config.REPORT_TYPES).required() + report_type: Joi.string().valid(config.REPORT_TYPES).required(), }) .required() .when('disaster_type', { is: 'flood', - then: Joi.object({ flood_depth: Joi.number().integer().min(0).max(200).required() }) // b.c is required only when a is true + then: Joi.object({flood_depth: Joi.number().integer().min(0).max(200).required()}), // b.c is required only when a is true }), text: Joi.string().allow(''), image_url: Joi.string().allow(''), created_at: Joi.date().iso().required(), location: Joi.object().required().keys({ lat: Joi.number().min(-90).max(90).required(), - lng: Joi.number().min(-180).max(180).required() - }) - }) + lng: Joi.number().min(-180).max(180).required(), + }), + }), }), (req, res, next) => { try { @@ -128,11 +127,15 @@ export default ({ config, db, logger }) => { cards(config, db, logger).byCardId(req.params.cardId) .then((card) => { // If the card does not exist then return an error message - if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }); + if (!card) { +res.status(404).json({statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'`}); +} // If the card already has received status then return an error message - else if (card && card.received) res.status(409).json({ statusCode: 409, - cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'` }); + else if (card && card.received) { +res.status(409).json({statusCode: 409, + cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'`}); +} // We have a card and it has not yet had a report received else { // Try and submit the report and update the card @@ -140,7 +143,7 @@ export default ({ config, db, logger }) => { .then((data) => { console.log(data); clearCache(); - res.status(200).json({ statusCode: 200, cardId: req.params.cardId, created: true }); + res.status(200).json({statusCode: 200, cardId: req.params.cardId, created: true}); }) .catch((err) => { /* istanbul ignore next */ @@ -150,7 +153,7 @@ export default ({ config, db, logger }) => { }); } }); - } catch(err) { + } catch (err) { /* istanbul ignore next */ logger.error(err); /* istanbul ignore next */ @@ -159,64 +162,63 @@ export default ({ config, db, logger }) => { } ); - //Gives an s3 signed url for the frontend to upload an image to + // Gives an s3 signed url for the frontend to upload an image to api.get('/:cardId/images', validate({ - params: { cardId: Joi.string().min(7).max(14).required() } + params: {cardId: Joi.string().min(7).max(14).required()}, }), (req, res, next) => { let s3params = { Bucket: config.IMAGE_BUCKET, - Key: 'originals/' + req.params.cardId + ".jpg", - ContentType:req.query.file_type + Key: 'originals/' + req.params.cardId + '.jpg', + ContentType: req.query.file_type, }; s3.getSignedUrl('putObject', s3params, (err, data) => { - if (err){ + if (err) { /* istanbul ignore next */ logger.error('could not get signed url from S3'); /* istanbul ignore next */ logger.error(err); } else { - var returnData = { - signedRequest : data, - url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/'+ config.IMAGE_BUCKET+'/'+ s3params.Key + let returnData = { + signedRequest: data, + url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/'+ config.IMAGE_BUCKET+'/'+ s3params.Key, }; - //write the url into the db under image_url for this card + // write the url into the db under image_url for this card cards(config, db, logger).byCardId(req.params.cardId) .then((card) => { - if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }) - else { + if (!card) { +res.status(404).json({statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'`}); +} else { // Try and submit the report and update the card - cards(config, db, logger).updateReport(card, {image_url: 'https://'+config.IMAGES_HOST+'/'+req.params.cardId+'.jpg' }) + cards(config, db, logger).updateReport(card, {image_url: 'https://'+config.IMAGES_HOST+'/'+req.params.cardId+'.jpg'}) .then((data) => { console.log(data); clearCache(); - logger.debug( "s3 signed request: " + returnData.signedRequest); + logger.debug( 's3 signed request: ' + returnData.signedRequest); res.write(JSON.stringify(returnData)); res.end(); - //res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); + // res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); }) .catch((err) => { /* istanbul ignore next */ logger.error(err); /* istanbul ignore next */ next(err); - }) + }); } - - - }) + }); } }); }); // Update a card report with new details including the image URL api.patch('/:cardId', validate({ - params: { cardId: Joi.string().min(7).max(14).required() }, + params: {cardId: Joi.string().min(7).max(14).required()}, body: Joi.object().keys({ - image_url: Joi.string().required() - }) + image_url: Joi.string().required(), + }), }), (req, res, next) => { try { @@ -224,8 +226,10 @@ export default ({ config, db, logger }) => { cards(config, db, logger).byCardId(req.params.cardId) .then((card) => { // If the card does not exist then return an error message - if (!card) res.status(404).json({ statusCode: 404, cardId: req.params.cardId, - message: `No card exists with id '${req.params.cardId}'` }); + if (!card) { +res.status(404).json({statusCode: 404, cardId: req.params.cardId, + message: `No card exists with id '${req.params.cardId}'`}); +} // We have a card else { // Try and submit the report and update the card @@ -233,7 +237,7 @@ export default ({ config, db, logger }) => { .then((data) => { console.log(data); clearCache(); - res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); + res.status(200).json({statusCode: 200, cardId: req.params.cardId, updated: true}); }) .catch((err) => { /* istanbul ignore next */ @@ -243,7 +247,7 @@ export default ({ config, db, logger }) => { }); } }); - } catch(err) { + } catch (err) { /* istanbul ignore next */ logger.error(err); /* istanbul ignore next */ diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index e376f20..e7aa4b0 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -4,14 +4,13 @@ export default (config, db, logger) => ({ // Create a new card entry with the given cardId create: (cardId, body) => new Promise((resolve, reject) => { - // Setup query let query = `INSERT INTO ${config.TABLE_GRASP_CARDS} (card_id, username, network, language, received) VALUES ($1, $2, $3, $4, $5) RETURNING pkey`; // Setup values - let values = [ cardId, body.username, body.network, body.language, false ]; + let values = [cardId, body.username, body.network, body.language, false]; // Execute logger.debug(query, values); @@ -20,13 +19,13 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err)} + reject(err); +} ); }), // Return specific card by id byCardId: (cardId) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT c.card_id, c.username, c.network, c.language, c.received, CASE WHEN r.card_id IS NOT NULL THEN @@ -40,7 +39,7 @@ export default (config, db, logger) => ({ LIMIT 1`; // Setup values - let values = [ cardId ]; + let values = [cardId]; // Execute logger.debug(query, values); @@ -49,23 +48,22 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) - }) + reject(err); + }); }), // Add an entry to the reports table and then update the card record accordingly submitReport: (card, body) => new Promise((resolve, reject) => { - // Setup our queries let queries = [ { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, - values: [ card.card_id, { flood_depth: body.water_depth }, body.text, - body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat ] + values: [card.card_id, {flood_depth: body.water_depth}, body.text, + body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat], }, - /*** TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready + /** * TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) @@ -77,14 +75,14 @@ export default (config, db, logger) => ({ { query: `UPDATE ${config.TABLE_GRASP_CARDS} SET received = TRUE WHERE card_id = $1`, - values: [ card.card_id ] + values: [card.card_id], }, { query: `INSERT INTO ${config.TABLE_GRASP_LOG} (card_id, event_type) VALUES ($1, $2)`, - values: [ card.card_id, 'REPORT SUBMITTED' ] - } + values: [card.card_id, 'REPORT SUBMITTED'], + }, ]; // Log queries to debugger @@ -98,27 +96,26 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); }), // Update the reports table with new report details updateReport: (card, body) => new Promise((resolve, reject) => { - // Setup our queries let queries = [ { query: `UPDATE ${config.TABLE_GRASP_REPORTS} SET image_url = COALESCE($2, image_url) WHERE card_id = $1`, - values: [ card.card_id, body.image_url ] + values: [card.card_id, body.image_url], }, { query: `INSERT INTO ${config.TABLE_GRASP_LOG} (card_id, event_type) VALUES ($1, $2)`, - values: [ card.card_id, 'REPORT UPDATES' ] - } + values: [card.card_id, 'REPORT UPDATES'], + }, ]; // Log queries to debugger @@ -132,8 +129,8 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); - }) + }), }); diff --git a/src/api/routes/cities/index.js b/src/api/routes/cities/index.js index f08a687..1cb88df 100644 --- a/src/api/routes/cities/index.js +++ b/src/api/routes/cities/index.js @@ -1,17 +1,17 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import cities from './model'; // Import any required utility functions -import { cacheResponse, handleGeoResponse } from '../../../lib/util'; +import {cacheResponse, handleGeoResponse} from '../../../lib/util'; // Import validation dependencies import Joi from 'joi'; import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Get a list of infrastructure by type for a given city @@ -19,8 +19,8 @@ export default ({ config, db, logger }) => { validate({ query: { format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => cities(config, db, logger).all() .then((data) => handleGeoResponse(data, req, res, next)) diff --git a/src/api/routes/cities/model.js b/src/api/routes/cities/model.js index af738ea..2b36c8c 100644 --- a/src/api/routes/cities/model.js +++ b/src/api/routes/cities/model.js @@ -4,7 +4,6 @@ export default (config, db, logger) => ({ // A list of all infrastructure matching a given type all: () => new Promise((resolve, reject) => { - // Setup query let query = `SELECT code, name, the_geom FROM cognicity.instance_regions`; @@ -14,6 +13,6 @@ export default (config, db, logger) => ({ db.any(query).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) .catch((err) => reject(err)); - }) + }), }); diff --git a/src/api/routes/feeds/index.js b/src/api/routes/feeds/index.js index 18e4898..ba24dad 100644 --- a/src/api/routes/feeds/index.js +++ b/src/api/routes/feeds/index.js @@ -1,4 +1,4 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import feeds from './model'; @@ -8,7 +8,7 @@ import Joi from 'joi'; import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Create a new qlue record in the database @@ -25,9 +25,9 @@ export default ({ config, db, logger }) => { disaster_type: Joi.string().valid(config.API_FEEDS_QLUE_DISASTER_TYPES).required(), location: Joi.object().required().keys({ lat: Joi.number().min(-90).max(90).required(), - lng: Joi.number().min(-180).max(180).required() - }) - }) + lng: Joi.number().min(-180).max(180).required(), + }), + }), }), (req, res, next) => feeds(config, db, logger).addQlueReport(req.body) .then((data) => res.json(data)) @@ -53,9 +53,9 @@ export default ({ config, db, logger }) => { disaster_type: Joi.string().valid(config.API_FEEDS_DETIK_DISASTER_TYPES).required(), location: Joi.object().required().keys({ latitude: Joi.number().min(-90).max(90).required(), - longitude: Joi.number().min(-180).max(180).required() - }) - }) + longitude: Joi.number().min(-180).max(180).required(), + }), + }), }), (req, res, next) => feeds(config, db, logger).addDetikReport(req.body) .then((data) => res.json(data)) diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index 2831a5e..3605c6c 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -4,51 +4,53 @@ export default (config, db, logger) => ({ // Add a new qlue report addQlueReport: (body) => new Promise((resolve, reject) => { - // Setup query let query = `INSERT INTO ${config.TABLE_FEEDS_QLUE} (post_id, created_at, disaster_type, text, image_url, title, qlue_city, the_geom) VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_Point($8,$9),4326))`; // Setup values - let values = [ body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, - body.title, body.qlue_city, body.location.lng, body.location.lat ]; + let values = [body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, + body.title, body.qlue_city, body.location.lng, body.location.lat]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then(() => resolve({ post_id: body.post_id, created: true })) + .then(() => resolve({post_id: body.post_id, created: true})) .catch((err) => { - if (err.constraint === 'reports_post_id_key') - resolve({ post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table` }); - else + if (err.constraint === 'reports_post_id_key') { +resolve({post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table`}); +} else /* istanbul ignore next */ - reject(err); + { +reject(err); +} }); }), // Add a detik report addDetikReport: (body) => new Promise((resolve, reject) => { - // Setup query let query = `INSERT INTO ${config.TABLE_FEEDS_DETIK} (contribution_id, created_at, disaster_type, title, text, url, image_url, the_geom) VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_POINT($8, $9),4326))`; // Setup values - let values = [ body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, - body.url, body.image_url, body.location.longitude, body.location.latitude ]; + let values = [body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, + body.url, body.image_url, body.location.longitude, body.location.latitude]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then(() => resolve({ contribution_id: body.contribution_id, created: true })) + .then(() => resolve({contribution_id: body.contribution_id, created: true})) .catch((err) => { - if (err.constraint === 'reports_contribution_id_key') - resolve({ contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); - else + if (err.constraint === 'reports_contribution_id_key') { +resolve({contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); +} else /* istanbul ignore next */ - reject(err); + { +reject(err); +} }); - }) + }), }); diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index d3e8b95..92379e0 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -1,17 +1,17 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import floodgauges from './model'; // Import any required utility functions -import { cacheResponse, handleGeoResponse } from '../../../lib/util'; +import {cacheResponse, handleGeoResponse} from '../../../lib/util'; // Import validation dependencies import Joi from 'joi'; import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Get a list of all flood gauge reports @@ -20,8 +20,8 @@ export default ({ config, db, logger }) => { query: { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => floodgauges(config, db, logger).all() .then((data) => handleGeoResponse(data, req, res, next)) @@ -36,11 +36,11 @@ export default ({ config, db, logger }) => { // Get a single flood gauge report api.get('/:id', cacheResponse('1 minute'), validate({ - params: { id: Joi.number().integer().required() } , + params: {id: Joi.number().integer().required()}, query: { format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => floodgauges(config, db, logger).byId(req.params.id) .then((data) => handleGeoResponse(data, req, res, next)) diff --git a/src/api/routes/floodgauges/model.js b/src/api/routes/floodgauges/model.js index b114bc8..cb0a9b4 100644 --- a/src/api/routes/floodgauges/model.js +++ b/src/api/routes/floodgauges/model.js @@ -15,7 +15,7 @@ export default (config, db, logger) => ({ // Setup values let timeWindow = (Date.now() / 1000) - config.API_FLOODGAUGE_REPORTS_TIME_WINDOW; - let values = [ timeWindow, city, config.API_FLOODGAUGE_REPORTS_LIMIT ]; + let values = [timeWindow, city, config.API_FLOODGAUGE_REPORTS_LIMIT]; // Execute logger.debug(query, values); @@ -23,12 +23,10 @@ export default (config, db, logger) => ({ .then((data) => resolve(data)) /* istanbul ignore next */ .catch((err) => reject(err)); - }), // Return specific flood gauge report by id byId: (id) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT gaugeid, gaugenameid, the_geom, array_to_json(array_agg((measuredatetime, depth, warninglevel, @@ -38,7 +36,7 @@ export default (config, db, logger) => ({ GROUP BY gaugeid, the_geom, gaugenameid`; // Setup values - let values = [ id ]; + let values = [id]; // Execute logger.debug(query, values); @@ -48,8 +46,8 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); - }) + }), }); diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index 59dc237..3ab63c1 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -1,10 +1,10 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import floods from './model'; // Import any required utility functions -import { cacheResponse, formatGeo, jwtCheck } from '../../../lib/util'; +import {cacheResponse, formatGeo, jwtCheck} from '../../../lib/util'; // Caching import apicache from 'apicache'; @@ -22,20 +22,20 @@ import validate from 'celebrate'; const REM_STATES = { 1: { severity: 'Unknown', - levelDescription: 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -' + levelDescription: 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -', }, 2: { severity: 'Minor', - levelDescription: 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS' + levelDescription: 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS', }, 3: { severity: 'Moderate', - levelDescription: 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS' + levelDescription: 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS', }, 4: { severity: 'Severe', - levelDescription: 'FLOODING OF OVER 150 CENTIMETERS' - } + levelDescription: 'FLOODING OF OVER 150 CENTIMETERS', + }, }; // Function to clear out the cache @@ -44,7 +44,7 @@ const clearCache = () => { apicache.clear(CACHE_GROUP_FLOODS_STATES); }; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); const cap = new Cap(logger); // Setup our cap formatter @@ -55,14 +55,15 @@ export default ({ config, db, logger }) => { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(['xml'].concat(config.FORMATS)).default(config.FORMAT_DEFAULT), geoformat: Joi.any().valid(['cap'].concat(config.GEO_FORMATS)).default(config.GEO_FORMAT_DEFAULT), - minimum_state: Joi.number().integer().valid(Object.keys(REM_STATES)) - } + minimum_state: Joi.number().integer().valid(Object.keys(REM_STATES)), + }, }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_FLOODS; - if (req.query.geoformat === 'cap' && req.query.format !== 'xml') res.status(400).json({ statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\'' }); - else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') res.status(400).json({ statusCode: 400, message: 'format must be \'json\' when geoformat IN (\'geojson\',\'topojson\')' }); - else floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) + if (req.query.geoformat === 'cap' && req.query.format !== 'xml') res.status(400).json({statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\''}); + else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') res.status(400).json({statusCode: 400, message: 'format must be \'json\' when geoformat IN (\'geojson\',\'topojson\')'}); + else { +floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? // If CAP format has been required first convert to geojson then to CAP @@ -72,7 +73,7 @@ export default ({ config, db, logger }) => { .catch((err) => next(err)) : // Otherwise hand off to geo formatter formatGeo(data, req.query.geoformat) - .then((formatted) => res.status(200).json({ statusCode: 200, result: formatted })) + .then((formatted) => res.status(200).json({statusCode: 200, result: formatted})) /* istanbul ignore next */ .catch((err) => next(err)) ) @@ -82,6 +83,7 @@ export default ({ config, db, logger }) => { /* istanbul ignore next */ next(err); }); +} } ); @@ -91,8 +93,8 @@ export default ({ config, db, logger }) => { query: { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - minimum_state: Joi.number().integer().valid(Object.keys(REM_STATES)) - } + minimum_state: Joi.number().integer().valid(Object.keys(REM_STATES)), + }, }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_FLOODS_STATES; @@ -110,13 +112,13 @@ export default ({ config, db, logger }) => { // Update the flood status of a local area api.put('/:localAreaId', jwtCheck, validate({ - params: { localAreaId: Joi.number().integer().required() }, + params: {localAreaId: Joi.number().integer().required()}, body: Joi.object().keys({ - state: Joi.number().integer().valid(Object.keys(REM_STATES).map((state) => parseInt(state))).required() + state: Joi.number().integer().valid(Object.keys(REM_STATES).map((state) => parseInt(state))).required(), }), query: { - username: Joi.string().required() - } + username: Joi.string().required(), + }, }), (req, res, next) => floods(config, db, logger).updateREMState(req.params.localAreaId, req.body.state, req.query.username) .then(() => { @@ -135,10 +137,10 @@ export default ({ config, db, logger }) => { // Remove the flood status of a local and add a log entry for audit api.delete('/:localAreaId', jwtCheck, validate({ - params: { localAreaId: Joi.number().integer().required() }, + params: {localAreaId: Joi.number().integer().required()}, query: { - username: Joi.string().required() - } + username: Joi.string().required(), + }, }), (req, res, next) => floods(config, db, logger).clearREMState(req.params.localAreaId, req.query.username) .then(() => { diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index 691004a..a73db1d 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -4,7 +4,6 @@ export default (config, db, logger) => ({ // Get all flood reports for a given city all: (city, minimum_state) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT local_area as area_id, state, last_updated FROM ${config.TABLE_REM_STATUS} status, ${config.TABLE_LOCAL_AREAS} area @@ -13,23 +12,22 @@ export default (config, db, logger) => ({ AND ($1 IS NULL OR area.instance_region_code=$1)`; // Setup values - let values = [ city, minimum_state ]; + let values = [city, minimum_state]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => { - resolve(data) + resolve(data); }) .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); }), // Get all flood reports for a given city allGeo: (city, minimum_state) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT la.the_geom, la.pkey as area_id, la.geom_id, la.area_name, la.parent_name, la.city_name, rs.state, rs.last_updated @@ -41,24 +39,23 @@ export default (config, db, logger) => ({ WHERE $1 IS NULL OR instance_region_code = $1`; // Setup values - let values = [ city, minimum_state ]; + let values = [city, minimum_state]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => { - resolve(data) + resolve(data); }) /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); }), // Update the REM state and append to the log updateREMState: (localAreaId, state, username) => new Promise((resolve, reject) => { - // Setup a timestamp with current date/time in ISO format let timestamp = (new Date()).toISOString(); @@ -70,14 +67,14 @@ export default (config, db, logger) => ({ VALUES ( $1, $2, $3 ) ON CONFLICT (local_area) DO UPDATE SET state=$2, last_updated=$3`, - values: [ localAreaId, state, timestamp ] + values: [localAreaId, state, timestamp], }, { query: `INSERT INTO ${config.TABLE_REM_STATUS_LOG} ( local_area, state, changed, username ) VALUES ( $1, $2, $3, $4 )`, - values: [ localAreaId, state, timestamp, username ] - } + values: [localAreaId, state, timestamp, username], + }, ]; // Log queries to debugger @@ -88,18 +85,17 @@ export default (config, db, logger) => ({ return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => { - resolve(data) + resolve(data); }) /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); }), // Remove the REM state record and append to the log clearREMState: (localAreaId, username) => new Promise((resolve, reject) => { - // Setup a timestamp with current date/time in ISO format let timestamp = (new Date()).toISOString(); @@ -108,14 +104,14 @@ export default (config, db, logger) => ({ { query: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = $1`, - values: [ localAreaId ] + values: [localAreaId], }, { query: `INSERT INTO ${config.TABLE_REM_STATUS_LOG} ( local_area, state, changed, username ) VALUES ( $1, $2, $3, $4 )`, - values: [ localAreaId, null, timestamp, username ] - } + values: [localAreaId, null, timestamp, username], + }, ]; // Log queries to debugger @@ -126,13 +122,13 @@ export default (config, db, logger) => ({ return t.batch(queries.map((query) => t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => { - resolve(data) + resolve(data); }) /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ reject(err); - }) - }) + }); + }), }); diff --git a/src/api/routes/infrastructure/index.js b/src/api/routes/infrastructure/index.js index 71f0391..9a555b6 100644 --- a/src/api/routes/infrastructure/index.js +++ b/src/api/routes/infrastructure/index.js @@ -1,28 +1,28 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import infrastructure from './model'; // Import any required utility functions -import { cacheResponse, handleGeoResponse } from '../../../lib/util'; +import {cacheResponse, handleGeoResponse} from '../../../lib/util'; // Import validation dependencies import Joi from 'joi'; import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Get a list of infrastructure by type for a given city api.get('/:type', cacheResponse(config.CACHE_DURATION_INFRASTRUCTURE), validate({ - params: { type: Joi.any().valid(config.INFRASTRUCTURE_TYPES) }, + params: {type: Joi.any().valid(config.INFRASTRUCTURE_TYPES)}, query: { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => infrastructure(config, db, logger).all(req.query.city, req.params.type) .then((data) => handleGeoResponse(data, req, res, next)) diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index cb5dff0..4718959 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -4,14 +4,13 @@ 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 FROM infrastructure.${type} WHERE ($1 IS NULL OR tags->>'instance_region_code'=$1)`; // Setup values - let values = [ city ]; + let values = [city]; // Execute logger.debug(query, values); @@ -20,8 +19,8 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); - }) + }), }); diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 2bf706a..4407eda 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -1,10 +1,10 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import archive from './model'; // Import any required utility functions -import { cacheResponse, handleGeoResponse } from '../../../../lib/util'; +import {cacheResponse, handleGeoResponse} from '../../../../lib/util'; // Import validation dependencies import BaseJoi from 'joi'; @@ -13,7 +13,7 @@ const Joi = BaseJoi.extend(Extension); import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Get a list of all reports @@ -27,8 +27,8 @@ export default ({ config, db, logger }) => { end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), // TODO we should restrict output to geo/topojson only. CAP format doesn't make sense for historic data. format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => archive(config, db, logger).all(req.query.start, req.query.end, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js index ae3e9e6..0f59046 100644 --- a/src/api/routes/reports/archive/model.js +++ b/src/api/routes/reports/archive/model.js @@ -8,7 +8,6 @@ export default (config, db, logger) => ({ * @param {string} city Optional, instance region code (e.g. 'jbd') */ all: (start, end, city) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT pkey, created_at, source, status, url, image_url, disaster_type, report_data, tags, title, text, the_geom @@ -18,14 +17,14 @@ export default (config, db, logger) => ({ AND ($3 IS NULL OR tags->>'instance_region_code'=$3) ORDER BY created_at DESC LIMIT $4`; - //var timeWindow = (Date.now() / 1000) - timeperiod; + // var timeWindow = (Date.now() / 1000) - timeperiod; - let values = [ start, end, city, config.API_REPORTS_LIMIT ]; + let values = [start, end, city, config.API_REPORTS_LIMIT]; // Execute logger.debug(query, values); db.any(query, values).timeout(config.PGTIMEOUT) .then((data) => resolve(data)) .catch((err) => reject(err)); - }) + }), }); diff --git a/src/api/routes/reports/archive/test.js b/src/api/routes/reports/archive/test.js index 397deaf..74e43cc 100644 --- a/src/api/routes/reports/archive/test.js +++ b/src/api/routes/reports/archive/test.js @@ -2,34 +2,34 @@ const request = require('supertest'); const assert = require('chai').assert; require('it-each')(); -import { init } from '../../..'; +import {init} from '../../..'; // Setup an array of tests to run const tests = [ { url: '/reports', exp: { - status: 200 - } + status: 200, + }, }, { url: '/reports?city=jbd', exp: { - status: 200 - } + status: 200, + }, }, { url: '/reports?city=xxx', exp: { - status: 400 - } + status: 400, + }, }, { url: '/reports/0', exp: { - status: 404 - } - } + status: 404, + }, + }, ]; // Run the tests diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index 90c5ece..802ea95 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -1,4 +1,4 @@ -import { Router } from 'express'; +import {Router} from 'express'; // Import our data model import reports from './model'; @@ -6,13 +6,13 @@ import reports from './model'; import archive from './archive'; // Import any required utility functions -import { cacheResponse, handleGeoResponse } from '../../../lib/util'; +import {cacheResponse, handleGeoResponse} from '../../../lib/util'; // Import validation dependencies import Joi from 'joi'; import validate from 'celebrate'; -export default ({ config, db, logger }) => { +export default ({config, db, logger}) => { let api = Router(); // Get a list of all reports @@ -22,8 +22,8 @@ export default ({ config, db, logger }) => { city: Joi.any().valid(config.REGION_CODES), timeperiod: Joi.number().integer().positive().max(config.API_REPORTS_TIME_WINDOW_MAX).default(config.API_REPORTS_TIME_WINDOW), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => reports(config, db, logger).all(req.query.timeperiod, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) @@ -41,11 +41,11 @@ export default ({ config, db, logger }) => { // Get a single report api.get('/:id', cacheResponse('1 minute'), validate({ - params: { id: Joi.number().integer().required() } , + params: {id: Joi.number().integer().required()}, query: { format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT) - } + geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + }, }), (req, res, next) => reports(config, db, logger).byId(req.params.id) .then((data) => handleGeoResponse(data, req, res, next)) diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 0efe68c..846fa1b 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -8,7 +8,6 @@ export default (config, db, logger) => ({ * @param {string} city Optional, instance region code (e.g. 'jbd') */ all: (timeperiod, city) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT pkey, created_at, source, status, url, image_url, disaster_type, report_data, tags, title, text, the_geom @@ -17,9 +16,9 @@ export default (config, db, logger) => ({ AND ($2 IS NULL OR tags->>'instance_region_code'=$2) ORDER BY created_at DESC LIMIT $3`; - var timeWindow = (Date.now() / 1000) - timeperiod; + let timeWindow = (Date.now() / 1000) - timeperiod; - let values = [ timeWindow, city, config.API_REPORTS_LIMIT ]; + let values = [timeWindow, city, config.API_REPORTS_LIMIT]; // Execute logger.debug(query, values); @@ -28,13 +27,12 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); }), // Return specific report by id byId: (id) => new Promise((resolve, reject) => { - // Setup query let query = `SELECT pkey, created_at, source, status, url, image_url, disaster_type, report_data, tags, title, text, the_geom @@ -42,7 +40,7 @@ export default (config, db, logger) => ({ WHERE pkey = $1`; // Setup values - let values = [ id ]; + let values = [id]; // Execute logger.debug(query, values); @@ -51,8 +49,8 @@ export default (config, db, logger) => ({ /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - reject(err) + reject(err); }); - }) + }), }); diff --git a/src/config.js b/src/config.js index 951ebd0..d3bc50a 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,4 @@ -require('dotenv').config({silent:true}); +require('dotenv').config({silent: true}); export default { APP_NAME: process.env.APP_NAME || 'cognicity-server', diff --git a/src/db.js b/src/db.js index 1da9d31..096921e 100755 --- a/src/db.js +++ b/src/db.js @@ -3,11 +3,10 @@ import Promise from 'bluebird'; // Import DB library const pgp = require('pg-promise')({ // Initialization Options - promiseLib: Promise // Use bluebird for enhanced Promises -}) ; + promiseLib: Promise, // Use bluebird for enhanced Promises +}); export default (config, logger) => new Promise((resolve, reject) => { - // Build the connection string const cn = `postgres://${config.PGUSER}:${config.PGPASSWORD}@${config.PGHOST}:${config.PGPORT}/${config.PGDATABASE}?ssl=${config.PGSSL}`; logger.debug(cn); diff --git a/src/index.js b/src/index.js index 08f9dec..b0afa26 100755 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ import initializeDb from './db'; import routes from './api'; // Import server -import { init } from './server.js'; +import {init} from './server.js'; // Import logging libraries import logger from 'winston'; // Application logging @@ -26,7 +26,7 @@ try { fs.accessSync(config.LOG_DIR, fs.W_OK); } logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR : 'current working directory' }`); -} catch(e) { +} catch (e) { // If we cannot write to the desired directory then log in the current directory logger.info(`Cannot log to '${config.LOG_DIR}', logging to current working directory instead`); config.LOG_DIR = ''; @@ -38,7 +38,7 @@ logger.add(logger.transports.File, { json: config.LOG_JSON, // Log in json or plain text maxsize: config.LOG_MAX_FILE_SIZE, // Max size of each file maxFiles: config.LOG_MAX_FILES, // Max number of files - level: config.LOG_LEVEL // Level of log messages + level: config.LOG_LEVEL, // Level of log messages }); // If we are not in development and console logging has not been requested then remove it diff --git a/src/lib/cap.js b/src/lib/cap.js index f39b17a..5d50f1a 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -25,18 +25,17 @@ module.exports = class Cap { geoJsonToAtomCap(features) { let self = this; let feed = { - "@xmlns": "http://www.w3.org/2005/Atom", - id: 'https://data.petabencana.id/floods', - title: 'petabencana.id Flood Affected Areas', - updated: moment().tz('Asia/Jakarta').format(), - author: { + '@xmlns': 'http://www.w3.org/2005/Atom', + 'id': 'https://data.petabencana.id/floods', + 'title': 'petabencana.id Flood Affected Areas', + 'updated': moment().tz('Asia/Jakarta').format(), + 'author': { name: 'petabencana.id', - uri: 'https://petabencana.id/' - } + uri: 'https://petabencana.id/', + }, }; for (let feature of features) { - let alert = self.createAlert( feature ); // If alert creation failed, don't create the entry if (!alert) { @@ -48,16 +47,16 @@ module.exports = class Cap { feed.entry.push({ // Note, this ID does not resolve to a real resource - but enough information is contained in the URL that we could resolve the flooded report at the same point in time id: 'https://data.petabencana.id/floods?parent_name='+encodeURI(feature.properties.parent_name)+'&area_name='+encodeURI(feature.properties.area_name)+'&time='+encodeURI(moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ')), - title: alert.identifier + " Flood Affected Area", + title: alert.identifier + ' Flood Affected Area', updated: moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'), content: { - "@type": "text/xml", - alert: alert - } + '@type': 'text/xml', + 'alert': alert, + }, }); } - return builder.create( {feed:feed} ).end(); + return builder.create( {feed: feed} ).end(); } /** @@ -71,17 +70,17 @@ module.exports = class Cap { let alert = {}; - alert["@xmlns"] = "urn:oasis:names:tc:emergency:cap:1.2"; + alert['@xmlns'] = 'urn:oasis:names:tc:emergency:cap:1.2'; - let identifier = feature.properties.parent_name + "." + feature.properties.area_name + "." + moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'); - identifier = identifier.replace(/ /g,'_'); + let identifier = feature.properties.parent_name + '.' + feature.properties.area_name + '.' + moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'); + identifier = identifier.replace(/ /g, '_'); alert.identifier = encodeURI(identifier); alert.sender = 'BPBD.JAKARTA.GOV.ID'; alert.sent = moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'); - alert.status = "Actual"; - alert.msgType = "Alert"; - alert.scope = "Public"; + alert.status = 'Actual'; + alert.msgType = 'Alert'; + alert.scope = 'Public'; alert.info = self.createInfo( feature ); // If info creation failed, don't create the alert @@ -103,39 +102,39 @@ module.exports = class Cap { let info = {}; - info.category = "Met"; - info.event = "FLOODING"; - info.urgency = "Immediate"; + info.category = 'Met'; + info.event = 'FLOODING'; + info.urgency = 'Immediate'; - let severity = ""; - let levelDescription = ""; + let severity = ''; + let levelDescription = ''; if ( feature.properties.state === 1 ) { - severity = "Unknown"; - levelDescription = "AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -"; + severity = 'Unknown'; + levelDescription = 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -'; } else if ( feature.properties.state === 2 ) { - severity = "Minor"; - levelDescription = "FLOODING OF BETWEEN 10 and 70 CENTIMETERS"; + severity = 'Minor'; + levelDescription = 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS'; } else if ( feature.properties.state === 3 ) { - severity = "Moderate"; - levelDescription = "FLOODING OF BETWEEN 71 and 150 CENTIMETERS"; + severity = 'Moderate'; + levelDescription = 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS'; } else if ( feature.properties.state === 4 ) { - severity = "Severe"; - levelDescription = "FLOODING OF OVER 150 CENTIMETERS"; + severity = 'Severe'; + levelDescription = 'FLOODING OF OVER 150 CENTIMETERS'; } else { - self.logger.silly("Cap: createInfo(): State " + feature.properties.state + " cannot be resolved to a severity"); + self.logger.silly('Cap: createInfo(): State ' + feature.properties.state + ' cannot be resolved to a severity'); return; } info.severity = severity; - info.certainty = "Observed"; - info.senderName = "JAKARTA EMERGENCY MANAGEMENT AGENCY"; - info.headline = "FLOOD WARNING"; + info.certainty = 'Observed'; + info.senderName = 'JAKARTA EMERGENCY MANAGEMENT AGENCY'; + info.headline = 'FLOOD WARNING'; let descriptionTime = moment(feature.properties.last_updated).tz('Asia/Jakarta').format('HH:mm z'); - let descriptionArea = feature.properties.parent_name + ", " + feature.properties.area_name; - info.description = "AT " + descriptionTime + " THE JAKARTA EMERGENCY MANAGEMENT AGENCY OBSERVED " + levelDescription + " IN " + descriptionArea + "."; + let descriptionArea = feature.properties.parent_name + ', ' + feature.properties.area_name; + info.description = 'AT ' + descriptionTime + ' THE JAKARTA EMERGENCY MANAGEMENT AGENCY OBSERVED ' + levelDescription + ' IN ' + descriptionArea + '.'; - info.web = "https://petabencana.id/"; + info.web = 'https://petabencana.id/'; info.area = self.createArea( feature ); // If area creation failed, don't create the info @@ -157,18 +156,18 @@ module.exports = class Cap { let area = {}; - area.areaDesc = feature.properties.area_name + ", " + feature.properties.parent_name; + area.areaDesc = feature.properties.area_name + ', ' + feature.properties.parent_name; // Collate array of polygon-describing strings from different geometry types area.polygon = []; let featurePolygons; - if ( feature.geometry.type === "Polygon" ) { - featurePolygons = [ feature.geometry.coordinates ]; - } else if ( feature.geometry.type === "MultiPolygon" ) { + if ( feature.geometry.type === 'Polygon' ) { + featurePolygons = [feature.geometry.coordinates]; + } else if ( feature.geometry.type === 'MultiPolygon' ) { featurePolygons = feature.geometry.coordinates; } else { /* istanbul ignore next */ - self.logger.error( "Cap: createInfo(): Geometry type '" + feature.geometry.type + "' not supported" ); + self.logger.error( 'Cap: createInfo(): Geometry type \'' + feature.geometry.type + '\' not supported' ); /* istanbul ignore next */ return; } @@ -176,21 +175,21 @@ module.exports = class Cap { // Construct CAP suitable polygon strings (whitespace-delimited WGS84 coordinate pairs - e.g. "lat,lon lat,lon") // See: http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc97699550 - polygon // See: http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc520973440 - self.logger.debug( "Cap: createInfo(): " + featurePolygons.length + " polygons detected for " + area.areaDesc ); + self.logger.debug( 'Cap: createInfo(): ' + featurePolygons.length + ' polygons detected for ' + area.areaDesc ); for (let polygonIndex=0; polygonIndex 1 ) { /* istanbul ignore next */ - self.logger.error( "Cap: createInfo(): Polygon with interior rings is not supported" ); + self.logger.error( 'Cap: createInfo(): Polygon with interior rings is not supported' ); /* istanbul ignore next */ return; } - let polygon = ""; - self.logger.debug( "Cap: createInfo(): " + featurePolygons[polygonIndex][0].length + " points detected in polygon " + polygonIndex ); + let polygon = ''; + self.logger.debug( 'Cap: createInfo(): ' + featurePolygons[polygonIndex][0].length + ' points detected in polygon ' + polygonIndex ); for (let pointIndex=0; pointIndex cache(duration, config.CACHE); // Configure our JWT checker const jwtCheck = jwt({ secret: new Buffer(config.AUTH0_SECRET), - audience: config.AUTH0_CLIENT_ID + audience: config.AUTH0_CLIENT_ID, }); // TODO: Move to single auth0 mechanism once they support SPA auth using API -/*const jwtCheck = jwt({ +/* const jwtCheck = jwt({ credentialsRequired: config.SECURE_AUTH0, secret: jwks.expressJwtSecret({ cache: true, @@ -39,16 +39,16 @@ dbgeo.defaults = { outputFormat: config.GEO_FORMAT_DEFAULT, geometryColumn: 'the_geom', geometryType: 'wkb', - precision: config.GEO_PRECISION + precision: config.GEO_PRECISION, }; // Format the geographic response with the required geo format const formatGeo = (body, outputFormat) => new Promise((resolve, reject) => { // Check that body is an array, required by dbgeo.parse - if (Object.prototype.toString.call( body ) !== '[object Array]'){ + if (Object.prototype.toString.call( body ) !== '[object Array]') { body = [body]; // Force to array } - dbgeo.parse(body, { outputFormat }, (err, formatted) => { + dbgeo.parse(body, {outputFormat}, (err, formatted) => { if (err) reject(err); resolve(formatted); }); @@ -58,23 +58,23 @@ const formatGeo = (body, outputFormat) => new Promise((resolve, reject) => { // status 200 or not found 404, catch and forward any errors in the process const handleGeoResponse = (data, req, res, next) => { return !data ? - res.status(404).json({ statusCode: 404, found: false, result: null }) : + res.status(404).json({statusCode: 404, found: false, result: null}) : formatGeo(data, req.query.geoformat) - .then((formatted) => res.status(200).json({ statusCode: 200, result: formatted })) + .then((formatted) => res.status(200).json({statusCode: 200, result: formatted})) /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ - next(err) + next(err); }); }; // Handle a regular response, send back result or 404 const handleResponse = (data, req, res) => { return !data ? - res.status(404).json({ statusCode: 404, found: false, result: null }) : - res.status(200).json({ statusCode: 200, result: data }); + res.status(404).json({statusCode: 404, found: false, result: null}) : + res.status(200).json({statusCode: 200, result: data}); }; module.exports = { - cacheResponse, formatGeo, handleResponse, handleGeoResponse, jwtCheck + cacheResponse, formatGeo, handleResponse, handleGeoResponse, jwtCheck, }; diff --git a/src/server.js b/src/server.js index 7e4cd50..9729be0 100644 --- a/src/server.js +++ b/src/server.js @@ -13,7 +13,6 @@ import morgan from 'morgan'; // Express logging // Function to initialize the api server const init = (config, initializeDb, routes, logger) => new Promise((resolve, reject) => { - // Create the server let app = express(); app.server = http.createServer(app); @@ -22,11 +21,11 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej const winstonStream = { write: function(message) { logger.info(message.slice(0, -1)); - } + }, }; // Setup express logger - app.use(morgan('combined', { stream : winstonStream })); + app.use(morgan('combined', {stream: winstonStream})); // Compress responses if required but only if caching is disabled if (config.COMPRESS && !config.CACHE) { @@ -35,7 +34,7 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej // Provide CORS support (not required if behind API gateway) if (config.CORS) { - app.use(cors({ exposedHeaders: config.CORS_HEADERS })); + app.use(cors({exposedHeaders: config.CORS_HEADERS})); } // Provide response time header in response @@ -44,7 +43,7 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej } // Parse body messages into json - app.use(bodyParser.json({ limit : config.BODY_LIMIT })); + app.use(bodyParser.json({limit: config.BODY_LIMIT})); // Try and connect to the db initializeDb(config, logger) @@ -53,11 +52,10 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej logger.debug('Successfully connected to DB'); // Mount the routes - app.use('/', routes({ config, db, logger })); + app.use('/', routes({config, db, logger})); // App is ready to go, resolve the promise resolve(app); - }) .catch((err) => { /* istanbul ignore next */ @@ -67,9 +65,9 @@ const init = (config, initializeDb, routes, logger) => new Promise((resolve, rej /* istanbul ignore next */ // We cannot continue without a DB, reject reject(err); - }) -}) + }); +}); // Export the init function for use externally -module.exports = { init }; +module.exports = {init}; diff --git a/src/test/index.js b/src/test/index.js index c7f829f..92f97e3 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -11,14 +11,14 @@ import initializeDb from '../db'; import routes from '../api'; // Import server object -import { init } from '../server.js'; +import {init} from '../server.js'; // Mock logger object for app const winston = require('winston'); const logger = new (winston.Logger)({ transports: [ - new (winston.transports.Console)({ raw: true, level: 'debug' }), - ] + new (winston.transports.Console)({raw: true, level: 'debug'}), + ], }); @@ -40,7 +40,6 @@ const pg = require('pg'); // Create a top-level testing harness describe('Cognicity Server Testing Harness', function() { - // PG config string for dummy data inserts let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; @@ -49,9 +48,9 @@ let reportid = 1; // Auth JWT support const jwt = require('jsonwebtoken'); -let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_CLIENT_ID}); +let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH0_CLIENT_ID}); - it('Server starts', function(done){ + it('Server starts', function(done) { // Test optional server params config.COMPRESS = true; config.CORS = true; @@ -59,7 +58,6 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ // Initialise init(config, initializeDb, routes, logger).then((app) => { - testServer(app); testCards(app); testCities(app); @@ -74,18 +72,17 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ // Removes dummy data describe('Cleans up', function() { - it ('Removes dummy report data', function(done){ + it('Removes dummy report data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_REPORTS} WHERE source = 'grasp' AND text = 'integration testing';`, - values: [ reportid ] + values: [reportid], }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -93,18 +90,17 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); }); - it ('Removes dummy cards data', function(done){ + it('Removes dummy cards data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_GRASP_CARDS} WHERE username = 'testuser' AND network = 'test network';`, - values: [ reportid ] + values: [reportid], }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -112,18 +108,17 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); }); - it ('Removes dummy cards data', function(done){ + it('Removes dummy cards data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_GRASP_REPORTS} WHERE text = 'integration testing';`, - values: [ reportid ] + values: [reportid], }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -132,17 +127,16 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy data from REM floods table - it ('Removes dummy flood data', function(done){ + it('Removes dummy flood data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 5`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -151,17 +145,16 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy data from REM floods table - it ('Removes dummy flood data from log', function(done){ + it('Removes dummy flood data from log', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_REM_STATUS_LOG} WHERE username = testing`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -170,17 +163,16 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy data from qlue table - it ('Removes dummy qlue data', function(done){ + it('Removes dummy qlue data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -189,17 +181,16 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy qlue data from all reports table - it ('Removes dummy qlue data', function(done){ + it('Removes dummy qlue data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'qlue'`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -208,17 +199,16 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy data from detik table - it ('Removes dummy detik data', function(done){ + it('Removes dummy detik data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } @@ -227,24 +217,23 @@ let token = jwt.sign({},new Buffer(config.AUTH0_SECRET),{audience: config.AUTH0_ }); // Remove dummy detik data from all reports table - it ('Removes dummy detik data', function(done){ + it('Removes dummy detik data', function(done) { let queryObject = { text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'detik'`, }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone){ - client.query(queryObject, function(){ + pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { + client.query(queryObject, function() { if (err) { - console.log(err.message) + console.log(err.message); pgDone(); - } - else { + } else { pgDone(); done(); } }); }); }); - return (done()) + return (done()); }); }); }); diff --git a/src/test/testCAP.js b/src/test/testCAP.js index 70d267d..cec73ff 100644 --- a/src/test/testCAP.js +++ b/src/test/testCAP.js @@ -4,80 +4,78 @@ const test = require('unit.js'); // Cap formatter helper import Cap from '../lib/cap'; -export default function (logger){ - - describe('CAP Utility', function(){ - +export default function(logger) { + describe('CAP Utility', function() { const cap = new Cap(logger); // Setup our cap formatter // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; // Test geometry string creation - it('Can create suitable polygon strings from geojson features', function(done){ + it('Can create suitable polygon strings from geojson features', function(done) { cap.createArea(feature); let result = cap.createArea(feature); - test.value(typeof(result.polygon[0])).is('string') + test.value(typeof(result.polygon[0])).is('string'); done(); }); // dummy data (multipolygon) - feature = {"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + feature = {'type': 'Feature', 'geometry': {'type': 'MultiPolygon', 'coordinates': [[[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; // Test multigeometry string creation - it('Will operate on multipolygon objects', function(done){ + it('Will operate on multipolygon objects', function(done) { let result = cap.createArea(feature); - test.value(typeof(result.polygon[0])).is('string') + test.value(typeof(result.polygon[0])).is('string'); done(); }); // Test level severe - it('Create the correct severity levels', function(done){ + it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":4,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; let result = cap.createInfo(feature); - test.value(result.severity).is("Severe") + test.value(result.severity).is('Severe'); done(); }); // Test level moderate - it('Create the correct severity levels', function(done){ + it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":3,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 3, 'last_updated': '2017-03-31T02:45:52.574Z'}}; let result = cap.createInfo(feature); - test.value(result.severity).is("Moderate") + test.value(result.severity).is('Moderate'); done(); }); // Test level minor - it('Create the correct severity levels', function(done){ + it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":2,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 2, 'last_updated': '2017-03-31T02:45:52.574Z'}}; let result = cap.createInfo(feature); - test.value(result.severity).is("Minor") + test.value(result.severity).is('Minor'); done(); }); // Test level Unknown - it('Create the correct severity levels', function(done){ + it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[106.8367359996,-6.2305420003],[106.8367039999,-6.2307239997],[106.8367995401,-6.2301399095],[106.8367359996,-6.2305420003]]]},"properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 1, 'last_updated': '2017-03-31T02:45:52.574Z'}}; let result = cap.createInfo(feature); - test.value(result.severity).is("Unknown") + test.value(result.severity).is('Unknown'); done(); }); // Test bad geometry will bubble up internall within CAP module - it('Test unsupported complex polygon geometry (interior rings)', function(done){ + it('Test unsupported complex polygon geometry (interior rings)', function(done) { // dummy data (polygon) - let feature = {"type":"Feature","geometry":{"type":"Polygon","coordinates" : [[ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ], [ [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 2 ] , [ 2 , 2 ] ]]}, "properties":{"area_id":"1346","geom_id":"3171100002004000","area_name":"RW 04","parent_name":"KUNINGAN TIMUR","city_name":"Jakarta","state":1,"last_updated":"2017-03-31T02:45:52.574Z"}} + let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[0, 0], [3, 6], [6, 1], [0, 0]], [[2, 2], [3, 3], [4, 2], [2, 2]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 1, 'last_updated': '2017-03-31T02:45:52.574Z'}}; let result = cap.createInfo(feature); - test.value(result).is(undefined) + test.value(result).is(undefined); done(); }); }); diff --git a/src/test/testCards.js b/src/test/testCards.js index 130c7e8..70df9db 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -1,35 +1,33 @@ const test = require('unit.js'); -export default function (app){ +export default function(app) { // Cards endpoint describe('Cards endpoint', function() { // Cards - it('Return 404 if card requested without ID (GET /cards)', function(done){ + it('Return 404 if card requested without ID (GET /cards)', function(done) { test.httpAgent(app) .get('/cards') .expect(404) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can get reports - it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done){ + it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done) { test.httpAgent(app) .get('/cards/1') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); @@ -38,26 +36,24 @@ export default function (app){ // Cards end to end test describe('End-to-end card test', function() { - let cardId = '0'; // Request a card, submit and get resulting card details - it('Get card one time link', function(done){ + it('Get card one time link', function(done) { test.httpAgent(app) .post('/cards') .send({ - "username": "testuser", - "network": "test network", - "language": "en" + 'username': 'testuser', + 'network': 'test network', + 'language': 'en', }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - cardId = res.body.cardId + } else { + cardId = res.body.cardId; test.value(res.body.created).is(true); done(); } @@ -65,44 +61,42 @@ export default function (app){ }); // Check HEAD request on cardId - it('Check HEAD request on cardId', function(done){ + it('Check HEAD request on cardId', function(done) { test.httpAgent(app) .head('/cards/'+cardId) .expect(200) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Request a card, submit and get resulting report - it('Put card data', function(done){ + it('Put card data', function(done) { test.httpAgent(app) .put('/cards/'+cardId) .send({ - "disaster_type":"flood", - "card_data":{ - "flood_depth": 20, - "report_type": "flood" + 'disaster_type': 'flood', + 'card_data': { + 'flood_depth': 20, + 'report_type': 'flood', + }, + 'text': 'integration testing', + 'created_at': '2017-06-07T07:00:00+0700', + 'location': { + 'lat': -6.4, + 'lng': 106.6, }, - "text": "integration testing", - "created_at": "2017-06-07T07:00:00+0700", - "location": { - "lat": -6.4, - "lng": 106.6 - } }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { console.log(res.body); done(); } @@ -110,16 +104,15 @@ export default function (app){ }); // Get signed URL for card image - it('Get card image link', function(done){ + it('Get card image link', function(done) { this.timeout(15000); // nested call test.httpAgent(app) .get('/cards/'+cardId+'/images') .expect(200) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { console.log(res.body); done(); } @@ -127,40 +120,38 @@ export default function (app){ }); // Request a card, submit and get resulting report - it('Get card data', function(done){ + it('Get card data', function(done) { test.httpAgent(app) .get('/cards/'+cardId) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { test.value(res.body.result.card_id).is(cardId); test.value(res.body.result.username).is('testuser'); - test.value(res.body.result.network).is('test network') - test.value(res.body.result.language).is('en') - test.value(res.body.result.report.text).is('integration testing') + test.value(res.body.result.network).is('test network'); + test.value(res.body.result.language).is('en'); + test.value(res.body.result.report.text).is('integration testing'); done(); } }); }); // Update a card - it('Update card data', function(done){ + it('Update card data', function(done) { test.httpAgent(app) .patch('/cards/'+cardId) .send({ - "image_url": "dummy image url" + 'image_url': 'dummy image url', }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testCities.js b/src/test/testCities.js index 4270262..c312119 100644 --- a/src/test/testCities.js +++ b/src/test/testCities.js @@ -1,19 +1,18 @@ const test = require('unit.js'); -export default function (app){ +export default function(app) { // Cities endpoint describe('Cities endpoint', function() { // Can get cities - it('Return 200 for cities list', function(done){ + it('Return 200 for cities list', function(done) { test.httpAgent(app) .get('/cities') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testDB.js b/src/test/testDB.js index 682db55..a311951 100644 --- a/src/test/testDB.js +++ b/src/test/testDB.js @@ -4,22 +4,22 @@ const test = require('unit.js'); // Database utility import initializeDb from '../db'; -export default function(){ +export default function() { describe('Test CogniCity Server Database Module', function() { - it('Catches errors on startup', function(done){ + it('Catches errors on startup', function(done) { // Try and connect to the db let config = {}; let logger = {}; - logger.error =function(err){ + logger.error =function(err) { console.log(err); - } - logger.debug =function(err){ + }; + logger.debug =function(err) { console.log(err); - } + }; initializeDb(config, logger) .then((db) => { test.value(db).is(null); - //done(); do nothing here, an error should be forced by empty config + // done(); do nothing here, an error should be forced by empty config }) .catch((err) => { console.log(err); diff --git a/src/test/testFeeds.js b/src/test/testFeeds.js index c2baaab..ece0af8 100644 --- a/src/test/testFeeds.js +++ b/src/test/testFeeds.js @@ -1,111 +1,106 @@ const test = require('unit.js'); -export default function (app){ +export default function(app) { // Feeds endpoint describe('Feeds endpoint', function() { // Can post to qlue - it('Return 200 for post to feeds/qlue', function(done){ + it('Return 200 for post to feeds/qlue', function(done) { test.httpAgent(app) .post('/feeds/qlue') .send({ - "post_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "qlue_city": "surabaya", - "disaster_type": "flood", - "text": "test report", - "location":{ - "lat":45, - "lng":45 - } + 'post_id': '9999', + 'created_at': '2017-06-07T14:32+0700', + 'qlue_city': 'surabaya', + 'disaster_type': 'flood', + 'text': 'test report', + 'location': { + 'lat': 45, + 'lng': 45, + }, }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Catch duplicate post to qlue - it('Catch duplicate entry to feeds/qlue', function(done){ + it('Catch duplicate entry to feeds/qlue', function(done) { test.httpAgent(app) .post('/feeds/qlue') .send({ - "post_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "qlue_city": "surabaya", - "disaster_type": "flood", - "text": "test report", - "location":{ - "lat":45, - "lng":45 - } + 'post_id': '9999', + 'created_at': '2017-06-07T14:32+0700', + 'qlue_city': 'surabaya', + 'disaster_type': 'flood', + 'text': 'test report', + 'location': { + 'lat': 45, + 'lng': 45, + }, }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - test.value(res.body.created).is(false) + } else { + test.value(res.body.created).is(false); done(); } - }); }); // Can post to detik - it('Return 200 for post to feeds/detik', function(done){ + it('Return 200 for post to feeds/detik', function(done) { test.httpAgent(app) .post('/feeds/detik') .send({ - "contribution_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "disaster_type": "flood", - "location":{ - "latitude":45, - "longitude":45 + 'contribution_id': '9999', + 'created_at': '2017-06-07T14:32+0700', + 'disaster_type': 'flood', + 'location': { + 'latitude': 45, + 'longitude': 45, }, - "text":"test report" + 'text': 'test report', }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Catch duplicate post to detik - it('Return 200 for post to feeds/detik', function(done){ + it('Return 200 for post to feeds/detik', function(done) { test.httpAgent(app) .post('/feeds/detik') .send({ - "contribution_id": "9999", - "created_at": "2017-06-07T14:32+0700", - "disaster_type": "flood", - "location":{ - "latitude":45, - "longitude":45 + 'contribution_id': '9999', + 'created_at': '2017-06-07T14:32+0700', + 'disaster_type': 'flood', + 'location': { + 'latitude': 45, + 'longitude': 45, }, - "text":"test report" + 'text': 'test report', }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { test.value(res.body.created).is(false); done(); } diff --git a/src/test/testFloodgauges.js b/src/test/testFloodgauges.js index 6ecf6e9..af68838 100644 --- a/src/test/testFloodgauges.js +++ b/src/test/testFloodgauges.js @@ -1,51 +1,48 @@ const test = require('unit.js'); -export default function (app){ +export default function(app) { // Flood gauges endpoint describe('Flood gauges endpoint', function() { // Can get flood gauge data - it('Return 200 for get /floodgauges', function(done){ + it('Return 200 for get /floodgauges', function(done) { test.httpAgent(app) .get('/floodgauges') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Catch invalid city in floodgauge - it('Return 400 for get /floodgauges?city=xxx', function(done){ + it('Return 400 for get /floodgauges?city=xxx', function(done) { test.httpAgent(app) .get('/floodgauges?city=xxx') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Catch invalid floodgauge id - it('Return 404 for get /floodgauges/:id', function(done){ + it('Return 404 for get /floodgauges/:id', function(done) { test.httpAgent(app) .get('/floodgauges/0') .expect(404) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testFloods.js b/src/test/testFloods.js index 5ef4f01..81f8183 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -1,164 +1,153 @@ const test = require('unit.js'); -export default function (app, jwt){ - +export default function(app, jwt) { // Floods endpoint -describe('Flood areas endpoint', function(){ - +describe('Flood areas endpoint', function() { // Test put flood auth - it ('Catch bad auth for put a flood (PUT /floods/:id)', function(done){ + it('Catch bad auth for put a flood (PUT /floods/:id)', function(done) { test.httpAgent(app) .put('/floods/5') .send({ - "state": "2" + 'state': '2', }) .expect(401) .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ + .end(function(err, res) { + if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Test delete flood auth - it ('Catch bad auth for delete a flood (PUT /floods/:id)', function(done){ + it('Catch bad auth for delete a flood (PUT /floods/:id)', function(done) { test.httpAgent(app) .delete('/floods/5') .send({ - "username": "testing" + 'username': 'testing', }) .expect(401) .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ + .end(function(err, res) { + if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Test put flood auth - it ('Put a flood with auth(PUT /floods/:id)', function(done){ + it('Put a flood with auth(PUT /floods/:id)', function(done) { test.httpAgent(app) .put('/floods/5?username=testing') - .set({ 'Authorization': 'Bearer ' + jwt}) + .set({'Authorization': 'Bearer ' + jwt}) .send({ - "state": "2" + 'state': '2', }) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ + .end(function(err, res) { + if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Test delete flood auth - it ('Delete a flood (PUT /floods/:id)', function(done){ + it('Delete a flood (PUT /floods/:id)', function(done) { test.httpAgent(app) .delete('/floods/5?username=testing') - .set({ 'Authorization': 'Bearer ' + jwt}) + .set({'Authorization': 'Bearer ' + jwt}) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ - if (err){ + .end(function(err, res) { + if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Get floods - it('Get floods (GET /floods)', function(done){ + it('Get floods (GET /floods)', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Get floods - it('Get severe floods (GET /floods?minimum_state=3)', function(done){ + it('Get severe floods (GET /floods?minimum_state=3)', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?minimum_state=3') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Just get flood states - it('Get flood states without geo (GET /floods/states)', function(done){ + it('Get flood states without geo (GET /floods/states)', function(done) { test.httpAgent(app) .get('/floods/states') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Get geographic floods - it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done){ + it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods/?format=json&geoformat=geojson') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can get reports in CAP format - it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done){ + it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) .expect('Content-Type', /text/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testInfrastructure.js b/src/test/testInfrastructure.js index 8d85674..ad83a74 100644 --- a/src/test/testInfrastructure.js +++ b/src/test/testInfrastructure.js @@ -1,35 +1,33 @@ const test = require('unit.js'); -export default function (app){ +export default function(app) { // Infrastructure endpoint describe('Infrastructure endpoint', function() { // Catch invalid top-level infrastructure endpoint - it('Return 404 for get /infrastructure', function(done){ + it('Return 404 for get /infrastructure', function(done) { test.httpAgent(app) .get('/infrastructure') .expect(404) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Return waterways infrastructure - it('Return 200 for get /infrastructure/waterways', function(done){ + it('Return 200 for get /infrastructure/waterways', function(done) { test.httpAgent(app) .get('/infrastructure/waterways') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testReports.js b/src/test/testReports.js index 880b12f..b2d2e67 100644 --- a/src/test/testReports.js +++ b/src/test/testReports.js @@ -1,67 +1,63 @@ const test = require('unit.js'); -export default function (app, reportid){ +export default function(app, reportid) { // Reports endpoint describe('Reports endpoint', function() { // Can get reports - it('Get all reports (GET /reports)', function(done){ + it('Get all reports (GET /reports)', function(done) { test.httpAgent(app) .get('/reports') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can get reports by city - it('Get reports by city /reports?city=jbd', function(done){ + it('Get reports by city /reports?city=jbd', function(done) { test.httpAgent(app) .get('/reports?city=jbd') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Catch report by city error - it('Get reports by city /reports?city=xxx', function(done){ + it('Get reports by city /reports?city=xxx', function(done) { test.httpAgent(app) .get('/reports?city=xxx') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can get reports - it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done){ + it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done) { test.httpAgent(app) .get('/reports/'+reportid) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index e4a69ac..92178ad 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -1,148 +1,138 @@ const test = require('unit.js'); // TODO - test against an actual time in the database? Entered through cards.js (or add a new call to cards here) -export default function (app){ +export default function(app) { // Reports endpoint describe('Reports Archive Endpoint', function() { // Can get reports between given timestamps - it('Can get reports between given timestamps', function(done){ + it('Can get reports between given timestamps', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); - it('Can get reports between given timestamps as geojson', function(done){ + it('Can get reports between given timestamps as geojson', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - test.value(res.body.result.type).is("FeatureCollection"); + } else { + test.value(res.body.result.type).is('FeatureCollection'); done(); } }); }); - it('Can get any reports by ID', function(done){ + it('Can get any reports by ID', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - test.value(res.body.result.type).is("FeatureCollection"); + } else { + test.value(res.body.result.type).is('FeatureCollection'); done(); } }); }); - it('Can get reports between given timestamps as topojson', function(done){ + it('Can get reports between given timestamps as topojson', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=topojson') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { - - test.value(res.body.result.type).is("Topology"); + } else { + test.value(res.body.result.type).is('Topology'); done(); } }); }); // Can catch no start parameter - it('Required start parameter by default', function(done){ + it('Required start parameter by default', function(done) { test.httpAgent(app) .get('/reports/archive?end=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can catch no end parameter - it('Required end parameter by default', function(done){ + it('Required end parameter by default', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can catch no UTC offset in end parameter - it('Required end parameter to have a UTC offset', function(done){ + it('Required end parameter to have a UTC offset', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-02-21T07:00:00%2B0700&end=2017-02-22T07:00:00') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can catch no UTC offset in start parameter - it('Required start parameter to have a UTC offset', function(done){ + it('Required start parameter to have a UTC offset', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-02-21T07:00:00') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); // Can catch no UTC offset in start parameter - it('Catches badly formed time stamp', function(done){ + it('Catches badly formed time stamp', function(done) { test.httpAgent(app) .get('/reports/archive?start=2017-02-21') .expect(400) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); diff --git a/src/test/testServer.js b/src/test/testServer.js index 49b752a..70571c3 100644 --- a/src/test/testServer.js +++ b/src/test/testServer.js @@ -1,32 +1,30 @@ const test = require('unit.js'); -export default function (app){ - describe('Top level API endpoint', function(){ - it('Gets current API version', function(done){ +export default function(app) { + describe('Top level API endpoint', function() { + it('Gets current API version', function(done) { test.httpAgent(app) .get('/') .expect(200) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); }); - it('Can handle unknown routes', function(done){ + it('Can handle unknown routes', function(done) { test.httpAgent(app) .get('/moon') .expect(404) .expect('Content-Type', /json/) - .end(function(err, res){ + .end(function(err, res) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); - } - else { + } else { done(); } }); From c034c55b33f0489d9f9f137a79305e41564af239 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:33:02 -0400 Subject: [PATCH 089/160] Added jsdoc comments --- src/test/testServer.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/testServer.js b/src/test/testServer.js index 70571c3..f54b3a3 100644 --- a/src/test/testServer.js +++ b/src/test/testServer.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testServer module + * @module test/testServer + * A module to test top-level API routes from the server + */ +import * as test from 'unit.js'; + +/** + * Test top-level server routes + * @alias module:test/testServer + * @param {Object} app - CogniCity server app object + */ export default function(app) { describe('Top level API endpoint', function() { it('Gets current API version', function(done) { From f4d5feb1a6955e55a9052fb5fae6501c5841cc90 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:33:18 -0400 Subject: [PATCH 090/160] specify node and mocha environments --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9b76622..615a829 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ module.exports = { "extends": ["eslint:recommended", "google"], "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, - "env": { "es6": true } + "env": { "es6": true, "node": true, "mocha": true } }; From a45631a65c0374b43dfede4be4446ae4ca04ea0a Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:51:31 -0400 Subject: [PATCH 091/160] Added JSDoc comments, fixed long lines --- src/test/testReportsArchive.js | 65 ++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index 92178ad..8300582 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -1,12 +1,28 @@ -const test = require('unit.js'); -// TODO - test against an actual time in the database? Entered through cards.js (or add a new call to cards here) +/** + * testReportsArchive module + * @module test/testReportsArchive + * A module to test the /reports/archive endpoint + */ + +import * as test from 'unit.js'; + +// TODO +// - test against an actual time in the database +// - Entered through cards.js (or add a new call to cards here) + +/** + * Test top-level server routes + * @alias module:test/testServer + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Reports endpoint describe('Reports Archive Endpoint', function() { // Can get reports between given timestamps it('Can get reports between given timestamps', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') + .get('/reports/archive? \ + start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -20,22 +36,10 @@ export default function(app) { it('Can get reports between given timestamps as geojson', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - test.fail(err.message + ' ' + JSON.stringify(res)); - } else { - test.value(res.body.result.type).is('FeatureCollection'); - done(); - } - }); - }); - - it('Can get any reports by ID', function(done) { - test.httpAgent(app) - .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') + .get('/reports/archive? \ + start=2017-06-07T00:00:00%2B0700& \ + end=2017-06-08T23:00:00%2B0700& \ + format=json&geoformat=geojson') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -48,9 +52,12 @@ export default function(app) { }); }); - it('Can get reports between given timestamps as topojson', function(done) { + it('Can get reports between timestamps as topojson', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=topojson') + .get('/reports/archive? \ + start=2017-06-07T00:00:00%2B0700& \ + end=2017-06-08T23:00:00%2B0700& \ + format=json&geoformat=topojson') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -66,7 +73,8 @@ export default function(app) { // Can catch no start parameter it('Required start parameter by default', function(done) { test.httpAgent(app) - .get('/reports/archive?end=2017-02-22T07:00:00%2B0700') + .get('/reports/archive? \ + end=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -81,7 +89,8 @@ export default function(app) { // Can catch no end parameter it('Required end parameter by default', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-02-22T07:00:00%2B0700') + .get('/reports/archive? \ + start=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -96,7 +105,9 @@ export default function(app) { // Can catch no UTC offset in end parameter it('Required end parameter to have a UTC offset', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-02-21T07:00:00%2B0700&end=2017-02-22T07:00:00') + .get('/reports/archive? \ + start=2017-02-21T07:00:00%2B0700& \ + end=2017-02-22T07:00:00') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -111,7 +122,8 @@ export default function(app) { // Can catch no UTC offset in start parameter it('Required start parameter to have a UTC offset', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-02-21T07:00:00') + .get('/reports/archive? \ + start=2017-02-21T07:00:00') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -126,7 +138,8 @@ export default function(app) { // Can catch no UTC offset in start parameter it('Catches badly formed time stamp', function(done) { test.httpAgent(app) - .get('/reports/archive?start=2017-02-21') + .get('/reports/archive? \ + start=2017-02-21') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { From ff666164951b98295cb7483e2fc24f299e4f8d69 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:52:18 -0400 Subject: [PATCH 092/160] disabled multi string check --- .eslintrc.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 615a829..bbdec48 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,18 @@ module.exports = { "extends": ["eslint:recommended", "google"], - "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, - "env": { "es6": true, "node": true, "mocha": true } + "parserOptions": + { + "ecmaVersion": 6, + "sourceType": "module" + }, + "env": + { "browser": false, + "es6": true, + "node": true, + "mocha": true + }, + "rules": + { + "no-multi-str": "off" // not a problem in node apps + } }; From 0fcdd646eeef33939761bf9cb3135e227a2c3bf4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 20:55:58 -0400 Subject: [PATCH 093/160] updated JSDoc comments --- src/test/testReports.js | 16 ++++++++++++++-- src/test/testReportsArchive.js | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/test/testReports.js b/src/test/testReports.js index b2d2e67..79ae8ff 100644 --- a/src/test/testReports.js +++ b/src/test/testReports.js @@ -1,5 +1,17 @@ -const test = require('unit.js'); +/** + * testReports module + * @module test/testReports + * A module to test the /reports endpoint + */ +import * as test from 'unit.js'; + +/** + * Test reports endpoint + * @alias module:test/testReports + * @param {Object} app - CogniCity server app object + * @param {Number} reportid - CogniCity report ID to test against + */ export default function(app, reportid) { // Reports endpoint describe('Reports endpoint', function() { @@ -49,7 +61,7 @@ export default function(app, reportid) { }); // Can get reports - it('Has a get all reports/:id endpoint (GET /reports/:id)', function(done) { + it('Get all reports/:id endpoint (GET /reports/:id)', function(done) { test.httpAgent(app) .get('/reports/'+reportid) .expect(200) diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index 8300582..6954b3b 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -11,8 +11,8 @@ import * as test from 'unit.js'; // - Entered through cards.js (or add a new call to cards here) /** - * Test top-level server routes - * @alias module:test/testServer + * Test reports archive endpoint + * @alias module:test/testReportsArchive * @param {Object} app - CogniCity server app object */ export default function(app) { From 5e85fcbc26ac202d299770c807cb4ee374b4d128 Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 29 Jun 2017 10:59:34 +1000 Subject: [PATCH 094/160] latest API (pre change of image upload) --- swagger-api.json | 355 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 348 insertions(+), 7 deletions(-) diff --git a/swagger-api.json b/swagger-api.json index 15808d2..6fab5e4 100644 --- a/swagger-api.json +++ b/swagger-api.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "2017-01-13T15:33:17Z", + "version": "2017-06-29T00:56:38Z", "title": "cognicity" }, "host": "data-dev.petabencana.id", @@ -363,12 +363,12 @@ } } }, + "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:cognicity-lambda-cards-${stageVariables.stage}/invocations", "requestTemplates": { "image/png": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/png\",\n\"base64Image\": \"$input.body\"\n}", "image/jpeg": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/jpeg\",\n\"base64Image\": \"$input.body\"\n}", "image/gif": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/gif\",\n\"base64Image\": \"$input.body\"\n}" }, - "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:cognicity-lambda-cards/invocations", "passthroughBehavior": "when_no_templates", "httpMethod": "POST", "contentHandling": "CONVERT_TO_TEXT", @@ -456,6 +456,11 @@ "uri": "https://${stageVariables.env}.petabencana.id/cities", "passthroughBehavior": "when_no_match", "httpMethod": "GET", + "cacheNamespace": "h1la88", + "cacheKeyParameters": [ + "method.request.querystring.format", + "method.request.querystring.geoformat" + ], "type": "http_proxy" } }, @@ -830,7 +835,8 @@ "/floods": { "get": { "produces": [ - "application/json" + "application/json", + "text/xml" ], "parameters": [ { @@ -867,6 +873,9 @@ "headers": { "Access-Control-Allow-Origin": { "type": "string" + }, + "Content-Type": { + "type": "string" } } } @@ -876,6 +885,7 @@ "default": { "statusCode": "200", "responseParameters": { + "method.response.header.Content-Type": "integration.response.header.Content-Type", "method.response.header.Access-Control-Allow-Origin": "'*'" } } @@ -889,9 +899,44 @@ }, "passthroughBehavior": "when_no_match", "httpMethod": "GET", + "cacheNamespace": "nvy9s5", + "cacheKeyParameters": [ + "method.request.querystring.city", + "method.request.querystring.format", + "method.request.querystring.geoformat", + "method.request.querystring.minimum_state" + ], "type": "http" } }, + "head": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/Empty" + } + } + }, + "x-amazon-apigateway-integration": { + "responses": { + "default": { + "statusCode": "200" + } + }, + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "passthroughBehavior": "when_no_match", + "type": "mock" + } + }, "options": { "consumes": [ "application/json" @@ -992,6 +1037,12 @@ }, "passthroughBehavior": "when_no_match", "httpMethod": "GET", + "cacheNamespace": "cxp4qo", + "cacheKeyParameters": [ + "method.request.querystring.city", + "method.request.querystring.format", + "method.request.querystring.minimum_state" + ], "type": "http" } }, @@ -1283,7 +1334,7 @@ "type": "string" }, { - "name": "geoformat", + "name": "timeperiod", "in": "query", "required": false, "type": "string" @@ -1293,6 +1344,12 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "geoformat", + "in": "query", + "required": false, + "type": "string" } ], "responses": { @@ -1321,10 +1378,18 @@ "requestParameters": { "integration.request.querystring.city": "method.request.querystring.city", "integration.request.querystring.geoformat": "method.request.querystring.geoformat", + "integration.request.querystring.timeperiod": "method.request.querystring.timeperiod", "integration.request.querystring.format": "method.request.querystring.format" }, "passthroughBehavior": "when_no_match", "httpMethod": "GET", + "cacheNamespace": "8vxlp8", + "cacheKeyParameters": [ + "method.request.querystring.city", + "method.request.querystring.geoformat", + "method.request.querystring.timeperiod", + "method.request.querystring.format" + ], "type": "http" } }, @@ -1451,12 +1516,288 @@ "httpMethod": "ANY", "cacheNamespace": "5tep32", "cacheKeyParameters": [ - "method.request.path.id" + "method.request.path.id", + "method.request.querystring.format" ], "type": "http_proxy" } } }, + "/stats": { + "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" + } + } + } + }, + "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,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + } + } + }, + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "passthroughBehavior": "when_no_match", + "type": "mock" + } + } + }, + "/stats/floodedRWsSummary": { + "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-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:cognicity-statistics-${stageVariables.stage}-floodedRWsSummary/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" + } + } + } + }, + "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": "'*'" + } + } + }, + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "passthroughBehavior": "when_no_match", + "type": "mock" + } + } + }, + "/stats/floodedRegionsSummary": { + "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-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-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" + } + } + } + }, + "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": "'*'" + } + } + }, + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "passthroughBehavior": "when_no_match", + "type": "mock" + } + } + }, + "/stats/reportsSummary": { + "get": { + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/Empty" + } + } + }, + "x-amazon-apigateway-integration": { + "responses": { + "default": { + "statusCode": "200" + } + }, + "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:cognicity-statistics-${stageVariables.stage}-reportsSummary/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" + } + } + } + }, + "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,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'", + "method.response.header.Access-Control-Allow-Origin": "'*'" + } + } + }, + "requestTemplates": { + "application/json": "{\"statusCode\": 200}" + }, + "passthroughBehavior": "when_no_match", + "type": "mock" + } + } + }, "/twilio": { "post": { "consumes": [ @@ -1490,10 +1831,10 @@ } } }, + "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:twilio/invocations", "requestTemplates": { "application/x-www-form-urlencoded": "{\n \"reqbody\":\"$input.path('$')\"\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", "contentHandling": "CONVERT_TO_TEXT", @@ -1567,4 +1908,4 @@ "image/jpeg", "image/gif" ] -} +} \ No newline at end of file From 556951394686e3f3511522be5edd54f6bbf66851 Mon Sep 17 00:00:00 2001 From: Matthew Berryman Date: Thu, 29 Jun 2017 11:05:35 +1000 Subject: [PATCH 095/160] latest API (post change of image upload) --- swagger-api.json | 94 +++++------------------------------------------- 1 file changed, 9 insertions(+), 85 deletions(-) diff --git a/swagger-api.json b/swagger-api.json index 6fab5e4..8fd2049 100644 --- a/swagger-api.json +++ b/swagger-api.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "2017-06-29T00:56:38Z", + "version": "2017-06-29T01:04:57Z", "title": "cognicity" }, "host": "data-dev.petabencana.id", @@ -256,12 +256,7 @@ } }, "/cards/{cardId}/images": { - "post": { - "consumes": [ - "image/jpeg", - "image/png", - "image/gif" - ], + "get": { "produces": [ "application/json" ], @@ -284,38 +279,6 @@ "type": "string" } } - }, - "400": { - "description": "400 response", - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } - }, - "404": { - "description": "404 response", - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } - }, - "409": { - "description": "409 response", - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } - }, - "500": { - "description": "500 response", - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - } - } } }, "x-amazon-apigateway-integration": { @@ -325,54 +288,15 @@ "responseParameters": { "method.response.header.Access-Control-Allow-Origin": "'*'" } - }, - ".*\\\"statusCode\\\":400.*": { - "statusCode": "400", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - }, - "responseTemplates": { - "application/json": "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n{\n \"statusCode\" : \"$errorMessageObj.statusCode\",\n \"message\" : \"$errorMessageObj.message\"\n}" - } - }, - ".*\\\"statusCode\\\":404.*": { - "statusCode": "404", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - }, - "responseTemplates": { - "application/json": "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n{\n \"statusCode\" : \"$errorMessageObj.statusCode\",\n \"message\" : \"$errorMessageObj.message\"\n}" - } - }, - ".*\\\"statusCode\\\":409.*": { - "statusCode": "409", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - }, - "responseTemplates": { - "application/json": "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n{\n \"statusCode\" : \"$errorMessageObj.statusCode\",\n \"message\" : \"$errorMessageObj.message\"\n}" - } - }, - ".*\\\"statusCode\\\":50*.*": { - "statusCode": "500", - "responseParameters": { - "method.response.header.Access-Control-Allow-Origin": "'*'" - }, - "responseTemplates": { - "application/json": "#set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage')))\n{\n \"statusCode\" : \"$errorMessageObj.statusCode\",\n \"message\" : \"$errorMessageObj.message\"\n}" - } } }, - "uri": "arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:917524458155:function:cognicity-lambda-cards-${stageVariables.stage}/invocations", - "requestTemplates": { - "image/png": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/png\",\n\"base64Image\": \"$input.body\"\n}", - "image/jpeg": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/jpeg\",\n\"base64Image\": \"$input.body\"\n}", - "image/gif": "{\n\"cardId\": \"$input.params('cardId')\",\n\"contentType\": \"image/gif\",\n\"base64Image\": \"$input.body\"\n}" + "uri": "https://${stageVariables.env}.petabencana.id/cards/{cardId}/images", + "requestParameters": { + "integration.request.path.cardId": "method.request.path.cardId" }, - "passthroughBehavior": "when_no_templates", - "httpMethod": "POST", - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws" + "passthroughBehavior": "when_no_match", + "httpMethod": "GET", + "type": "http" } }, "options": { @@ -406,7 +330,7 @@ "default": { "statusCode": "200", "responseParameters": { - "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'", + "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": "'*'" } From e23d9d8a2d1127d28e136aabec5f3df28e4e96b8 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 21:05:48 -0400 Subject: [PATCH 096/160] Added JSDoc comments --- src/test/testFloods.js | 18 +++++++++++++++--- src/test/testInfrastructure.js | 13 ++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/test/testFloods.js b/src/test/testFloods.js index 81f8183..9b160da 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -1,5 +1,17 @@ -const test = require('unit.js'); +/** + * testFloods module + * @module test/testFloods + * A module to test the /floods endpoint + */ +import * as test from 'unit.js'; + +/** + * Test infrastructure endpoint + * @alias module:test/testFloods + * @param {Object} app - CogniCity server app object + * @param {Object} jwt - Sample JSON Web Token for testing endpoint auth + */ export default function(app, jwt) { // Floods endpoint describe('Flood areas endpoint', function() { @@ -122,7 +134,7 @@ describe('Flood areas endpoint', function() { }); // Get geographic floods - it('Get floods in geojson (GET /floods?format=json&geoformat=geojson)', function(done) { + it('Get floods in geojson format', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods/?format=json&geoformat=geojson') @@ -138,7 +150,7 @@ describe('Flood areas endpoint', function() { }); // Can get reports in CAP format - it('Get all reports in CAP format (GET /floods?geoformat=cap)', function(done) { + it('Get all reports in CAP format', function(done) { this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') diff --git a/src/test/testInfrastructure.js b/src/test/testInfrastructure.js index ad83a74..d112de3 100644 --- a/src/test/testInfrastructure.js +++ b/src/test/testInfrastructure.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testInfrastructure module + * @module test/testInfrastructure + * A module to test the /infrastructure endpoint + */ +import * as test from 'unit.js'; + +/** + * Test infrastructure endpoint + * @alias module:test/testInfrastructure + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Infrastructure endpoint describe('Infrastructure endpoint', function() { From f4f1237013303a91d84d38932801637c561bee84 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Jun 2017 21:42:06 -0400 Subject: [PATCH 097/160] removed line breaks in long url queries --- src/test/testReportsArchive.js | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index 6954b3b..02d23cf 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -21,8 +21,7 @@ export default function(app) { // Can get reports between given timestamps it('Can get reports between given timestamps', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -36,10 +35,7 @@ export default function(app) { it('Can get reports between given timestamps as geojson', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-06-07T00:00:00%2B0700& \ - end=2017-06-08T23:00:00%2B0700& \ - format=json&geoformat=geojson') + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -54,10 +50,7 @@ export default function(app) { it('Can get reports between timestamps as topojson', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-06-07T00:00:00%2B0700& \ - end=2017-06-08T23:00:00%2B0700& \ - format=json&geoformat=topojson') + .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=topojson') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { @@ -73,8 +66,7 @@ export default function(app) { // Can catch no start parameter it('Required start parameter by default', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - end=2017-02-22T07:00:00%2B0700') + .get('/reports/archive?end=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -89,8 +81,7 @@ export default function(app) { // Can catch no end parameter it('Required end parameter by default', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-02-22T07:00:00%2B0700') + .get('/reports/archive?start=2017-02-22T07:00:00%2B0700') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -105,9 +96,7 @@ export default function(app) { // Can catch no UTC offset in end parameter it('Required end parameter to have a UTC offset', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-02-21T07:00:00%2B0700& \ - end=2017-02-22T07:00:00') + .get('/reports/archive?start=2017-02-21T07:00:00%2B0700&end=2017-02-22T07:00:00') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -122,8 +111,7 @@ export default function(app) { // Can catch no UTC offset in start parameter it('Required start parameter to have a UTC offset', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-02-21T07:00:00') + .get('/reports/archive?start=2017-02-21T07:00:00') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { @@ -138,8 +126,7 @@ export default function(app) { // Can catch no UTC offset in start parameter it('Catches badly formed time stamp', function(done) { test.httpAgent(app) - .get('/reports/archive? \ - start=2017-02-21') + .get('/reports/archive?start=2017-02-21') .expect(400) .expect('Content-Type', /json/) .end(function(err, res) { From dd4136d7d03f64c58c1e28f0f2fd19a18a2c4205 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 10:19:48 -0400 Subject: [PATCH 098/160] disabled eslint max-len for long query strings --- src/test/testReportsArchive.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/testReportsArchive.js b/src/test/testReportsArchive.js index 02d23cf..4b8550a 100644 --- a/src/test/testReportsArchive.js +++ b/src/test/testReportsArchive.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ /** * testReportsArchive module * @module test/testReportsArchive From 7175c6349feac8df9923e6a867d27a52fcb82eff Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 10:21:21 -0400 Subject: [PATCH 099/160] added jsdoc comment --- src/test/testFloodgauges.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/testFloodgauges.js b/src/test/testFloodgauges.js index af68838..62aeaf2 100644 --- a/src/test/testFloodgauges.js +++ b/src/test/testFloodgauges.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testFloodsgauges module + * @module test/testFloodsgauges + * A module to test the /floodgauges endpoint + */ +import * as test from 'unit.js'; + +/** + * Test floodgauges endpoint + * @alias module:test/testFloodsgauges + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Flood gauges endpoint describe('Flood gauges endpoint', function() { From 9ee7d55a6bd8cf5d9949f7902ced6bfa08fa73da Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 10:44:36 -0400 Subject: [PATCH 100/160] Applied eslint style fixes to tests --- src/test/testCAP.js | 141 +++++++++++++++++++++++++++++++++++++---- src/test/testCards.js | 20 ++++-- src/test/testCities.js | 13 +++- src/test/testDB.js | 20 ++++-- src/test/testFeeds.js | 13 +++- 5 files changed, 181 insertions(+), 26 deletions(-) diff --git a/src/test/testCAP.js b/src/test/testCAP.js index cec73ff..9f0a613 100644 --- a/src/test/testCAP.js +++ b/src/test/testCAP.js @@ -1,18 +1,44 @@ -// Unit.js -const test = require('unit.js'); +/** + * testCAP module + * @module test/testCAP + * A module to test the CAP data format utility + */ -// Cap formatter helper -import Cap from '../lib/cap'; +import * as test from 'unit.js'; // Unit testing module +import Cap from '../lib/cap'; // Cap formatter helper + +/** + * Test CAP data format utility + * @alias module:test/testCAP + * @param {Object} logger - CogniCity Server logger object + */ export default function(logger) { describe('CAP Utility', function() { const cap = new Cap(logger); // Setup our cap formatter // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': + {'type': 'Polygon', + 'coordinates': [ + [[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]}, + 'properties': + {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 4, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; // Test geometry string creation - it('Can create suitable polygon strings from geojson features', function(done) { + it('Can create polygon strings from geojson features', function(done) { cap.createArea(feature); let result = cap.createArea(feature); test.value(typeof(result.polygon[0])).is('string'); @@ -20,7 +46,24 @@ export default function(logger) { }); // dummy data (multipolygon) - feature = {'type': 'Feature', 'geometry': {'type': 'MultiPolygon', 'coordinates': [[[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + feature = {'type': 'Feature', + 'geometry': + {'type': 'MultiPolygon', + 'coordinates': [ + [[[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]]}, + 'properties': + {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 4, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; // Test multigeometry string creation it('Will operate on multipolygon objects', function(done) { @@ -32,7 +75,24 @@ export default function(logger) { // Test level severe it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 4, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': + {'type': 'Polygon', + 'coordinates': [ + [[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]}, + 'properties': + {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 4, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; let result = cap.createInfo(feature); test.value(result.severity).is('Severe'); @@ -42,7 +102,22 @@ export default function(logger) { // Test level moderate it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 3, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': + {'type': 'Polygon', 'coordinates': [ + [[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]}, + 'properties': {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 3, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; let result = cap.createInfo(feature); test.value(result.severity).is('Moderate'); @@ -52,7 +127,21 @@ export default function(logger) { // Test level minor it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 2, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': {'type': 'Polygon', 'coordinates': [ + [[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]}, + 'properties': {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 2, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; let result = cap.createInfo(feature); test.value(result.severity).is('Minor'); @@ -62,7 +151,21 @@ export default function(logger) { // Test level Unknown it('Create the correct severity levels', function(done) { // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[106.8367359996, -6.2305420003], [106.8367039999, -6.2307239997], [106.8367995401, -6.2301399095], [106.8367359996, -6.2305420003]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 1, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': {'type': 'Polygon', 'coordinates': [ + [[106.8367359996, -6.2305420003], + [106.8367039999, -6.2307239997], + [106.8367995401, -6.2301399095], + [106.8367359996, -6.2305420003]]]}, + 'properties': {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 1, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; let result = cap.createInfo(feature); test.value(result.severity).is('Unknown'); @@ -70,9 +173,21 @@ export default function(logger) { }); // Test bad geometry will bubble up internall within CAP module - it('Test unsupported complex polygon geometry (interior rings)', function(done) { + it('Catch unsupported complex polygon geometry', function(done) { // dummy data (polygon) - let feature = {'type': 'Feature', 'geometry': {'type': 'Polygon', 'coordinates': [[[0, 0], [3, 6], [6, 1], [0, 0]], [[2, 2], [3, 3], [4, 2], [2, 2]]]}, 'properties': {'area_id': '1346', 'geom_id': '3171100002004000', 'area_name': 'RW 04', 'parent_name': 'KUNINGAN TIMUR', 'city_name': 'Jakarta', 'state': 1, 'last_updated': '2017-03-31T02:45:52.574Z'}}; + let feature = {'type': 'Feature', + 'geometry': {'type': 'Polygon', 'coordinates': [ + [[0, 0], [3, 6], [6, 1], [0, 0]], + [[2, 2], [3, 3], [4, 2], [2, 2]]]}, + 'properties': {'area_id': '1346', + 'geom_id': '3171100002004000', + 'area_name': 'RW 04', + 'parent_name': 'KUNINGAN TIMUR', + 'city_name': 'Jakarta', + 'state': 1, + 'last_updated': '2017-03-31T02:45:52.574Z', + }, + }; let result = cap.createInfo(feature); test.value(result).is(undefined); diff --git a/src/test/testCards.js b/src/test/testCards.js index 70df9db..7b3934f 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testCards module + * @module test/testCards + * A module to test the /cards endpoint + */ +import * as test from 'unit.js'; + +/** + * Test cards endpoint + * @alias module:test/testCards + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Cards endpoint describe('Cards endpoint', function() { @@ -19,7 +30,7 @@ export default function(app) { }); // Can get reports - it('Return 400 if card requested with wrong ID (GET /cards/:id)', function(done) { + it('Return 400 if card ID is invalid (GET /cards/:id)', function(done) { test.httpAgent(app) .get('/cards/1') .expect(400) @@ -97,7 +108,6 @@ export default function(app) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); } else { - console.log(res.body); done(); } }); @@ -113,7 +123,6 @@ export default function(app) { if (err) { test.fail(err.message + ' ' + JSON.stringify(res)); } else { - console.log(res.body); done(); } }); @@ -133,7 +142,8 @@ export default function(app) { test.value(res.body.result.username).is('testuser'); test.value(res.body.result.network).is('test network'); test.value(res.body.result.language).is('en'); - test.value(res.body.result.report.text).is('integration testing'); + test.value(res.body.result.report.text) + .is('integration testing'); done(); } }); diff --git a/src/test/testCities.js b/src/test/testCities.js index c312119..cecfa7b 100644 --- a/src/test/testCities.js +++ b/src/test/testCities.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testCities module + * @module test/testCities + * A module to test the /cities endpoint + */ +import * as test from 'unit.js'; + +/** + * Test cities endpoint + * @alias module:test/testCities + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Cities endpoint describe('Cities endpoint', function() { diff --git a/src/test/testDB.js b/src/test/testDB.js index a311951..abed9ba 100644 --- a/src/test/testDB.js +++ b/src/test/testDB.js @@ -1,19 +1,27 @@ -// Import Unit.js -const test = require('unit.js'); +/* eslint-disable no-console */ +/** + * testDB module + * @module test/testDB + * A module to test the db utility module + */ -// Database utility -import initializeDb from '../db'; +import * as test from 'unit.js'; // Unit testing module +import initializeDb from '../db'; // Database utility +/** + * Test db utility module + * @alias module:test/testDB + */ export default function() { describe('Test CogniCity Server Database Module', function() { it('Catches errors on startup', function(done) { // Try and connect to the db let config = {}; let logger = {}; - logger.error =function(err) { + logger.error = function(err) { console.log(err); }; - logger.debug =function(err) { + logger.debug = function(err) { console.log(err); }; initializeDb(config, logger) diff --git a/src/test/testFeeds.js b/src/test/testFeeds.js index ece0af8..d8ca3d9 100644 --- a/src/test/testFeeds.js +++ b/src/test/testFeeds.js @@ -1,5 +1,16 @@ -const test = require('unit.js'); +/** + * testFeeds module + * @module test/testFeeds + * A module to test the /feeds endpoint + */ +import * as test from 'unit.js'; + +/** + * Test feeds endpoint + * @alias module:test/testFeeds + * @param {Object} app - CogniCity server app object + */ export default function(app) { // Feeds endpoint describe('Feeds endpoint', function() { From 6e9cc46c72a9da01493cf0f1c5e7ff742e024cb4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:12:03 -0400 Subject: [PATCH 101/160] Added JSDoc comment --- src/test/index.js | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/test/index.js b/src/test/index.js index 92f97e3..d9eb343 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -1,5 +1,9 @@ -// Testing for CogniCity Server -// Unit tests run together against live app, and database +/* eslint-disable no-console */ +/* @file Runs integration testing for CogniCity Server API. + * Unit tests are run in sequence against live app and database instance + * + * Tomas Holderness June 2017 + */ // Import config import config from '../config'; @@ -48,7 +52,9 @@ let reportid = 1; // Auth JWT support const jwt = require('jsonwebtoken'); -let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH0_CLIENT_ID}); +let token = jwt.sign({}, + new Buffer(config.AUTH0_SECRET), + {audience: config.AUTH0_CLIENT_ID}); it('Server starts', function(done) { // Test optional server params @@ -74,7 +80,9 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH describe('Cleans up', function() { it('Removes dummy report data', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE source = 'grasp' AND text = 'integration testing';`, + text: `DELETE FROM ${config.TABLE_REPORTS} + WHERE source = 'grasp' + AND text = 'integration testing';`, values: [reportid], }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { @@ -92,7 +100,9 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH it('Removes dummy cards data', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_GRASP_CARDS} WHERE username = 'testuser' AND network = 'test network';`, + text: `DELETE FROM ${config.TABLE_GRASP_CARDS} + WHERE username = 'testuser' + AND network = 'test network';`, values: [reportid], }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { @@ -110,7 +120,8 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH it('Removes dummy cards data', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_GRASP_REPORTS} WHERE text = 'integration testing';`, + text: `DELETE FROM ${config.TABLE_GRASP_REPORTS} + WHERE text = 'integration testing';`, values: [reportid], }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { @@ -147,7 +158,8 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH // Remove dummy data from REM floods table it('Removes dummy flood data from log', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_REM_STATUS_LOG} WHERE username = testing`, + text: `DELETE FROM ${config.TABLE_REM_STATUS_LOG} + WHERE username = testing`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { client.query(queryObject, function() { @@ -183,7 +195,8 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH // Remove dummy qlue data from all reports table it('Removes dummy qlue data', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'qlue'`, + text: `DELETE FROM ${config.TABLE_REPORTS} + WHERE fkey = 9999 AND source = 'qlue'`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { client.query(queryObject, function() { @@ -219,7 +232,8 @@ let token = jwt.sign({}, new Buffer(config.AUTH0_SECRET), {audience: config.AUTH // Remove dummy detik data from all reports table it('Removes dummy detik data', function(done) { let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} WHERE fkey = 9999 AND source = 'detik'`, + text: `DELETE FROM ${config.TABLE_REPORTS} + WHERE fkey = 9999 AND source = 'detik'`, }; pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { client.query(queryObject, function() { From 8328dc02d3b5ca1506031f64a1151b8efdc6ebb3 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:32:32 -0400 Subject: [PATCH 102/160] eslint fixes --- src/lib/cap.js | 404 +++++++++++++++++++++++++++---------------------- 1 file changed, 220 insertions(+), 184 deletions(-) diff --git a/src/lib/cap.js b/src/lib/cap.js index 5d50f1a..fbb9d99 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -4,198 +4,234 @@ import builder from 'xmlbuilder'; // moment module, JS date/time manipulation library import moment from 'moment-timezone'; - -/** - * CAP tranformer, transforms Peta Jakarta GeoJSON to CAP. - * @constructor - * @param {object} logger Configured Winston logger instance - */ +// Cap class module.exports = class Cap { - + /** + * Setup the CAP object to user specified logger + * @param {object} logger Configured Winston logger instance + */ constructor(logger) { this.logger = logger; } + /** + * Transform GeoJSON data to ATOM feed of CAP format XML data. + * See {@link https://tools.ietf.org/html/rfc4287|ATOM syndication format} + * @param {object} features Peta Jakarta GeoJSON features object + * @return {string} XML CAP data describing all areas + */ + geoJsonToAtomCap(features) { + let self = this; + let feed = { + '@xmlns': 'http://www.w3.org/2005/Atom', + 'id': 'https://data.petabencana.id/floods', + 'title': 'petabencana.id Flood Affected Areas', + 'updated': moment().tz('Asia/Jakarta').format(), + 'author': { + name: 'petabencana.id', + uri: 'https://petabencana.id/', + }, + }; + + for (let feature of features) { + let alert = self.createAlert( feature ); + // If alert creation failed, don't create the entry + if (!alert) { + continue; + } + + if (!feed.entry) feed.entry = []; + + feed.entry.push({ + // Note, this ID does not resolve to a real resource + // - but enough information is contained in the URL + // that we could resolve the flooded report at the same point in time + id: 'https://data.petabencana.id/floods?parent_name=' + +encodeURI(feature.properties.parent_name) + +'&area_name=' + +encodeURI(feature.properties.area_name) + +'&time=' + +encodeURI(moment.tz(feature.properties.last_updated, 'Asia/Jakarta' + ).format('YYYY-MM-DDTHH:mm:ssZ')), + title: alert.identifier + ' Flood Affected Area', + updated: moment.tz(feature.properties.last_updated, 'Asia/Jakarta' + ).format('YYYY-MM-DDTHH:mm:ssZ'), + content: { + '@type': 'text/xml', + 'alert': alert, + }, + }); + } + + return builder.create( {feed: feed} ).end(); + } - /** - * Transform from petabencana.id GeoJSON data to ATOM feed of CAP format XML data. - * See {@link https://tools.ietf.org/html/rfc4287|ATOM syndication format} - * @param {object} features Peta Jakarta GeoJSON features object - * @return {string} XML CAP data describing all areas - */ - geoJsonToAtomCap(features) { - let self = this; - let feed = { - '@xmlns': 'http://www.w3.org/2005/Atom', - 'id': 'https://data.petabencana.id/floods', - 'title': 'petabencana.id Flood Affected Areas', - 'updated': moment().tz('Asia/Jakarta').format(), - 'author': { - name: 'petabencana.id', - uri: 'https://petabencana.id/', - }, - }; - - for (let feature of features) { - let alert = self.createAlert( feature ); - // If alert creation failed, don't create the entry - if (!alert) { - continue; - } - - if (!feed.entry) feed.entry = []; - - feed.entry.push({ - // Note, this ID does not resolve to a real resource - but enough information is contained in the URL that we could resolve the flooded report at the same point in time - id: 'https://data.petabencana.id/floods?parent_name='+encodeURI(feature.properties.parent_name)+'&area_name='+encodeURI(feature.properties.area_name)+'&time='+encodeURI(moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ')), - title: alert.identifier + ' Flood Affected Area', - updated: moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'), - content: { - '@type': 'text/xml', - 'alert': alert, - }, - }); - } - - return builder.create( {feed: feed} ).end(); - } - - /** - * Create CAP ALERT object. - * See {@link http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc97699527|CAP specification 3.2.1 "alert" Element and Sub-elements} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing ALERT element for XML conversion by xmlbuilder - */ - createAlert(feature) { - let self = this; - - let alert = {}; - - alert['@xmlns'] = 'urn:oasis:names:tc:emergency:cap:1.2'; - - let identifier = feature.properties.parent_name + '.' + feature.properties.area_name + '.' + moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'); - identifier = identifier.replace(/ /g, '_'); - alert.identifier = encodeURI(identifier); - - alert.sender = 'BPBD.JAKARTA.GOV.ID'; - alert.sent = moment.tz(feature.properties.last_updated, 'Asia/Jakarta').format('YYYY-MM-DDTHH:mm:ssZ'); - alert.status = 'Actual'; - alert.msgType = 'Alert'; - alert.scope = 'Public'; - - alert.info = self.createInfo( feature ); - // If info creation failed, don't create the alert - if (!alert.info) { - return; - } - - return alert; - } - - /** - * Create a CAP INFO object. - * See {@link http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc97699542|CAP specification 3.2.2 "info" Element and Sub-elements} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing INFO element suitable for XML conversion by xmlbuilder - */ - createInfo(feature) { - let self = this; - - let info = {}; - - info.category = 'Met'; - info.event = 'FLOODING'; - info.urgency = 'Immediate'; - - let severity = ''; - let levelDescription = ''; - if ( feature.properties.state === 1 ) { - severity = 'Unknown'; - levelDescription = 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -'; - } else if ( feature.properties.state === 2 ) { - severity = 'Minor'; - levelDescription = 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS'; - } else if ( feature.properties.state === 3 ) { - severity = 'Moderate'; - levelDescription = 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS'; - } else if ( feature.properties.state === 4 ) { - severity = 'Severe'; - levelDescription = 'FLOODING OF OVER 150 CENTIMETERS'; - } else { - self.logger.silly('Cap: createInfo(): State ' + feature.properties.state + ' cannot be resolved to a severity'); - return; - } - info.severity = severity; - - info.certainty = 'Observed'; - info.senderName = 'JAKARTA EMERGENCY MANAGEMENT AGENCY'; - info.headline = 'FLOOD WARNING'; - - let descriptionTime = moment(feature.properties.last_updated).tz('Asia/Jakarta').format('HH:mm z'); - let descriptionArea = feature.properties.parent_name + ', ' + feature.properties.area_name; - info.description = 'AT ' + descriptionTime + ' THE JAKARTA EMERGENCY MANAGEMENT AGENCY OBSERVED ' + levelDescription + ' IN ' + descriptionArea + '.'; - - info.web = 'https://petabencana.id/'; - - info.area = self.createArea( feature ); - // If area creation failed, don't create the info - if (!info.area) { - return; - } - - return info; - } - - /** - * Create a CAP AREA object. - * See {@link http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc97699550|CAP specification 3.2.4 "area" Element and Sub-elements} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing AREA element suitable for XML conversion by xmlbuilder - */ - createArea(feature) { - let self = this; - - let area = {}; - - area.areaDesc = feature.properties.area_name + ', ' + feature.properties.parent_name; - - // Collate array of polygon-describing strings from different geometry types - area.polygon = []; - let featurePolygons; - if ( feature.geometry.type === 'Polygon' ) { - featurePolygons = [feature.geometry.coordinates]; - } else if ( feature.geometry.type === 'MultiPolygon' ) { - featurePolygons = feature.geometry.coordinates; - } else { + /** + * Create CAP ALERT object. + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ + CAP-v1.2-os.html#_Toc97699527| + CAP specification 3.2.1 "alert" Element and Sub-elements`} + * @param {object} feature petabencana.id GeoJSON feature + * @return {object} Object representing ALERT element for xmlbuilder + */ + createAlert(feature) { + let self = this; + + let alert = {}; + + alert['@xmlns'] = 'urn:oasis:names:tc:emergency:cap:1.2'; + + let identifier = feature.properties.parent_name + '.' + + feature.properties.area_name + '.' + + moment.tz(feature.properties.last_updated, 'Asia/Jakarta' + ).format('YYYY-MM-DDTHH:mm:ssZ'); + identifier = identifier.replace(/ /g, '_'); + alert.identifier = encodeURI(identifier); + + alert.sender = 'BPBD.JAKARTA.GOV.ID'; + alert.sent = moment.tz(feature.properties.last_updated, 'Asia/Jakarta' + ).format('YYYY-MM-DDTHH:mm:ssZ'); + alert.status = 'Actual'; + alert.msgType = 'Alert'; + alert.scope = 'Public'; + + alert.info = self.createInfo( feature ); + // If info creation failed, don't create the alert + if (!alert.info) { + return; + } + + return alert; + } + + /** + * Create a CAP INFO object. + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ + CAP-v1.2-os.html#_Toc97699542| + CAP specification 3.2.2 "info" Element and Sub-elements`} + * @param {object} feature petabencana.id GeoJSON feature + * @return {object} Object representing INFO element suitable for xmlbuilder + */ + createInfo(feature) { + let self = this; + + let info = {}; + + info.category = 'Met'; + info.event = 'FLOODING'; + info.urgency = 'Immediate'; + + let severity = ''; + let levelDescription = ''; + if ( feature.properties.state === 1 ) { + severity = 'Unknown'; + levelDescription = 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -'; + } else if ( feature.properties.state === 2 ) { + severity = 'Minor'; + levelDescription = 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS'; + } else if ( feature.properties.state === 3 ) { + severity = 'Moderate'; + levelDescription = 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS'; + } else if ( feature.properties.state === 4 ) { + severity = 'Severe'; + levelDescription = 'FLOODING OF OVER 150 CENTIMETERS'; + } else { + self.logger.silly('Cap: createInfo(): State ' + + feature.properties.state + + ' cannot be resolved to a severity'); + return; + } + info.severity = severity; + + info.certainty = 'Observed'; + info.senderName = 'JAKARTA EMERGENCY MANAGEMENT AGENCY'; + info.headline = 'FLOOD WARNING'; + + let descriptionTime = moment(feature.properties.last_updated + ).tz('Asia/Jakarta').format('HH:mm z'); + let descriptionArea = feature.properties.parent_name + + ', ' + feature.properties.area_name; + info.description = 'AT ' + + descriptionTime + + ' THE JAKARTA EMERGENCY MANAGEMENT AGENCY OBSERVED ' + + levelDescription + ' IN ' + descriptionArea + '.'; + + info.web = 'https://petabencana.id/'; + + info.area = self.createArea( feature ); + // If area creation failed, don't create the info + if (!info.area) { + return; + } + + return info; + } + + /** + * Create a CAP AREA object. + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ + CAP-v1.2-os.html#_Toc97699550| + CAP specification 3.2.4 "area" Element and Sub-elements`} + * @param {object} feature petabencana.id GeoJSON feature + * @return {object} Object representing AREA element for XML xmlbuilder + */ + createArea(feature) { + let self = this; + + let area = {}; + + area.areaDesc = feature.properties.area_name + + ', ' + feature.properties.parent_name; + + // Collate array of polygon-describing strings from different geometry types + area.polygon = []; + let featurePolygons; + if ( feature.geometry.type === 'Polygon' ) { + featurePolygons = [feature.geometry.coordinates]; + } else if ( feature.geometry.type === 'MultiPolygon' ) { + featurePolygons = feature.geometry.coordinates; + } else { /* istanbul ignore next */ - self.logger.error( 'Cap: createInfo(): Geometry type \'' + feature.geometry.type + '\' not supported' ); + self.logger.error( 'Cap: createInfo(): Geometry type \'' + + feature.geometry.type + '\' not supported' ); /* istanbul ignore next */ - return; - } - - // Construct CAP suitable polygon strings (whitespace-delimited WGS84 coordinate pairs - e.g. "lat,lon lat,lon") - // See: http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc97699550 - polygon - // See: http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html#_Toc520973440 - self.logger.debug( 'Cap: createInfo(): ' + featurePolygons.length + ' polygons detected for ' + area.areaDesc ); - for (let polygonIndex=0; polygonIndex 1 ) { + return; + } + + // Construct CAP suitable polygon strings + // (whitespace-delimited WGS84 coordinate pairs - e.g. "lat,lon lat,lon") + // See: `http://docs.oasis-open.org/emergency/cap/v1.2/ + // CAP-v1.2-os.html#_Toc97699550 - polygon` + // See: `http://docs.oasis-open.org/emergency/cap/v1.2/ + // CAP-v1.2-os.html#_Toc520973440` + self.logger.debug( 'Cap: createInfo(): ' + + featurePolygons.length + + ' polygons detected for ' + + area.areaDesc ); + for (let polygonIndex=0; polygonIndex < featurePolygons.length; + polygonIndex++) { + // Assume all geometries to be simple Polygons of single LineString + if ( featurePolygons[polygonIndex].length > 1 ) { /* istanbul ignore next */ - self.logger.error( 'Cap: createInfo(): Polygon with interior rings is not supported' ); + self.logger.error( `Cap: createInfo(): Polygon with interior rings is + not supported` ); /* istanbul ignore next */ - return; - } - - let polygon = ''; - self.logger.debug( 'Cap: createInfo(): ' + featurePolygons[polygonIndex][0].length + ' points detected in polygon ' + polygonIndex ); - for (let pointIndex=0; pointIndex Date: Thu, 29 Jun 2017 11:32:45 -0400 Subject: [PATCH 103/160] fixed long lines --- src/server.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server.js b/src/server.js index 9729be0..d483fc0 100644 --- a/src/server.js +++ b/src/server.js @@ -12,12 +12,13 @@ import responseTime from 'response-time'; import morgan from 'morgan'; // Express logging // Function to initialize the api server -const init = (config, initializeDb, routes, logger) => new Promise((resolve, reject) => { +const init = (config, initializeDb, routes, logger) => + new Promise((resolve, reject) => { // Create the server let app = express(); app.server = http.createServer(app); - // Winston stream function we can plug in to express so we can capture its logs along with our own + // Winston stream function for express so we can capture logs const winstonStream = { write: function(message) { logger.info(message.slice(0, -1)); From d19706561071e8031b1e91e376f07901f7362289 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:33:00 -0400 Subject: [PATCH 104/160] fixed long lines --- src/lib/util.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/util.js b/src/lib/util.js index e8acc11..515e6d0 100755 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -9,7 +9,8 @@ import config from '../config'; // Caching import apicache from 'apicache'; -apicache.options({debug: config.LOG_LEVEL === 'debug', statusCodes: {include: [200]}}); +apicache.options({debug: config.LOG_LEVEL === 'debug', + statusCodes: {include: [200]}}); let cache = apicache.middleware; // Cache response if enabled @@ -60,7 +61,8 @@ const handleGeoResponse = (data, req, res, next) => { return !data ? res.status(404).json({statusCode: 404, found: false, result: null}) : formatGeo(data, req.query.geoformat) - .then((formatted) => res.status(200).json({statusCode: 200, result: formatted})) + .then((formatted) => res.status(200).json({statusCode: 200, + result: formatted})) /* istanbul ignore next */ .catch((err) => { /* istanbul ignore next */ From cdd0b9ab838bb429df9ca535e55ab83d4bbcebc6 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:43:08 -0400 Subject: [PATCH 105/160] fixed long line comment strings --- src/lib/cap.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/cap.js b/src/lib/cap.js index fbb9d99..f0975ba 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -67,9 +67,9 @@ module.exports = class Cap { /** * Create CAP ALERT object. - * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ - CAP-v1.2-os.html#_Toc97699527| - CAP specification 3.2.1 "alert" Element and Sub-elements`} + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + + `CAP-v1.2-os.html#_Toc97699527|` + + `CAP specification 3.2.1 "alert" Element and Sub-elements`} * @param {object} feature petabencana.id GeoJSON feature * @return {object} Object representing ALERT element for xmlbuilder */ @@ -105,9 +105,9 @@ module.exports = class Cap { /** * Create a CAP INFO object. - * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ - CAP-v1.2-os.html#_Toc97699542| - CAP specification 3.2.2 "info" Element and Sub-elements`} + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + + `CAP-v1.2-os.html#_Toc97699542|` + + `CAP specification 3.2.2 "info" Element and Sub-elements`} * @param {object} feature petabencana.id GeoJSON feature * @return {object} Object representing INFO element suitable for xmlbuilder */ @@ -168,9 +168,9 @@ module.exports = class Cap { /** * Create a CAP AREA object. - * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/ - CAP-v1.2-os.html#_Toc97699550| - CAP specification 3.2.4 "area" Element and Sub-elements`} + * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + + `CAP-v1.2-os.html#_Toc97699550|` + + `CAP specification 3.2.4 "area" Element and Sub-elements`} * @param {object} feature petabencana.id GeoJSON feature * @return {object} Object representing AREA element for XML xmlbuilder */ @@ -199,10 +199,10 @@ module.exports = class Cap { // Construct CAP suitable polygon strings // (whitespace-delimited WGS84 coordinate pairs - e.g. "lat,lon lat,lon") - // See: `http://docs.oasis-open.org/emergency/cap/v1.2/ - // CAP-v1.2-os.html#_Toc97699550 - polygon` - // See: `http://docs.oasis-open.org/emergency/cap/v1.2/ - // CAP-v1.2-os.html#_Toc520973440` + // See: `http://docs.oasis-open.org/emergency/cap/v1.2/` + // + `CAP-v1.2-os.html#_Toc97699550 - polygon` + // See: `http://docs.oasis-open.org/emergency/cap/v1.2/` + // + `CAP-v1.2-os.html#_Toc520973440` self.logger.debug( 'Cap: createInfo(): ' + featurePolygons.length + ' polygons detected for ' From da2d02b208a971dba124531da2e5fff19a59d39f Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:44:15 -0400 Subject: [PATCH 106/160] fixed long line logger strings --- src/index.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index b0afa26..d89ab52 100755 --- a/src/index.js +++ b/src/index.js @@ -25,10 +25,12 @@ try { if (config.LOG_DIR !== '') { fs.accessSync(config.LOG_DIR, fs.W_OK); } - logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR : 'current working directory' }`); + logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR : + 'current working directory' }`); } catch (e) { - // If we cannot write to the desired directory then log in the current directory - logger.info(`Cannot log to '${config.LOG_DIR}', logging to current working directory instead`); + // If we cannot write to the desired directory then log tocurrent directory + logger.info(`Cannot log to '${config.LOG_DIR}', + logging to current working directory instead`); config.LOG_DIR = ''; } @@ -41,12 +43,12 @@ logger.add(logger.transports.File, { level: config.LOG_LEVEL, // Level of log messages }); -// If we are not in development and console logging has not been requested then remove it +// If we are not in development and console logging not requested then remove it if (config.NODE_ENV !== 'development' && !config.LOG_CONSOLE) { logger.remove(logger.transports.Console); } -// If we exit immediately winston does not get a chance to write the last log message +// If we exit immediately winston does not get chance to write last log message const exitWithStatus = (status) => { logger.info(`Exiting with status ${status}`); setTimeout(() => process.exit(status), 500); @@ -67,7 +69,8 @@ process init(config, initializeDb, routes, logger).then((app) => { // All good to go, start listening for requests app.server.listen(config.PORT); - logger.info(`Application started, listening on port ${app.server.address().port}`); + logger.info(`Application started,` + + `listening on port ${app.server.address().port}`); }).catch((err) => { // Error has occurred, log and shutdown logger.error('Error starting server: ' + err.message + ', ' + err.stack); From 126abb5edf95f6721eba4b819a40441d089d9cd1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:44:31 -0400 Subject: [PATCH 107/160] disabled eslint max-len check --- src/config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.js b/src/config.js index d3bc50a..5a20257 100644 --- a/src/config.js +++ b/src/config.js @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ require('dotenv').config({silent: true}); export default { From 9da38fae18dbb76cbed419e5b88cb99b450f5c48 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:44:53 -0400 Subject: [PATCH 108/160] fixed long query lines --- src/api/routes/reports/model.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 846fa1b..57e8522 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -6,12 +6,13 @@ export default (config, db, logger) => ({ * Return all reports within a defined time period, and optionally city * @param {integer} timeperiod Length of time period in seconds * @param {string} city Optional, instance region code (e.g. 'jbd') + * @return {Object} Query results */ all: (timeperiod, city) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, - status, url, image_url, disaster_type, report_data, tags, title, text, the_geom - FROM ${config.TABLE_REPORTS} + status, url, image_url, disaster_type, report_data, tags, title, text, + the_geom FROM ${config.TABLE_REPORTS} WHERE created_at >= to_timestamp($1) AND ($2 IS NULL OR tags->>'instance_region_code'=$2) ORDER BY created_at DESC LIMIT $3`; @@ -35,8 +36,8 @@ export default (config, db, logger) => ({ byId: (id) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, - status, url, image_url, disaster_type, report_data, tags, title, text, the_geom - FROM ${config.TABLE_REPORTS} + status, url, image_url, disaster_type, report_data, tags, title, text, + the_geom FROM ${config.TABLE_REPORTS} WHERE pkey = $1`; // Setup values From ac9e5f04902da0366f13897c286e2b67c3b27712 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:45:12 -0400 Subject: [PATCH 109/160] fixed long Joi validation lines --- src/api/routes/reports/index.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index 802ea95..ff961a5 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -13,19 +13,23 @@ import Joi from 'joi'; import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Get a list of all reports api.get('/', cacheResponse('1 minute'), validate({ query: { city: Joi.any().valid(config.REGION_CODES), - timeperiod: Joi.number().integer().positive().max(config.API_REPORTS_TIME_WINDOW_MAX).default(config.API_REPORTS_TIME_WINDOW), + timeperiod: Joi.number().integer().positive() + .max(config.API_REPORTS_TIME_WINDOW_MAX) + .default(config.API_REPORTS_TIME_WINDOW), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), - (req, res, next) => reports(config, db, logger).all(req.query.timeperiod, req.query.city) + (req, res, next) => reports(config, db, logger) + .all(req.query.timeperiod, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { /* istanbul ignore next */ @@ -43,8 +47,10 @@ export default ({config, db, logger}) => { validate({ params: {id: Joi.number().integer().required()}, query: { - format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + format: Joi.any().valid(config.FORMATS) + .default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), (req, res, next) => reports(config, db, logger).byId(req.params.id) From d1e78217147b82b3eecaf2ed5fc76873a05012a2 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:48:08 -0400 Subject: [PATCH 110/160] fixed JSDoc comments --- src/api/routes/reports/archive/model.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js index 0f59046..c0aa34e 100644 --- a/src/api/routes/reports/archive/model.js +++ b/src/api/routes/reports/archive/model.js @@ -1,17 +1,18 @@ import Promise from 'bluebird'; export default (config, db, logger) => ({ - /** * Return all reports within a defined time period, and optionally city - * @param {integer} timeperiod Length of time period in seconds - * @param {string} city Optional, instance region code (e.g. 'jbd') + * @param {integer} start Timestamp as ISO 8601 string for start of window + * @param {string} end Timestamp as ISO 8601 string for end of window + * @param {string} city Optional, instance region code (e.g. 'jbd')\ + * @return {Object} Query result */ all: (start, end, city) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, - status, url, image_url, disaster_type, report_data, tags, title, text, the_geom - FROM ${config.TABLE_REPORTS} + status, url, image_url, disaster_type, report_data, tags, title, text, + the_geom FROM ${config.TABLE_REPORTS} WHERE created_at >= $1::timestamp with time zone AND created_at <= $2::timestamp with time zone AND ($3 IS NULL OR tags->>'instance_region_code'=$3) From b705c664c686cebd932adcce5dc266d3b9994808 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:49:20 -0400 Subject: [PATCH 111/160] fixed long lines --- src/api/routes/reports/archive/index.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index 4407eda..c53ce89 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -14,7 +14,7 @@ const Joi = BaseJoi.extend(Extension); import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Get a list of all reports api.get('/', cacheResponse('1 minute'), @@ -22,15 +22,19 @@ export default ({config, db, logger}) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), - // TODO - does it matter than end time can be "before" start time? Can reference arguments using Joi.ref('start') + // TODO - does it matter than end time can be "before" start time? + // - Can reference arguments using Joi.ref('start') start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(), - // TODO we should restrict output to geo/topojson only. CAP format doesn't make sense for historic data. + // TODO we should restrict output to geo/topojson only. + // - CAP format doesn't make sense for historic data. format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), - (req, res, next) => archive(config, db, logger).all(req.query.start, req.query.end, req.query.city) + (req, res, next) => archive(config, db, logger) + .all(req.query.start, req.query.end, req.query.city) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { /* istanbul ignore next */ From 3aa585a12685ecf9455ba25f1401a381e3a79b63 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:55:10 -0400 Subject: [PATCH 112/160] fixed long lines --- src/api/routes/floods/index.js | 45 ++++++++++----- src/api/routes/floods/model.js | 79 +++++++++++++------------- src/api/routes/infrastructure/index.js | 8 ++- 3 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index 3ab63c1..da62b84 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -45,7 +45,7 @@ const clearCache = () => { }; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap const cap = new Cap(logger); // Setup our cap formatter // Get a list of all floods @@ -53,27 +53,40 @@ export default ({config, db, logger}) => { validate({ query: { city: Joi.any().valid(config.REGION_CODES), - format: Joi.any().valid(['xml'].concat(config.FORMATS)).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(['cap'].concat(config.GEO_FORMATS)).default(config.GEO_FORMAT_DEFAULT), + format: Joi.any().valid(['xml'].concat(config.FORMATS)) + .default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(['cap'].concat(config.GEO_FORMATS)) + .default(config.GEO_FORMAT_DEFAULT), minimum_state: Joi.number().integer().valid(Object.keys(REM_STATES)), }, }), (req, res, next) => { req.apicacheGroup = CACHE_GROUP_FLOODS; - if (req.query.geoformat === 'cap' && req.query.format !== 'xml') res.status(400).json({statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\''}); - else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') res.status(400).json({statusCode: 400, message: 'format must be \'json\' when geoformat IN (\'geojson\',\'topojson\')'}); + if (req.query.geoformat === 'cap' && req.query.format !== 'xml') { + res.status(400).json({statusCode: 400, + message: 'format must be \'xml\' when geoformat=\'cap\''}); + } + else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 + && req.query.format !== 'json') { + res.status(400).json({statusCode: 400, + message: 'format must be \'json\' when geoformat ' + +'IN (\'geojson\',\'topojson\')'}); + } else { floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? - // If CAP format has been required first convert to geojson then to CAP + // If CAP format has been required convert to geojson then to CAP formatGeo(data, 'geojson') - .then((formatted) => res.status(200).set('Content-Type', 'text/xml').send(cap.geoJsonToAtomCap(formatted.features))) + .then((formatted) => res.status(200) + .set('Content-Type', 'text/xml') + .send(cap.geoJsonToAtomCap(formatted.features))) /* istanbul ignore next */ .catch((err) => next(err)) : // Otherwise hand off to geo formatter formatGeo(data, req.query.geoformat) - .then((formatted) => res.status(200).json({statusCode: 200, result: formatted})) + .then((formatted) => res.status(200) + .json({statusCode: 200, result: formatted})) /* istanbul ignore next */ .catch((err) => next(err)) ) @@ -114,16 +127,20 @@ floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) validate({ params: {localAreaId: Joi.number().integer().required()}, body: Joi.object().keys({ - state: Joi.number().integer().valid(Object.keys(REM_STATES).map((state) => parseInt(state))).required(), + state: Joi.number().integer() + .valid(Object.keys(REM_STATES).map((state) => parseInt(state))) + .required(), }), query: { username: Joi.string().required(), }, }), - (req, res, next) => floods(config, db, logger).updateREMState(req.params.localAreaId, req.body.state, req.query.username) + (req, res, next) => floods(config, db, logger) + .updateREMState(req.params.localAreaId, req.body.state, req.query.username) .then(() => { clearCache(); - res.status(200).json({localAreaId: req.params.localAreaId, state: req.body.state, updated: true}); + res.status(200).json({localAreaId: req.params.localAreaId, + state: req.body.state, updated: true}); }) /* istanbul ignore next */ .catch((err) => { @@ -142,10 +159,12 @@ floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) username: Joi.string().required(), }, }), - (req, res, next) => floods(config, db, logger).clearREMState(req.params.localAreaId, req.query.username) + (req, res, next) => floods(config, db, logger) + .clearREMState(req.params.localAreaId, req.query.username) .then(() => { clearCache(); - res.status(200).json({localAreaId: req.params.localAreaId, state: null, updated: true}); + res.status(200).json({localAreaId: req.params.localAreaId, + state: null, updated: true}); }) /* istanbul ignore next */ .catch((err) => { diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index a73db1d..ae7f9ac 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -29,8 +29,8 @@ export default (config, db, logger) => ({ // Get all flood reports for a given city allGeo: (city, minimum_state) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT la.the_geom, la.pkey as area_id, la.geom_id, la.area_name, - la.parent_name, la.city_name, rs.state, rs.last_updated + let query = `SELECT la.the_geom, la.pkey as area_id, la.geom_id, + la.area_name, la.parent_name, la.city_name, rs.state, rs.last_updated FROM ${config.TABLE_LOCAL_AREAS} la ${minimum_state ? 'JOIN' : 'LEFT JOIN'} (SELECT local_area, state, last_updated FROM ${config.TABLE_REM_STATUS} @@ -55,44 +55,45 @@ export default (config, db, logger) => ({ }), // Update the REM state and append to the log - updateREMState: (localAreaId, state, username) => new Promise((resolve, reject) => { - // Setup a timestamp with current date/time in ISO format - let timestamp = (new Date()).toISOString(); - - // Setup our queries - let queries = [ - { - query: `INSERT INTO ${config.TABLE_REM_STATUS} - ( local_area, state, last_updated ) - VALUES ( $1, $2, $3 ) - ON CONFLICT (local_area) DO - UPDATE SET state=$2, last_updated=$3`, - values: [localAreaId, state, timestamp], - }, - { - query: `INSERT INTO ${config.TABLE_REM_STATUS_LOG} - ( local_area, state, changed, username ) - VALUES ( $1, $2, $3, $4 )`, - values: [localAreaId, state, timestamp, username], - }, - ]; - - // Log queries to debugger - for (let query of queries) logger.debug(query.query, query.values); - - // Execute in a transaction as both INSERT and UPDATE must happen together - db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))); - }).timeout(config.PGTIMEOUT) - .then((data) => { - resolve(data); - }) - /* istanbul ignore next */ - .catch((err) => { + updateREMState: (localAreaId, state, username) => + new Promise((resolve, reject) => { + // Setup a timestamp with current date/time in ISO format + let timestamp = (new Date()).toISOString(); + + // Setup our queries + let queries = [ + { + query: `INSERT INTO ${config.TABLE_REM_STATUS} + ( local_area, state, last_updated ) + VALUES ( $1, $2, $3 ) + ON CONFLICT (local_area) DO + UPDATE SET state=$2, last_updated=$3`, + values: [localAreaId, state, timestamp], + }, + { + query: `INSERT INTO ${config.TABLE_REM_STATUS_LOG} + ( local_area, state, changed, username ) + VALUES ( $1, $2, $3, $4 )`, + values: [localAreaId, state, timestamp, username], + }, + ]; + + // Log queries to debugger + for (let query of queries) logger.debug(query.query, query.values); + + // Execute in a transaction as both INSERT and UPDATE must happen together + db.tx((t) => { + return t.batch(queries.map((query) => t.none(query.query, query.values))); + }).timeout(config.PGTIMEOUT) + .then((data) => { + resolve(data); + }) /* istanbul ignore next */ - reject(err); - }); - }), + .catch((err) => { + /* istanbul ignore next */ + reject(err); + }); + }), // Remove the REM state record and append to the log clearREMState: (localAreaId, username) => new Promise((resolve, reject) => { diff --git a/src/api/routes/infrastructure/index.js b/src/api/routes/infrastructure/index.js index 9a555b6..ea1df68 100644 --- a/src/api/routes/infrastructure/index.js +++ b/src/api/routes/infrastructure/index.js @@ -12,7 +12,7 @@ import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Get a list of infrastructure by type for a given city api.get('/:type', cacheResponse(config.CACHE_DURATION_INFRASTRUCTURE), @@ -21,10 +21,12 @@ export default ({config, db, logger}) => { query: { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), - (req, res, next) => infrastructure(config, db, logger).all(req.query.city, req.params.type) + (req, res, next) => infrastructure(config, db, logger) + .all(req.query.city, req.params.type) .then((data) => handleGeoResponse(data, req, res, next)) .catch((err) => { /* istanbul ignore next */ From 3a6e1c45c26434673dba38e1c007e7020a62595e Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 11:55:43 -0400 Subject: [PATCH 113/160] fixed long line --- src/api/routes/floods/model.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index ae7f9ac..813810d 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -83,7 +83,8 @@ export default (config, db, logger) => ({ // Execute in a transaction as both INSERT and UPDATE must happen together db.tx((t) => { - return t.batch(queries.map((query) => t.none(query.query, query.values))); + return t.batch(queries.map((query) => + t.none(query.query, query.values))); }).timeout(config.PGTIMEOUT) .then((data) => { resolve(data); From cd16169551c0b616d56f91782174e435ff0685f1 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:00:08 -0400 Subject: [PATCH 114/160] fixed eslint braces issues --- src/api/routes/floods/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/routes/floods/index.js b/src/api/routes/floods/index.js index da62b84..b0b21f4 100644 --- a/src/api/routes/floods/index.js +++ b/src/api/routes/floods/index.js @@ -65,14 +65,12 @@ export default ({config, db, logger}) => { if (req.query.geoformat === 'cap' && req.query.format !== 'xml') { res.status(400).json({statusCode: 400, message: 'format must be \'xml\' when geoformat=\'cap\''}); - } - else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 + } else if (config.GEO_FORMATS.indexOf(req.query.geoformat) > -1 && req.query.format !== 'json') { res.status(400).json({statusCode: 400, message: 'format must be \'json\' when geoformat ' +'IN (\'geojson\',\'topojson\')'}); - } - else { + } else { floods(config, db, logger).allGeo(req.query.city, req.query.minimum_state) .then((data) => req.query.geoformat === 'cap' ? From dff4e7ab74ece4c2a666894577b745f7881ee8b9 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:03:59 -0400 Subject: [PATCH 115/160] fixed eslint issues --- src/api/routes/cities/index.js | 8 +++++--- src/api/routes/feeds/index.js | 8 +++++--- src/api/routes/feeds/model.js | 26 +++++++++++++++----------- src/api/routes/floodgauges/index.js | 6 ++++-- src/api/routes/floodgauges/model.js | 3 ++- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/api/routes/cities/index.js b/src/api/routes/cities/index.js index 1cb88df..b7ad402 100644 --- a/src/api/routes/cities/index.js +++ b/src/api/routes/cities/index.js @@ -12,14 +12,16 @@ import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Get a list of infrastructure by type for a given city api.get('/', cacheResponse('1 day'), validate({ query: { - format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + format: Joi.any().valid(config.FORMATS) + .default(config.FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), (req, res, next) => cities(config, db, logger).all() diff --git a/src/api/routes/feeds/index.js b/src/api/routes/feeds/index.js index ba24dad..b5ece90 100644 --- a/src/api/routes/feeds/index.js +++ b/src/api/routes/feeds/index.js @@ -9,7 +9,7 @@ import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Create a new qlue record in the database // TODO: What is mandatory around title / text, any rules AND/OR? @@ -22,7 +22,8 @@ export default ({config, db, logger}) => { text: Joi.string().allow('').required(), image_url: Joi.string(), qlue_city: Joi.string().valid(config.API_FEEDS_QLUE_CITIES).required(), - disaster_type: Joi.string().valid(config.API_FEEDS_QLUE_DISASTER_TYPES).required(), + disaster_type: Joi.string().valid(config.API_FEEDS_QLUE_DISASTER_TYPES) + .required(), location: Joi.object().required().keys({ lat: Joi.number().min(-90).max(90).required(), lng: Joi.number().min(-180).max(180).required(), @@ -50,7 +51,8 @@ export default ({config, db, logger}) => { text: Joi.string().allow('').required(), url: Joi.string().allow(''), image_url: Joi.string(), - disaster_type: Joi.string().valid(config.API_FEEDS_DETIK_DISASTER_TYPES).required(), + disaster_type: Joi.string().valid(config.API_FEEDS_DETIK_DISASTER_TYPES) + .required(), location: Joi.object().required().keys({ latitude: Joi.number().min(-90).max(90).required(), longitude: Joi.number().min(-180).max(180).required(), diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index 3605c6c..2fbba8c 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -19,7 +19,8 @@ export default (config, db, logger) => ({ .then(() => resolve({post_id: body.post_id, created: true})) .catch((err) => { if (err.constraint === 'reports_post_id_key') { -resolve({post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table`}); + resolve({post_id: body.post_id, created: false, + message: `${body.post_id} already exists in reports table`}); } else /* istanbul ignore next */ { @@ -32,25 +33,28 @@ reject(err); addDetikReport: (body) => new Promise((resolve, reject) => { // Setup query let query = `INSERT INTO ${config.TABLE_FEEDS_DETIK} - (contribution_id, created_at, disaster_type, title, text, url, image_url, the_geom) + (contribution_id, created_at, disaster_type, title, text, url, image_url, + the_geom) VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_POINT($8, $9),4326))`; // Setup values - let values = [body.contribution_id, body.created_at, body.disaster_type, body.title, body.text, - body.url, body.image_url, body.location.longitude, body.location.latitude]; + let values = [body.contribution_id, body.created_at, body.disaster_type, + body.title, body.text, body.url, body.image_url, body.location.longitude, + body.location.latitude]; // Execute logger.debug(query, values); db.oneOrNone(query, values).timeout(config.PGTIMEOUT) - .then(() => resolve({contribution_id: body.contribution_id, created: true})) + .then(() => resolve({contribution_id: body.contribution_id, + created: true})) .catch((err) => { if (err.constraint === 'reports_contribution_id_key') { -resolve({contribution_id: body.contribution_id, created: false, message: `${body.contribution_id} already exists in reports table`}); -} else - /* istanbul ignore next */ - { -reject(err); -} + resolve({contribution_id: body.contribution_id, created: false, + message: `${body.contribution_id} already exists in reports table`}); + } else { + /* istanbul ignore next */ + reject(err); + } }); }), }); diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index 92379e0..5adeaaa 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -20,7 +20,8 @@ export default ({config, db, logger}) => { query: { city: Joi.any().valid(config.REGION_CODES), format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), (req, res, next) => floodgauges(config, db, logger).all() @@ -39,7 +40,8 @@ export default ({config, db, logger}) => { params: {id: Joi.number().integer().required()}, query: { format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT), - geoformat: Joi.any().valid(config.GEO_FORMATS).default(config.GEO_FORMAT_DEFAULT), + geoformat: Joi.any().valid(config.GEO_FORMATS) + .default(config.GEO_FORMAT_DEFAULT), }, }), (req, res, next) => floodgauges(config, db, logger).byId(req.params.id) diff --git a/src/api/routes/floodgauges/model.js b/src/api/routes/floodgauges/model.js index cb0a9b4..02ca4b2 100644 --- a/src/api/routes/floodgauges/model.js +++ b/src/api/routes/floodgauges/model.js @@ -14,7 +14,8 @@ export default (config, db, logger) => ({ GROUP BY gaugeid, the_geom, gaugenameid LIMIT $3`; // Setup values - let timeWindow = (Date.now() / 1000) - config.API_FLOODGAUGE_REPORTS_TIME_WINDOW; + let timeWindow = (Date.now() / 1000) - + config.API_FLOODGAUGE_REPORTS_TIME_WINDOW; let values = [timeWindow, city, config.API_FLOODGAUGE_REPORTS_LIMIT]; // Execute From 25f77ab742272d6fdf8baab813242b98eabc12eb Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:06:51 -0400 Subject: [PATCH 116/160] fixing lint issues --- src/api/routes/cards/model.js | 18 ++++++++++-------- src/api/routes/feeds/model.js | 23 ++++++++++++----------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index e7aa4b0..1f7daaa 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -27,11 +27,11 @@ export default (config, db, logger) => ({ // Return specific card by id byCardId: (cardId) => new Promise((resolve, reject) => { // Setup query - let query = `SELECT c.card_id, c.username, c.network, c.language, c.received, - CASE WHEN r.card_id IS NOT NULL THEN - json_build_object('created_at', r.created_at, 'disaster_type', r.disaster_type, - 'text', r.text, 'card_data', r.card_data, 'image_url', r.image_url, - 'status', r.status) + let query = `SELECT c.card_id, c.username, c.network, c.language, + c.received, CASE WHEN r.card_id IS NOT NULL THEN + json_build_object('created_at', r.created_at, 'disaster_type', + r.disaster_type, 'text', r.text, 'card_data', r.card_data, 'image_url', + r.image_url, 'status', r.status) ELSE null END AS report FROM ${config.TABLE_GRASP_CARDS} c LEFT JOIN ${config.TABLE_GRASP_REPORTS} r USING (card_id) @@ -52,16 +52,18 @@ export default (config, db, logger) => ({ }); }), - // Add an entry to the reports table and then update the card record accordingly + // Add entry to the reports table and then update the card record accordingly submitReport: (card, body) => new Promise((resolve, reject) => { // Setup our queries let queries = [ { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) - VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, + VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, + ST_SetSRID(ST_Point($7,$8),4326))`, values: [card.card_id, {flood_depth: body.water_depth}, body.text, - body.created_at, 'flood', 'Confirmed', body.location.lng, body.location.lat], + body.created_at, 'flood', 'Confirmed', body.location.lng, + body.location.lat], }, /** * TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready { diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index 2fbba8c..a863edc 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -6,12 +6,14 @@ export default (config, db, logger) => ({ addQlueReport: (body) => new Promise((resolve, reject) => { // Setup query let query = `INSERT INTO ${config.TABLE_FEEDS_QLUE} - (post_id, created_at, disaster_type, text, image_url, title, qlue_city, the_geom) + (post_id, created_at, disaster_type, text, image_url, title, qlue_city, + the_geom) VALUES ($1, $2, $3, $4, $5, $6, $7, ST_SetSRID(ST_Point($8,$9),4326))`; // Setup values - let values = [body.post_id, body.created_at, body.disaster_type, body.text, body.image_url, - body.title, body.qlue_city, body.location.lng, body.location.lat]; + let values = [body.post_id, body.created_at, body.disaster_type, body.text, + body.image_url,body.title, body.qlue_city, body.location.lng, + body.location.lat]; // Execute logger.debug(query, values); @@ -21,13 +23,11 @@ export default (config, db, logger) => ({ if (err.constraint === 'reports_post_id_key') { resolve({post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table`}); -} else - /* istanbul ignore next */ - { -reject(err); -} - }); - }), + } else{ + reject(err); + } + }); + }), // Add a detik report addDetikReport: (body) => new Promise((resolve, reject) => { @@ -50,7 +50,8 @@ reject(err); .catch((err) => { if (err.constraint === 'reports_contribution_id_key') { resolve({contribution_id: body.contribution_id, created: false, - message: `${body.contribution_id} already exists in reports table`}); + message: `${body.contribution_id}` + + ` already exists in reports table`}); } else { /* istanbul ignore next */ reject(err); From b8e868682bbc5d1f50ff938fb2bcee5a6b612f72 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:08:10 -0400 Subject: [PATCH 117/160] updated card model for new card data type --- src/api/routes/cards/model.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index 1f7daaa..b57e9bf 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -56,16 +56,6 @@ export default (config, db, logger) => ({ submitReport: (card, body) => new Promise((resolve, reject) => { // Setup our queries let queries = [ - { - query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} - (card_id, card_data, text, created_at, disaster_type, status, the_geom) - VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, - ST_SetSRID(ST_Point($7,$8),4326))`, - values: [card.card_id, {flood_depth: body.water_depth}, body.text, - body.created_at, 'flood', 'Confirmed', body.location.lng, - body.location.lat], - }, - /** * TODO - renable this query (and delete the one above) for updated data structure when PetaBencana.id client is ready { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} (card_id, card_data, text, created_at, disaster_type, status, the_geom) @@ -73,7 +63,6 @@ export default (config, db, logger) => ({ values: [ card.card_id, body.card_data, body.text, body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] }, - ***/ { query: `UPDATE ${config.TABLE_GRASP_CARDS} SET received = TRUE WHERE card_id = $1`, From ca05bb9ee31322aa0fd743e783eb2fc4e3ffc85a Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:15:09 -0400 Subject: [PATCH 118/160] fixed eslint issues --- src/api/index.js | 4 +-- src/api/routes/cards/index.js | 46 ++++++++++++++--------------- src/api/routes/cards/model.js | 11 ++++--- src/api/routes/feeds/model.js | 4 +-- src/api/routes/floodgauges/index.js | 2 +- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 8739534..a74a76f 100755 --- a/src/api/index.js +++ b/src/api/index.js @@ -20,7 +20,7 @@ import reports from './routes/reports'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Return the API version api.get('/', (req, res) => { @@ -37,7 +37,7 @@ export default ({config, db, logger}) => { api.use('/infrastructure', infrastructure({config, db, logger})); api.use('/reports', reports({config, db, logger})); - // Handle validation errors (wording of messages can be overridden using err.isJoi) + // Handle validation errors (wording can be overridden using err.isJoi) api.use(validate.errors()); // Handle not found errors diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index f8da28e..6d0e97f 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -27,7 +27,7 @@ const clearCache = () => { export default ({config, db, logger}) => { // Router - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Create an S3 object let s3 = new AWS.S3( @@ -50,7 +50,8 @@ export default ({config, db, logger}) => { (req, res, next) => { let cardId = shortid.generate(); cards(config, db, logger).create(cardId, req.body) - .then((data) => data ? res.status(200).json({cardId: cardId, created: true}) : + .then((data) => data ? res.status(200) + .json({cardId: cardId, created: true}) : next(new Error('Failed to create card'))) .catch((err) => { /* istanbul ignore next */ @@ -110,7 +111,8 @@ export default ({config, db, logger}) => { .required() .when('disaster_type', { is: 'flood', - then: Joi.object({flood_depth: Joi.number().integer().min(0).max(200).required()}), // b.c is required only when a is true + then: Joi.object({flood_depth: Joi.number().integer().min(0) + .max(200).required()}), // b.c is required only when a is true }), text: Joi.string().allow(''), image_url: Joi.string().allow(''), @@ -128,22 +130,21 @@ export default ({config, db, logger}) => { .then((card) => { // If the card does not exist then return an error message if (!card) { -res.status(404).json({statusCode: 404, cardId: req.params.cardId, + res.status(404).json({statusCode: 404, cardId: req.params.cardId, message: `No card exists with id '${req.params.cardId}'`}); -} - // If the card already has received status then return an error message - else if (card && card.received) { -res.status(409).json({statusCode: 409, - cardId: req.params.cardId, message: `Report already received for card '${req.params.cardId}'`}); -} - // We have a card and it has not yet had a report received - else { + } else if (card && card.received) { + // If card already has received status then return an error message + res.status(409).json({statusCode: 409, + cardId: req.params.cardId, message: `Report already received for '+ + ' card '${req.params.cardId}'`}); + } else { + // We have a card and it has not yet had a report received // Try and submit the report and update the card cards(config, db, logger).submitReport(card, req.body) .then((data) => { - console.log(data); clearCache(); - res.status(200).json({statusCode: 200, cardId: req.params.cardId, created: true}); + res.status(200).json({statusCode: 200, + cardId: req.params.cardId, created: true}); }) .catch((err) => { /* istanbul ignore next */ @@ -181,7 +182,8 @@ res.status(409).json({statusCode: 409, } else { let returnData = { signedRequest: data, - url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/'+ config.IMAGE_BUCKET+'/'+ s3params.Key, + url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/' + + config.IMAGE_BUCKET+'/'+ s3params.Key, }; // write the url into the db under image_url for this card @@ -192,14 +194,14 @@ res.status(404).json({statusCode: 404, cardId: req.params.cardId, message: `No card exists with id '${req.params.cardId}'`}); } else { // Try and submit the report and update the card - cards(config, db, logger).updateReport(card, {image_url: 'https://'+config.IMAGES_HOST+'/'+req.params.cardId+'.jpg'}) + cards(config, db, logger).updateReport(card, + {image_url: 'https://'+config.IMAGES_HOST+'/' + +req.params.cardId+'.jpg'}) .then((data) => { - console.log(data); clearCache(); logger.debug( 's3 signed request: ' + returnData.signedRequest); res.write(JSON.stringify(returnData)); res.end(); - // res.status(200).json({ statusCode: 200, cardId: req.params.cardId, updated: true }); }) .catch((err) => { /* istanbul ignore next */ @@ -229,15 +231,13 @@ res.status(404).json({statusCode: 404, cardId: req.params.cardId, if (!card) { res.status(404).json({statusCode: 404, cardId: req.params.cardId, message: `No card exists with id '${req.params.cardId}'`}); -} - // We have a card - else { + } else { // We have a card // Try and submit the report and update the card cards(config, db, logger).updateReport(card, req.body) .then((data) => { - console.log(data); clearCache(); - res.status(200).json({statusCode: 200, cardId: req.params.cardId, updated: true}); + res.status(200).json({statusCode: 200, + cardId: req.params.cardId, updated: true}); }) .catch((err) => { /* istanbul ignore next */ diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index b57e9bf..ba9495a 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -58,10 +58,13 @@ export default (config, db, logger) => ({ let queries = [ { query: `INSERT INTO ${config.TABLE_GRASP_REPORTS} - (card_id, card_data, text, created_at, disaster_type, status, the_geom) - VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, ST_SetSRID(ST_Point($7,$8),4326))`, - values: [ card.card_id, body.card_data, body.text, - body.created_at, body.disaster_type, 'Confirmed', body.location.lng, body.location.lat ] + (card_id, card_data, text, created_at, disaster_type, status, + the_geom) + VALUES ($1, $2, COALESCE($3,''), $4, $5, $6, + ST_SetSRID(ST_Point($7,$8),4326))`, + values: [card.card_id, body.card_data, body.text, + body.created_at, body.disaster_type, 'Confirmed', body.location.lng, + body.location.lat], }, { query: `UPDATE ${config.TABLE_GRASP_CARDS} diff --git a/src/api/routes/feeds/model.js b/src/api/routes/feeds/model.js index a863edc..08179f8 100644 --- a/src/api/routes/feeds/model.js +++ b/src/api/routes/feeds/model.js @@ -12,7 +12,7 @@ export default (config, db, logger) => ({ // Setup values let values = [body.post_id, body.created_at, body.disaster_type, body.text, - body.image_url,body.title, body.qlue_city, body.location.lng, + body.image_url, body.title, body.qlue_city, body.location.lng, body.location.lat]; // Execute @@ -23,7 +23,7 @@ export default (config, db, logger) => ({ if (err.constraint === 'reports_post_id_key') { resolve({post_id: body.post_id, created: false, message: `${body.post_id} already exists in reports table`}); - } else{ + } else { reject(err); } }); diff --git a/src/api/routes/floodgauges/index.js b/src/api/routes/floodgauges/index.js index 5adeaaa..913307d 100644 --- a/src/api/routes/floodgauges/index.js +++ b/src/api/routes/floodgauges/index.js @@ -12,7 +12,7 @@ import validate from 'celebrate'; export default ({config, db, logger}) => { - let api = Router(); + let api = Router(); // eslint-disable-line new-cap // Get a list of all flood gauge reports api.get('/', cacheResponse('1 minute'), From 2fca7dd03c47e8a19399d204ecbf513508325486 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:21:42 -0400 Subject: [PATCH 119/160] change minimumState variable to camelCase --- src/api/routes/floods/model.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/routes/floods/model.js b/src/api/routes/floods/model.js index 813810d..7efa149 100644 --- a/src/api/routes/floods/model.js +++ b/src/api/routes/floods/model.js @@ -3,7 +3,7 @@ import Promise from 'bluebird'; export default (config, db, logger) => ({ // Get all flood reports for a given city - all: (city, minimum_state) => new Promise((resolve, reject) => { + all: (city, minimumState) => new Promise((resolve, reject) => { // Setup query let query = `SELECT local_area as area_id, state, last_updated FROM ${config.TABLE_REM_STATUS} status, ${config.TABLE_LOCAL_AREAS} area @@ -12,7 +12,7 @@ export default (config, db, logger) => ({ AND ($1 IS NULL OR area.instance_region_code=$1)`; // Setup values - let values = [city, minimum_state]; + let values = [city, minimumState]; // Execute logger.debug(query, values); @@ -27,19 +27,19 @@ export default (config, db, logger) => ({ }), // Get all flood reports for a given city - allGeo: (city, minimum_state) => new Promise((resolve, reject) => { + allGeo: (city, minimumState) => new Promise((resolve, reject) => { // Setup query let query = `SELECT la.the_geom, la.pkey as area_id, la.geom_id, la.area_name, la.parent_name, la.city_name, rs.state, rs.last_updated FROM ${config.TABLE_LOCAL_AREAS} la - ${minimum_state ? 'JOIN' : 'LEFT JOIN'} + ${minimumState ? 'JOIN' : 'LEFT JOIN'} (SELECT local_area, state, last_updated FROM ${config.TABLE_REM_STATUS} WHERE state IS NOT NULL AND ($2 IS NULL OR state >= $2)) rs ON la.pkey = rs.local_area WHERE $1 IS NULL OR instance_region_code = $1`; // Setup values - let values = [city, minimum_state]; + let values = [city, minimumState]; // Execute logger.debug(query, values); From 247058273db04640f6afa3241311f8d988e4d5e4 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:25:51 -0400 Subject: [PATCH 120/160] testing move of test timeout parameter to arrow function --- src/test/testCards.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/testCards.js b/src/test/testCards.js index 7b3934f..da64301 100644 --- a/src/test/testCards.js +++ b/src/test/testCards.js @@ -114,8 +114,7 @@ export default function(app) { }); // Get signed URL for card image - it('Get card image link', function(done) { - this.timeout(15000); // nested call + it('Get card image link', (done) => { test.httpAgent(app) .get('/cards/'+cardId+'/images') .expect(200) @@ -126,7 +125,7 @@ export default function(app) { done(); } }); - }); + }).timeout(150000); // Request a card, submit and get resulting report it('Get card data', function(done) { From 5da5b66c994962b68565f0f40e07416f93d8ef8c Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:38:35 -0400 Subject: [PATCH 121/160] changed tests wit long timeouts to arrow functions --- src/test/testFloods.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/test/testFloods.js b/src/test/testFloods.js index 9b160da..7c2f915 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -87,8 +87,7 @@ describe('Flood areas endpoint', function() { }); // Get floods - it('Get floods (GET /floods)', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get floods (GET /floods)', (done) => { test.httpAgent(app) .get('/floods') .expect(200) @@ -100,11 +99,10 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned // Get floods it('Get severe floods (GET /floods?minimum_state=3)', function(done) { - this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?minimum_state=3') .expect(200) @@ -116,7 +114,8 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned + // Just get flood states it('Get flood states without geo (GET /floods/states)', function(done) { @@ -134,8 +133,7 @@ describe('Flood areas endpoint', function() { }); // Get geographic floods - it('Get floods in geojson format', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get floods in geojson format', (done) => { test.httpAgent(app) .get('/floods/?format=json&geoformat=geojson') .expect(200) @@ -147,11 +145,10 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned // Can get reports in CAP format - it('Get all reports in CAP format', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get all reports in CAP format', (done) => { test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) @@ -163,6 +160,6 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned; }); } From 6cbd4cf3957bf0d5eae80acaaff20290cfc5284d Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 12:38:35 -0400 Subject: [PATCH 122/160] changed tests with long timeouts to arrow functions --- src/test/testFloods.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/test/testFloods.js b/src/test/testFloods.js index 9b160da..7c2f915 100644 --- a/src/test/testFloods.js +++ b/src/test/testFloods.js @@ -87,8 +87,7 @@ describe('Flood areas endpoint', function() { }); // Get floods - it('Get floods (GET /floods)', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get floods (GET /floods)', (done) => { test.httpAgent(app) .get('/floods') .expect(200) @@ -100,11 +99,10 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned // Get floods it('Get severe floods (GET /floods?minimum_state=3)', function(done) { - this.timeout(15000); // a lot of data is returned test.httpAgent(app) .get('/floods?minimum_state=3') .expect(200) @@ -116,7 +114,8 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned + // Just get flood states it('Get flood states without geo (GET /floods/states)', function(done) { @@ -134,8 +133,7 @@ describe('Flood areas endpoint', function() { }); // Get geographic floods - it('Get floods in geojson format', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get floods in geojson format', (done) => { test.httpAgent(app) .get('/floods/?format=json&geoformat=geojson') .expect(200) @@ -147,11 +145,10 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned // Can get reports in CAP format - it('Get all reports in CAP format', function(done) { - this.timeout(15000); // a lot of data is returned + it('Get all reports in CAP format', (done) => { test.httpAgent(app) .get('/floods?format=xml&geoformat=cap') .expect(200) @@ -163,6 +160,6 @@ describe('Flood areas endpoint', function() { done(); } }); - }); + }).timeout(15000); // a lot of data is returned; }); } From 63db0e021df8529eb3846a113f36df6db6607e0b Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 13:13:52 -0400 Subject: [PATCH 123/160] =?UTF-8?q?Code=20now=20conforms=20to=20ESlint=20s?= =?UTF-8?q?tyle=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9521d43..906704f 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,8 @@ 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. +Code follows the [Google JavaScript style](https://google.github.io/styleguide/jsguide.html).ESLint is run with tests to enfoce style. + ### Issue Tracking Issues are tracked using [GitHub](https://github.com/urbanriskmap/cognicity-server/issues) From 83424c14ccbda21b986bc596bdb9f6bf17f84e4b Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 14:15:10 -0400 Subject: [PATCH 124/160] Fix #41 - removed node-pg from test modules --- src/test/index.js | 225 +++++++++++++++++----------------------------- 1 file changed, 80 insertions(+), 145 deletions(-) diff --git a/src/test/index.js b/src/test/index.js index d9eb343..bd03448 100644 --- a/src/test/index.js +++ b/src/test/index.js @@ -7,6 +7,7 @@ // Import config import config from '../config'; +config.PGDATABASE = 'cognicity_server_testing'; // Import DB initializer import initializeDb from '../db'; @@ -40,12 +41,15 @@ import testCAP from './testCAP.js'; import testDB from './testDB.js'; // Put some sample data in the database -const pg = require('pg'); +import Promise from 'bluebird'; +const pgp = require('pg-promise')({ + promiseLib: Promise, // Use bluebird for enhanced promises +}); // Create a top-level testing harness describe('Cognicity Server Testing Harness', function() { // PG config string for dummy data inserts -let PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; +const PG_CONFIG_STRING = 'postgres://'+config.PGUSER+'@'+config.PGHOST+':'+config.PGPORT+'/'+config.PGDATABASE; // Global report value let reportid = 1; @@ -78,174 +82,105 @@ let token = jwt.sign({}, // Removes dummy data describe('Cleans up', function() { - it('Removes dummy report data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} - WHERE source = 'grasp' - AND text = 'integration testing';`, - values: [reportid], - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); - }); + let db = pgp(PG_CONFIG_STRING); + it('Removes dummy report data', function(done) { + let query = `DELETE FROM ${config.TABLE_REPORTS} + WHERE source = 'grasp' + AND text = 'integration testing';`; + + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); + }); - it('Removes dummy cards data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_GRASP_CARDS} - WHERE username = 'testuser' - AND network = 'test network';`, - values: [reportid], - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); - }); + it('Removes dummy cards data', function(done) { + let query = `DELETE FROM ${config.TABLE_GRASP_CARDS} + WHERE username = 'testuser' + AND network = 'test network';`; - it('Removes dummy cards data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_GRASP_REPORTS} - WHERE text = 'integration testing';`, - values: [reportid], - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); - }); + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); + }); + + it('Removes dummy cards data', function(done) { + let query = `DELETE FROM ${config.TABLE_GRASP_REPORTS} + WHERE text = 'integration testing';`; + + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); + }); // Remove dummy data from REM floods table it('Removes dummy flood data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 5`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); + let query = `DELETE FROM ${config.TABLE_REM_STATUS} WHERE local_area = 5`; + + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); // Remove dummy data from REM floods table it('Removes dummy flood data from log', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_REM_STATUS_LOG} - WHERE username = testing`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); + let query = `DELETE FROM ${config.TABLE_REM_STATUS_LOG} + WHERE username = 'testing'`; + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); - }); + // Remove dummy data from qlue table it('Removes dummy qlue data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); + let query =`DELETE FROM ${config.TABLE_FEEDS_QLUE} WHERE pkey = 9999`; + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); // Remove dummy qlue data from all reports table it('Removes dummy qlue data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} - WHERE fkey = 9999 AND source = 'qlue'`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); + let query = `DELETE FROM ${config.TABLE_REPORTS} + WHERE fkey = 9999 AND source = 'qlue'`; + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); // Remove dummy data from detik table it('Removes dummy detik data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); + let query = `DELETE FROM ${config.TABLE_FEEDS_DETIK} WHERE pkey = 9999`; + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); - }); // Remove dummy detik data from all reports table it('Removes dummy detik data', function(done) { - let queryObject = { - text: `DELETE FROM ${config.TABLE_REPORTS} - WHERE fkey = 9999 AND source = 'detik'`, - }; - pg.connect(PG_CONFIG_STRING, function(err, client, pgDone) { - client.query(queryObject, function() { - if (err) { - console.log(err.message); - pgDone(); - } else { - pgDone(); - done(); - } - }); - }); + let query = `DELETE FROM ${config.TABLE_REPORTS} + WHERE fkey = 9999 AND source = 'detik'`; + db.none(query) + .then(() => { + done(); + }) + .catch((error) => console.log(error)); }); return (done()); }); From c2270c64795057efa7630ff799f107788630cfe0 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 14:15:29 -0400 Subject: [PATCH 125/160] Changed tests to require specific test database --- .travis.yml | 2 +- README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c319e8f..e071b3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ branches: before_install: - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi - - git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && cd urbanriskmap/cognicity-schema && bash build/run.sh && cd - + - export DATABASE=cognicity_server_testing ; git clone -b dev https://github.com/urbanriskmap/cognicity-schema.git urbanriskmap/cognicity-schema && cd urbanriskmap/cognicity-schema && bash build/run.sh && cd - install: - npm install diff --git a/README.md b/README.md index 906704f..d800274 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,8 @@ 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. +To run tests locally a new database "cognicity_server_testing" is required on localhost. + Code follows the [Google JavaScript style](https://google.github.io/styleguide/jsguide.html).ESLint is run with tests to enfoce style. ### Issue Tracking From f6229246e528d36e52ec1f2f695ee1d17955c2f2 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 14:37:50 -0400 Subject: [PATCH 126/160] Added format tests for reports --- src/test/testReports.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/test/testReports.js b/src/test/testReports.js index 79ae8ff..d770093 100644 --- a/src/test/testReports.js +++ b/src/test/testReports.js @@ -30,6 +30,36 @@ export default function(app, reportid) { }); }); + // Can get reports as geojson + it('Get all reports as geojson', function(done) { + test.httpAgent(app) + .get('/reports?format=json&geoformat=geojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } else { + done(); + } + }); + }); + + // Can get reports as geojson + it('Get all reports as topojson', function(done) { + test.httpAgent(app) + .get('/reports?format=json&geoformat=topojson') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) { + test.fail(err.message + ' ' + JSON.stringify(res)); + } else { + done(); + } + }); + }); + // Can get reports by city it('Get reports by city /reports?city=jbd', function(done) { test.httpAgent(app) @@ -60,8 +90,8 @@ export default function(app, reportid) { }); }); - // Can get reports - it('Get all reports/:id endpoint (GET /reports/:id)', function(done) { + // Can report by id + it('Get reports/:id endpoint', function(done) { test.httpAgent(app) .get('/reports/'+reportid) .expect(200) From d09d4e7404473e64b83f4f414b164a8de4bd8455 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 14:38:53 -0400 Subject: [PATCH 127/160] formatting --- src/api/routes/reports/model.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 57e8522..875d913 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -1,7 +1,6 @@ import Promise from 'bluebird'; export default (config, db, logger) => ({ - /** * Return all reports within a defined time period, and optionally city * @param {integer} timeperiod Length of time period in seconds @@ -53,5 +52,4 @@ export default (config, db, logger) => ({ reject(err); }); }), - }); From 009c067ef2c6b0ee812bfbd9b92930565ccb798c Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:08:40 -0400 Subject: [PATCH 128/160] Added JSDoc comments --- src/db.js | 11 +++++++++++ src/index.js | 4 ++++ src/server.js | 14 ++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/db.js b/src/db.js index 096921e..e5b9927 100755 --- a/src/db.js +++ b/src/db.js @@ -1,3 +1,8 @@ +/** + * CogniCity Server Database + * @module db + * Database initializer + **/ import Promise from 'bluebird'; // Import DB library @@ -6,6 +11,12 @@ const pgp = require('pg-promise')({ promiseLib: Promise, // Use bluebird for enhanced Promises }); +/** + * @alias module:db + * @param {Object} config - configuration + * @param {Object} logger - logger + * @return {Object} db - PG Promise database + **/ export default (config, logger) => new Promise((resolve, reject) => { // Build the connection string const cn = `postgres://${config.PGUSER}:${config.PGPASSWORD}@${config.PGHOST}:${config.PGPORT}/${config.PGDATABASE}?ssl=${config.PGSSL}`; diff --git a/src/index.js b/src/index.js index d89ab52..a5eb8dd 100755 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,7 @@ +/* + * @file Run CogniCity Data Server + * @author Urban Risk Lab, 2017 + */ // Import express, fs and http import fs from 'fs'; import path from 'path'; diff --git a/src/server.js b/src/server.js index d483fc0..21fc05d 100644 --- a/src/server.js +++ b/src/server.js @@ -1,3 +1,9 @@ +/** + * CogniCity Data Server Module + * @module server + * Core server module + **/ +// Import import Promise from 'bluebird'; // Express middleware and http @@ -11,6 +17,14 @@ import compression from 'compression'; import responseTime from 'response-time'; import morgan from 'morgan'; // Express logging +/** + * @alias module:server + * @param {Object} config - configuration + * @param {Object} initializeDb - database initialization + * @param {Object} routes - routes + * @param {Object} logger - logger + * @return {Object} - Express server application + **/ // Function to initialize the api server const init = (config, initializeDb, routes, logger) => new Promise((resolve, reject) => { From b384c28476a7070f66a190fb666505d546a95011 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:11:39 -0400 Subject: [PATCH 129/160] Added JSDoc comment --- src/lib/cap.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/cap.js b/src/lib/cap.js index f0975ba..6b0fac9 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -1,4 +1,9 @@ 'use strict'; +/** + * CogniCity CAP data format utility + * @module lib/cap + * @param {object} logger Configured Winston logger instance + */ // XML builder used to create XML output import builder from 'xmlbuilder'; @@ -8,6 +13,7 @@ import moment from 'moment-timezone'; module.exports = class Cap { /** * Setup the CAP object to user specified logger + * @alias module:lib/cap * @param {object} logger Configured Winston logger instance */ constructor(logger) { From 07fa18b6e5b81b7a08181efd664d2e4c584e120a Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:19:16 -0400 Subject: [PATCH 130/160] updated JSDoc comments --- src/api/index.js | 12 +++++++++++- src/index.js | 4 ++-- src/lib/cap.js | 24 ++++++++++++------------ src/lib/util.js | 5 +++++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index a74a76f..e1f2365 100755 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,3 +1,7 @@ +/** + * CogniCity Server Data API + * @module src/api/index + **/ import {Router} from 'express'; // Import the dependencies we need to handle the request @@ -18,7 +22,13 @@ import floods from './routes/floods'; import infrastructure from './routes/infrastructure'; import reports from './routes/reports'; - +/** +* @alias module:src/api/index +* @param {Object} config Server configuration +* @param {Object} db PG Promise database instance +* @param {Object} logger Configured Winston logger instance +* @return {Object} api Express router object for API routes +**/ export default ({config, db, logger}) => { let api = Router(); // eslint-disable-line new-cap diff --git a/src/index.js b/src/index.js index a5eb8dd..e9d6282 100755 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ -/* +/** * @file Run CogniCity Data Server * @author Urban Risk Lab, 2017 - */ + **/ // Import express, fs and http import fs from 'fs'; import path from 'path'; diff --git a/src/lib/cap.js b/src/lib/cap.js index 6b0fac9..f8a1786 100644 --- a/src/lib/cap.js +++ b/src/lib/cap.js @@ -2,8 +2,8 @@ /** * CogniCity CAP data format utility * @module lib/cap - * @param {object} logger Configured Winston logger instance - */ + * @param {Object} logger Configured Winston logger instance + **/ // XML builder used to create XML output import builder from 'xmlbuilder'; @@ -14,7 +14,7 @@ module.exports = class Cap { /** * Setup the CAP object to user specified logger * @alias module:lib/cap - * @param {object} logger Configured Winston logger instance + * @param {Object} logger Configured Winston logger instance */ constructor(logger) { this.logger = logger; @@ -22,9 +22,9 @@ module.exports = class Cap { /** * Transform GeoJSON data to ATOM feed of CAP format XML data. * See {@link https://tools.ietf.org/html/rfc4287|ATOM syndication format} - * @param {object} features Peta Jakarta GeoJSON features object - * @return {string} XML CAP data describing all areas - */ + * @param {Object} features Peta Jakarta GeoJSON features object + * @return {String} XML CAP data describing all areas + **/ geoJsonToAtomCap(features) { let self = this; let feed = { @@ -76,8 +76,8 @@ module.exports = class Cap { * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + `CAP-v1.2-os.html#_Toc97699527|` + `CAP specification 3.2.1 "alert" Element and Sub-elements`} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing ALERT element for xmlbuilder + * @param {Object} feature petabencana.id GeoJSON feature + * @return {Object} Object representing ALERT element for xmlbuilder */ createAlert(feature) { let self = this; @@ -114,8 +114,8 @@ module.exports = class Cap { * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + `CAP-v1.2-os.html#_Toc97699542|` + `CAP specification 3.2.2 "info" Element and Sub-elements`} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing INFO element suitable for xmlbuilder + * @param {Object} feature petabencana.id GeoJSON feature + * @return {Object} Object representing INFO element suitable for xmlbuilder */ createInfo(feature) { let self = this; @@ -177,8 +177,8 @@ module.exports = class Cap { * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/` + `CAP-v1.2-os.html#_Toc97699550|` + `CAP specification 3.2.4 "area" Element and Sub-elements`} - * @param {object} feature petabencana.id GeoJSON feature - * @return {object} Object representing AREA element for XML xmlbuilder + * @param {Object} feature petabencana.id GeoJSON feature + * @return {Object} Object representing AREA element for XML xmlbuilder */ createArea(feature) { let self = this; diff --git a/src/lib/util.js b/src/lib/util.js index 515e6d0..f2f8436 100755 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,3 +1,8 @@ +/** + * @file CogniCity Server Utility file + * various functions to assist server + **/ + // Import dependencies import Promise from 'bluebird'; import jwt from 'express-jwt'; From 278919900a1808984978d8f2f94281ef2b401bb0 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:46:38 -0400 Subject: [PATCH 131/160] Added JSDoc comments --- package.json | 1 + src/api/index.js | 1 + src/api/routes/cards/index.js | 11 +++++++++++ src/api/routes/cards/model.js | 13 ++++++++++++- src/api/routes/reports/index.js | 14 +++++++++++++- src/api/routes/reports/model.js | 20 +++++++++++++------- src/config.js | 5 +++++ src/db.js | 1 + src/index.js | 1 + src/lib/util.js | 2 +- src/server.js | 1 + 11 files changed, 60 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 74441d3..8e15d59 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "eslint-config-google": "^0.8.0", "estraverse": "^4.2.0", "esutils": "^2.0.2", + "jsdoc": "^3.4.3", "jsonwebtoken": "^7.4.1", "mocha": "^3.3.0", "nodemon": "^1.11.0", diff --git a/src/api/index.js b/src/api/index.js index e1f2365..78a8575 100755 --- a/src/api/index.js +++ b/src/api/index.js @@ -23,6 +23,7 @@ import infrastructure from './routes/infrastructure'; import reports from './routes/reports'; /** +* Build CogniCity Server Data API * @alias module:src/api/index * @param {Object} config Server configuration * @param {Object} db PG Promise database instance diff --git a/src/api/routes/cards/index.js b/src/api/routes/cards/index.js index 6d0e97f..1134611 100644 --- a/src/api/routes/cards/index.js +++ b/src/api/routes/cards/index.js @@ -1,3 +1,7 @@ +/** + * CogniCity Server /cards endpoint + * @module src/api/cards/index + **/ import {Router} from 'express'; // Import our data model @@ -25,6 +29,13 @@ const clearCache = () => { apicache.clear(CACHE_GROUP_CARDS); }; +/** +* @alias module:src/api/cards/index +* @param {Object} config Server configuration +* @param {Object} db PG Promise database instance +* @param {Object} logger Configured Winston logger instance +* @return {Object} api Express router object for cards route +**/ export default ({config, db, logger}) => { // Router let api = Router(); // eslint-disable-line new-cap diff --git a/src/api/routes/cards/model.js b/src/api/routes/cards/model.js index ba9495a..50235ad 100644 --- a/src/api/routes/cards/model.js +++ b/src/api/routes/cards/model.js @@ -1,7 +1,18 @@ +/** + * CogniCity Server /cards data model + * @module src/api/cards/model + **/ import Promise from 'bluebird'; +/** +* Database interaction for Cards objects +* @alias module:src/api/cards/model +* @param {Object} config Server configuration +* @param {Object} db PG Promise database instance +* @param {Object} logger Configured Winston logger instance +* @return {Object} data Query results +**/ export default (config, db, logger) => ({ - // Create a new card entry with the given cardId create: (cardId, body) => new Promise((resolve, reject) => { // Setup query diff --git a/src/api/routes/reports/index.js b/src/api/routes/reports/index.js index ff961a5..89a1e28 100644 --- a/src/api/routes/reports/index.js +++ b/src/api/routes/reports/index.js @@ -1,4 +1,8 @@ -import {Router} from 'express'; +/** + * CogniCity Server /reports endpoint + * @module src/api/reports/index + **/ + import {Router} from 'express'; // Import our data model import reports from './model'; @@ -12,6 +16,14 @@ import {cacheResponse, handleGeoResponse} from '../../../lib/util'; import Joi from 'joi'; import validate from 'celebrate'; +/** + * Methods to get current flood reports from database + * @alias module:src/api/reports/index + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} api Express router object for reports route + */ export default ({config, db, logger}) => { let api = Router(); // eslint-disable-line new-cap diff --git a/src/api/routes/reports/model.js b/src/api/routes/reports/model.js index 875d913..12c3ba9 100644 --- a/src/api/routes/reports/model.js +++ b/src/api/routes/reports/model.js @@ -1,12 +1,18 @@ -import Promise from 'bluebird'; +/** + * CogniCity Server /reports data model + * @module src/api/reports/model + **/ + import Promise from 'bluebird'; +/** + * Methods to get current flood reports from database + * @alias module:src/api/reports/model + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} Query results + */ export default (config, db, logger) => ({ - /** - * Return all reports within a defined time period, and optionally city - * @param {integer} timeperiod Length of time period in seconds - * @param {string} city Optional, instance region code (e.g. 'jbd') - * @return {Object} Query results - */ all: (timeperiod, city) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, diff --git a/src/config.js b/src/config.js index 5a20257..a1086d9 100644 --- a/src/config.js +++ b/src/config.js @@ -1,3 +1,8 @@ +/** + * CogniCity Server configuration + * @file config + * @return {Object} Server configuration +*/ /* eslint-disable max-len */ require('dotenv').config({silent: true}); diff --git a/src/db.js b/src/db.js index e5b9927..3c25335 100755 --- a/src/db.js +++ b/src/db.js @@ -12,6 +12,7 @@ const pgp = require('pg-promise')({ }); /** + * Database interaction for Cards objects * @alias module:db * @param {Object} config - configuration * @param {Object} logger - logger diff --git a/src/index.js b/src/index.js index e9d6282..8478b89 100755 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ /** + * CogniCity Data Server * @file Run CogniCity Data Server * @author Urban Risk Lab, 2017 **/ diff --git a/src/lib/util.js b/src/lib/util.js index f2f8436..b93baa5 100755 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,6 +1,6 @@ /** + * Server utility functions * @file CogniCity Server Utility file - * various functions to assist server **/ // Import dependencies diff --git a/src/server.js b/src/server.js index 21fc05d..b10d6f2 100644 --- a/src/server.js +++ b/src/server.js @@ -18,6 +18,7 @@ import responseTime from 'response-time'; import morgan from 'morgan'; // Express logging /** + * Core server object * @alias module:server * @param {Object} config - configuration * @param {Object} initializeDb - database initialization From 4da6794668451df3cdb54ba689e360bb461823bf Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:52:22 -0400 Subject: [PATCH 132/160] Added JSDoc generation --- jsdoc/api_index.js.html | 127 + jsdoc/api_routes_cards_index.js.html | 335 ++ jsdoc/api_routes_cards_model.js.html | 205 ++ .../api_routes_reports_archive_model.js.html | 95 + jsdoc/api_routes_reports_index.js.html | 144 + jsdoc/api_routes_reports_model.js.html | 125 + .../db%0ADatabase%20initializermodule_.html | 131 + jsdoc/cognicity-server/3.0.1/db.js.html | 88 + .../3.0.1/fonts/OpenSans-Bold-webfont.eot | Bin 0 -> 19544 bytes .../3.0.1/fonts/OpenSans-Bold-webfont.svg | 1830 ++++++++++ .../3.0.1/fonts/OpenSans-Bold-webfont.woff | Bin 0 -> 22432 bytes .../fonts/OpenSans-BoldItalic-webfont.eot | Bin 0 -> 20133 bytes .../fonts/OpenSans-BoldItalic-webfont.svg | 1830 ++++++++++ .../fonts/OpenSans-BoldItalic-webfont.woff | Bin 0 -> 23048 bytes .../3.0.1/fonts/OpenSans-Italic-webfont.eot | Bin 0 -> 20265 bytes .../3.0.1/fonts/OpenSans-Italic-webfont.svg | 1830 ++++++++++ .../3.0.1/fonts/OpenSans-Italic-webfont.woff | Bin 0 -> 23188 bytes .../3.0.1/fonts/OpenSans-Light-webfont.eot | Bin 0 -> 19514 bytes .../3.0.1/fonts/OpenSans-Light-webfont.svg | 1831 ++++++++++ .../3.0.1/fonts/OpenSans-Light-webfont.woff | Bin 0 -> 22248 bytes .../fonts/OpenSans-LightItalic-webfont.eot | Bin 0 -> 20535 bytes .../fonts/OpenSans-LightItalic-webfont.svg | 1835 ++++++++++ .../fonts/OpenSans-LightItalic-webfont.woff | Bin 0 -> 23400 bytes .../3.0.1/fonts/OpenSans-Regular-webfont.eot | Bin 0 -> 19836 bytes .../3.0.1/fonts/OpenSans-Regular-webfont.svg | 1831 ++++++++++ .../3.0.1/fonts/OpenSans-Regular-webfont.woff | Bin 0 -> 22660 bytes jsdoc/cognicity-server/3.0.1/index.html | 273 ++ jsdoc/cognicity-server/3.0.1/index.js.html | 136 + .../3.0.1/scripts/linenumber.js | 25 + .../scripts/prettify/Apache-License-2.0.txt | 202 ++ .../3.0.1/scripts/prettify/lang-css.js | 2 + .../3.0.1/scripts/prettify/prettify.js | 28 + ...erver%0ACore%20server%20modulemodule_.html | 131 + jsdoc/cognicity-server/3.0.1/server.js.html | 141 + .../3.0.1/styles/jsdoc-default.css | 354 ++ .../3.0.1/styles/prettify-jsdoc.css | 111 + .../3.0.1/styles/prettify-tomorrow.css | 132 + jsdoc/config.js.html | 140 + jsdoc/db%0ADatabase%20initializermodule_.html | 142 + jsdoc/db.js.html | 100 + jsdoc/fonts/OpenSans-Bold-webfont.eot | Bin 0 -> 19544 bytes jsdoc/fonts/OpenSans-Bold-webfont.svg | 1830 ++++++++++ jsdoc/fonts/OpenSans-Bold-webfont.woff | Bin 0 -> 22432 bytes jsdoc/fonts/OpenSans-BoldItalic-webfont.eot | Bin 0 -> 20133 bytes jsdoc/fonts/OpenSans-BoldItalic-webfont.svg | 1830 ++++++++++ jsdoc/fonts/OpenSans-BoldItalic-webfont.woff | Bin 0 -> 23048 bytes jsdoc/fonts/OpenSans-Italic-webfont.eot | Bin 0 -> 20265 bytes jsdoc/fonts/OpenSans-Italic-webfont.svg | 1830 ++++++++++ jsdoc/fonts/OpenSans-Italic-webfont.woff | Bin 0 -> 23188 bytes jsdoc/fonts/OpenSans-Light-webfont.eot | Bin 0 -> 19514 bytes jsdoc/fonts/OpenSans-Light-webfont.svg | 1831 ++++++++++ jsdoc/fonts/OpenSans-Light-webfont.woff | Bin 0 -> 22248 bytes jsdoc/fonts/OpenSans-LightItalic-webfont.eot | Bin 0 -> 20535 bytes jsdoc/fonts/OpenSans-LightItalic-webfont.svg | 1835 ++++++++++ jsdoc/fonts/OpenSans-LightItalic-webfont.woff | Bin 0 -> 23400 bytes jsdoc/fonts/OpenSans-Regular-webfont.eot | Bin 0 -> 19836 bytes jsdoc/fonts/OpenSans-Regular-webfont.svg | 1831 ++++++++++ jsdoc/fonts/OpenSans-Regular-webfont.woff | Bin 0 -> 22660 bytes jsdoc/global.html | 3166 +++++++++++++++++ jsdoc/index.html | 466 +++ jsdoc/index.js.html | 148 + jsdoc/lib_cap.js.html | 307 ++ jsdoc/lib_util.js.html | 151 + jsdoc/module-lib_cap.html | 224 ++ jsdoc/module-src_api_cards_index.html | 172 + jsdoc/module-src_api_cards_model.html | 373 ++ jsdoc/module-src_api_index.html | 373 ++ jsdoc/module-src_api_reports_index.html | 373 ++ jsdoc/module-src_api_reports_model.html | 373 ++ jsdoc/scripts/linenumber.js | 25 + jsdoc/scripts/prettify/Apache-License-2.0.txt | 202 ++ jsdoc/scripts/prettify/lang-css.js | 2 + jsdoc/scripts/prettify/prettify.js | 28 + ...erver%0ACore%20server%20modulemodule_.html | 142 + jsdoc/server.js.html | 153 + jsdoc/styles/jsdoc-default.css | 354 ++ jsdoc/styles/prettify-jsdoc.css | 111 + jsdoc/styles/prettify-tomorrow.css | 132 + ...0CAP%20data%20format%20utilitymodule_.html | 142 + jsdoc/test_testCAP.js.html | 261 ++ ...test%20the%20_cards%20endpointmodule_.html | 142 + jsdoc/test_testCards.js.html | 233 ++ ...est%20the%20_cities%20endpointmodule_.html | 142 + jsdoc/test_testCities.js.html | 96 + ...%20the%20db%20utility%20modulemodule_.html | 142 + jsdoc/test_testDB.js.html | 102 + ...test%20the%20_feeds%20endpointmodule_.html | 142 + jsdoc/test_testFeeds.js.html | 185 + jsdoc/test_testFloodgauges.js.html | 126 + ...est%20the%20_floods%20endpointmodule_.html | 142 + jsdoc/test_testFloods.js.html | 229 ++ ...0the%20_floodgauges%20endpointmodule_.html | 142 + ...e%20_infrastructure%20endpointmodule_.html | 142 + jsdoc/test_testInfrastructure.js.html | 111 + ...st%20the%20_reports%20endpointmodule_.html | 142 + jsdoc/test_testReports.js.html | 172 + ...%20_reports_archive%20endpointmodule_.html | 142 + jsdoc/test_testReportsArchive.js.html | 206 ++ ...20routes%20from%20the%20servermodule_.html | 142 + jsdoc/test_testServer.js.html | 108 + package.json | 3 +- 101 files changed, 35809 insertions(+), 1 deletion(-) create mode 100644 jsdoc/api_index.js.html create mode 100644 jsdoc/api_routes_cards_index.js.html create mode 100644 jsdoc/api_routes_cards_model.js.html create mode 100644 jsdoc/api_routes_reports_archive_model.js.html create mode 100644 jsdoc/api_routes_reports_index.js.html create mode 100644 jsdoc/api_routes_reports_model.js.html create mode 100644 jsdoc/cognicity-server/3.0.1/db%0ADatabase%20initializermodule_.html create mode 100644 jsdoc/cognicity-server/3.0.1/db.js.html create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-BoldItalic-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-BoldItalic-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-BoldItalic-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Italic-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Italic-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Italic-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.eot create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.svg create mode 100644 jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.woff create mode 100644 jsdoc/cognicity-server/3.0.1/index.html create mode 100644 jsdoc/cognicity-server/3.0.1/index.js.html create mode 100644 jsdoc/cognicity-server/3.0.1/scripts/linenumber.js create mode 100644 jsdoc/cognicity-server/3.0.1/scripts/prettify/Apache-License-2.0.txt create mode 100644 jsdoc/cognicity-server/3.0.1/scripts/prettify/lang-css.js create mode 100644 jsdoc/cognicity-server/3.0.1/scripts/prettify/prettify.js create mode 100644 jsdoc/cognicity-server/3.0.1/server%0ACore%20server%20modulemodule_.html create mode 100644 jsdoc/cognicity-server/3.0.1/server.js.html create mode 100644 jsdoc/cognicity-server/3.0.1/styles/jsdoc-default.css create mode 100644 jsdoc/cognicity-server/3.0.1/styles/prettify-jsdoc.css create mode 100644 jsdoc/cognicity-server/3.0.1/styles/prettify-tomorrow.css create mode 100644 jsdoc/config.js.html create mode 100644 jsdoc/db%0ADatabase%20initializermodule_.html create mode 100644 jsdoc/db.js.html create mode 100644 jsdoc/fonts/OpenSans-Bold-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-Bold-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-Bold-webfont.woff create mode 100644 jsdoc/fonts/OpenSans-BoldItalic-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-BoldItalic-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-BoldItalic-webfont.woff create mode 100644 jsdoc/fonts/OpenSans-Italic-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-Italic-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-Italic-webfont.woff create mode 100644 jsdoc/fonts/OpenSans-Light-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-Light-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-Light-webfont.woff create mode 100644 jsdoc/fonts/OpenSans-LightItalic-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-LightItalic-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-LightItalic-webfont.woff create mode 100644 jsdoc/fonts/OpenSans-Regular-webfont.eot create mode 100644 jsdoc/fonts/OpenSans-Regular-webfont.svg create mode 100644 jsdoc/fonts/OpenSans-Regular-webfont.woff create mode 100644 jsdoc/global.html create mode 100644 jsdoc/index.html create mode 100644 jsdoc/index.js.html create mode 100644 jsdoc/lib_cap.js.html create mode 100644 jsdoc/lib_util.js.html create mode 100644 jsdoc/module-lib_cap.html create mode 100644 jsdoc/module-src_api_cards_index.html create mode 100644 jsdoc/module-src_api_cards_model.html create mode 100644 jsdoc/module-src_api_index.html create mode 100644 jsdoc/module-src_api_reports_index.html create mode 100644 jsdoc/module-src_api_reports_model.html create mode 100644 jsdoc/scripts/linenumber.js create mode 100644 jsdoc/scripts/prettify/Apache-License-2.0.txt create mode 100644 jsdoc/scripts/prettify/lang-css.js create mode 100644 jsdoc/scripts/prettify/prettify.js create mode 100644 jsdoc/server%0ACore%20server%20modulemodule_.html create mode 100644 jsdoc/server.js.html create mode 100644 jsdoc/styles/jsdoc-default.css create mode 100644 jsdoc/styles/prettify-jsdoc.css create mode 100644 jsdoc/styles/prettify-tomorrow.css create mode 100644 jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html create mode 100644 jsdoc/test_testCAP.js.html create mode 100644 jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html create mode 100644 jsdoc/test_testCards.js.html create mode 100644 jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html create mode 100644 jsdoc/test_testCities.js.html create mode 100644 jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html create mode 100644 jsdoc/test_testDB.js.html create mode 100644 jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html create mode 100644 jsdoc/test_testFeeds.js.html create mode 100644 jsdoc/test_testFloodgauges.js.html create mode 100644 jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html create mode 100644 jsdoc/test_testFloods.js.html create mode 100644 jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html create mode 100644 jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html create mode 100644 jsdoc/test_testInfrastructure.js.html create mode 100644 jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html create mode 100644 jsdoc/test_testReports.js.html create mode 100644 jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html create mode 100644 jsdoc/test_testReportsArchive.js.html create mode 100644 jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html create mode 100644 jsdoc/test_testServer.js.html diff --git a/jsdoc/api_index.js.html b/jsdoc/api_index.js.html new file mode 100644 index 0000000..aa7a9b0 --- /dev/null +++ b/jsdoc/api_index.js.html @@ -0,0 +1,127 @@ + + + + + JSDoc: Source: api/index.js + + + + + + + + + + +
+ +

Source: api/index.js

+ + + + + + +
+
+
/**
+ * CogniCity Server Data API
+ * @module src/api/index
+ **/
+import {Router} from 'express';
+
+// Import the dependencies we need to handle the request
+import errorHandler from 'api-error-handler';
+
+// Import validation dependencies
+import validate from 'celebrate';
+
+// Get the current version
+import {version} from '../../package.json';
+
+// Import our routes
+import cards from './routes/cards';
+import cities from './routes/cities';
+import feeds from './routes/feeds';
+import floodgauges from './routes/floodgauges';
+import floods from './routes/floods';
+import infrastructure from './routes/infrastructure';
+import reports from './routes/reports';
+
+/**
+* Build CogniCity Server Data API
+* @alias module:src/api/index
+* @param {Object} config Server configuration
+* @param {Object} db PG Promise database instance
+* @param {Object} logger Configured Winston logger instance
+* @return {Object} api Express router object for API routes
+**/
+export default ({config, db, logger}) => {
+	let api = Router(); // eslint-disable-line new-cap
+
+	// Return the API version
+	api.get('/', (req, res) => {
+		res.status(200).json({version});
+	});
+
+	// Mount the various endpoints
+	// api.use('/areas', cards({ config, db, logger }));// TODO: local_areas
+	api.use('/cards', cards({config, db, logger}));
+	api.use('/cities', cities({config, db, logger}));
+	api.use('/feeds', feeds({config, db, logger}));
+	api.use('/floodgauges', floodgauges({config, db, logger}));
+	api.use('/floods', floods({config, db, logger}));
+	api.use('/infrastructure', infrastructure({config, db, logger}));
+	api.use('/reports', reports({config, db, logger}));
+
+	// Handle validation errors (wording can be overridden using err.isJoi)
+	api.use(validate.errors());
+
+	// Handle not found errors
+	api.use((req, res) => {
+		res.status(404).json({message: 'URL not found', url: req.url});
+	});
+
+	// Handle errors gracefully returning nicely formatted json
+	api.use(errorHandler());
+
+	return api;
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_cards_index.js.html b/jsdoc/api_routes_cards_index.js.html new file mode 100644 index 0000000..6579318 --- /dev/null +++ b/jsdoc/api_routes_cards_index.js.html @@ -0,0 +1,335 @@ + + + + + JSDoc: Source: api/routes/cards/index.js + + + + + + + + + + +
+ +

Source: api/routes/cards/index.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /cards endpoint
+ * @module src/api/cards/index
+ **/
+import {Router} from 'express';
+
+// Import our data model
+import cards from './model';
+
+// Import any required utility functions
+import {cacheResponse, handleResponse} from '../../../lib/util';
+
+// Import validation dependencies
+import Joi from 'joi';
+import validate from 'celebrate';
+
+// Import ID generator
+import shortid from 'shortid';
+
+// Import image upload capabilities
+import AWS from 'aws-sdk';
+
+// Caching
+import apicache from 'apicache';
+const CACHE_GROUP_CARDS = '/cards';
+
+// Function to clear out the cache
+const clearCache = () => {
+  apicache.clear(CACHE_GROUP_CARDS);
+};
+
+/**
+* @alias module:src/api/cards/index
+* @param {Object} config Server configuration
+* @param {Object} db PG Promise database instance
+* @param {Object} logger Configured Winston logger instance
+* @return {Object} api Express router object for cards route
+**/
+export default ({config, db, logger}) => {
+  // Router
+  let api = Router(); // eslint-disable-line new-cap
+
+  // Create an S3 object
+  let s3 = new AWS.S3(
+    {
+      accessKeyId: config.AWS_S3_ACCESS_KEY_ID,
+      secretAccessKey: config.AWS_S3_SECRET_ACCESS_KEY,
+      signatureVersion: config.AWS_S3_SIGNATURE_VERSION,
+      region: config.AWS_REGION,
+    });
+
+  // Create a new card and if successful return generated cardId
+  api.post('/',
+    validate({
+      body: Joi.object().keys({
+        username: Joi.string().required(),
+        network: Joi.string().required(),
+        language: Joi.string().valid(config.LANGUAGES).required(),
+      }),
+    }),
+    (req, res, next) => {
+      let cardId = shortid.generate();
+      cards(config, db, logger).create(cardId, req.body)
+        .then((data) => data ? res.status(200)
+          .json({cardId: cardId, created: true}) :
+          next(new Error('Failed to create card')))
+        .catch((err) => {
+          /* istanbul ignore next */
+          logger.error(err);
+          /* istanbul ignore next */
+          next(err);
+        });
+    }
+  );
+
+  // Check for the existence of a card
+  api.head('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS),
+    validate({
+      params: {cardId: Joi.string().required()},
+    }),
+    (req, res, next) => {
+      req.apicacheGroup = CACHE_GROUP_CARDS;
+      cards(config, db, logger).byCardId(req.params.cardId)
+        .then((data) => data ? res.status(200).end() : res.status(404).end())
+        .catch((err) => {
+          /* istanbul ignore next */
+          logger.error(err);
+          /* istanbul ignore next */
+          next(err);
+        });
+    }
+  );
+
+  // Return a card
+  api.get('/:cardId', cacheResponse(config.CACHE_DURATION_CARDS),
+    validate({
+      params: {cardId: Joi.string().min(7).max(14).required()},
+    }),
+    (req, res, next) => {
+      req.apicacheGroup = CACHE_GROUP_CARDS;
+      cards(config, db, logger).byCardId(req.params.cardId)
+        .then((data) => handleResponse(data, req, res, next))
+        .catch((err) => {
+          /* istanbul ignore next */
+          logger.error(err);
+          /* istanbul ignore next */
+          next(err);
+        });
+    }
+  );
+
+  // Update a card record with a report
+  api.put('/:cardId', validate({
+    params: {cardId: Joi.string().min(7).max(14).required()},
+    body: Joi.object().keys({
+      disaster_type: Joi.string().valid(config.DISASTER_TYPES).required(),
+      card_data: Joi.object()
+        .keys({
+            flood_depth: Joi.number(),
+            report_type: Joi.string().valid(config.REPORT_TYPES).required(),
+        })
+        .required()
+        .when('disaster_type', {
+            is: 'flood',
+            then: Joi.object({flood_depth: Joi.number().integer().min(0)
+              .max(200).required()}),    // b.c is required only when a is true
+        }),
+      text: Joi.string().allow(''),
+      image_url: Joi.string().allow(''),
+      created_at: Joi.date().iso().required(),
+      location: Joi.object().required().keys({
+        lat: Joi.number().min(-90).max(90).required(),
+        lng: Joi.number().min(-180).max(180).required(),
+      }),
+    }),
+  }),
+  (req, res, next) => {
+    try {
+      // First get the card we wish to update
+      cards(config, db, logger).byCardId(req.params.cardId)
+        .then((card) => {
+          // If the card does not exist then return an error message
+          if (!card) {
+            res.status(404).json({statusCode: 404, cardId: req.params.cardId,
+            message: `No card exists with id '${req.params.cardId}'`});
+          } else if (card && card.received) {
+            // If card already has received status then return an error message
+            res.status(409).json({statusCode: 409,
+            cardId: req.params.cardId, message: `Report already received for '+
+              ' card '${req.params.cardId}'`});
+          } else {
+            // We have a card and it has not yet had a report received
+            // Try and submit the report and update the card
+            cards(config, db, logger).submitReport(card, req.body)
+              .then((data) => {
+                clearCache();
+                res.status(200).json({statusCode: 200,
+                  cardId: req.params.cardId, created: true});
+              })
+              .catch((err) => {
+                /* istanbul ignore next */
+                logger.error(err);
+                /* istanbul ignore next */
+                next(err);
+              });
+          }
+        });
+      } catch (err) {
+        /* istanbul ignore next */
+        logger.error(err);
+        /* istanbul ignore next */
+        next(err);
+      }
+    }
+  );
+
+  // Gives an s3 signed url for the frontend to upload an image to
+  api.get('/:cardId/images', validate({
+    params: {cardId: Joi.string().min(7).max(14).required()},
+  }),
+  (req, res, next) => {
+    let s3params = {
+      Bucket: config.IMAGE_BUCKET,
+      Key: 'originals/' + req.params.cardId + '.jpg',
+      ContentType: req.query.file_type,
+    };
+    s3.getSignedUrl('putObject', s3params, (err, data) => {
+      if (err) {
+        /* istanbul ignore next */
+        logger.error('could not get signed url from S3');
+        /* istanbul ignore next */
+        logger.error(err);
+      } else {
+        let returnData = {
+          signedRequest: data,
+          url: 'https://s3.'+config.AWS_REGION+'.amazonaws.com/'
+                + config.IMAGE_BUCKET+'/'+ s3params.Key,
+        };
+        // write the url into the db under image_url for this card
+
+        cards(config, db, logger).byCardId(req.params.cardId)
+          .then((card) => {
+            if (!card) {
+res.status(404).json({statusCode: 404, cardId: req.params.cardId,
+              message: `No card exists with id '${req.params.cardId}'`});
+} else {
+              // Try and submit the report and update the card
+              cards(config, db, logger).updateReport(card,
+                {image_url: 'https://'+config.IMAGES_HOST+'/'
+                            +req.params.cardId+'.jpg'})
+              .then((data) => {
+                clearCache();
+                logger.debug( 's3 signed request: ' + returnData.signedRequest);
+                res.write(JSON.stringify(returnData));
+                res.end();
+              })
+              .catch((err) => {
+                /* istanbul ignore next */
+                logger.error(err);
+                /* istanbul ignore next */
+                next(err);
+              });
+            }
+          });
+      }
+    });
+  });
+
+  // Update a card report with new details including the image URL
+  api.patch('/:cardId', validate({
+    params: {cardId: Joi.string().min(7).max(14).required()},
+    body: Joi.object().keys({
+      image_url: Joi.string().required(),
+    }),
+  }),
+  (req, res, next) => {
+    try {
+      // First get the card we wish to update
+      cards(config, db, logger).byCardId(req.params.cardId)
+        .then((card) => {
+          // If the card does not exist then return an error message
+          if (!card) {
+res.status(404).json({statusCode: 404, cardId: req.params.cardId,
+            message: `No card exists with id '${req.params.cardId}'`});
+          } else { // We have a card
+            // Try and submit the report and update the card
+            cards(config, db, logger).updateReport(card, req.body)
+              .then((data) => {
+                clearCache();
+                res.status(200).json({statusCode: 200,
+                  cardId: req.params.cardId, updated: true});
+              })
+              .catch((err) => {
+                /* istanbul ignore next */
+                logger.error(err);
+                /* istanbul ignore next */
+                next(err);
+              });
+          }
+        });
+      } catch (err) {
+        /* istanbul ignore next */
+        logger.error(err);
+        /* istanbul ignore next */
+        next(err);
+      }
+    }
+  );
+
+  return api;
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_cards_model.js.html b/jsdoc/api_routes_cards_model.js.html new file mode 100644 index 0000000..e36f92a --- /dev/null +++ b/jsdoc/api_routes_cards_model.js.html @@ -0,0 +1,205 @@ + + + + + JSDoc: Source: api/routes/cards/model.js + + + + + + + + + + +
+ +

Source: api/routes/cards/model.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /cards data model
+ * @module src/api/cards/model
+ **/
+import Promise from 'bluebird';
+
+/**
+* Database interaction for Cards objects
+* @alias module:src/api/cards/model
+* @param {Object} config Server configuration
+* @param {Object} db PG Promise database instance
+* @param {Object} logger Configured Winston logger instance
+* @return {Object} data Query results
+**/
+export default (config, db, logger) => ({
+  // Create a new card entry with the given cardId
+  create: (cardId, body) => new Promise((resolve, reject) => {
+    // Setup query
+    let query = `INSERT INTO ${config.TABLE_GRASP_CARDS}
+      (card_id, username, network, language, received)
+      VALUES ($1, $2, $3, $4, $5) RETURNING pkey`;
+
+    // Setup values
+    let values = [cardId, body.username, body.network, body.language, false];
+
+    // Execute
+    logger.debug(query, values);
+    db.oneOrNone(query, values).timeout(config.PGTIMEOUT)
+      .then((data) => resolve(data))
+      /* istanbul ignore next */
+      .catch((err) => {
+        /* istanbul ignore next */
+        reject(err);
+}
+      );
+  }),
+
+  // Return specific card by id
+  byCardId: (cardId) => new Promise((resolve, reject) => {
+    // Setup query
+    let query = `SELECT c.card_id, c.username, c.network, c.language,
+      c.received, CASE WHEN r.card_id IS NOT NULL THEN
+        json_build_object('created_at', r.created_at, 'disaster_type',
+        r.disaster_type, 'text', r.text, 'card_data', r.card_data, 'image_url',
+        r.image_url, 'status', r.status)
+      ELSE null END AS report
+      FROM ${config.TABLE_GRASP_CARDS} c
+      LEFT JOIN ${config.TABLE_GRASP_REPORTS} r USING (card_id)
+      WHERE c.card_id = $1
+      LIMIT 1`;
+
+    // Setup values
+    let values = [cardId];
+
+    // Execute
+    logger.debug(query, values);
+    db.oneOrNone(query, values).timeout(config.PGTIMEOUT)
+      .then((data) => resolve(data))
+      /* istanbul ignore next */
+      .catch((err) => {
+        /* istanbul ignore next */
+        reject(err);
+      });
+  }),
+
+  // Add entry to the reports table and then update the card record accordingly
+  submitReport: (card, body) => new Promise((resolve, reject) => {
+    // Setup our queries
+    let queries = [
+      {
+        query: `INSERT INTO ${config.TABLE_GRASP_REPORTS}
+          (card_id, card_data, text, created_at, disaster_type, status,
+            the_geom)
+          VALUES ($1, $2, COALESCE($3,''), $4, $5, $6,
+          ST_SetSRID(ST_Point($7,$8),4326))`,
+        values: [card.card_id, body.card_data, body.text,
+          body.created_at, body.disaster_type, 'Confirmed', body.location.lng,
+          body.location.lat],
+      },
+      {
+        query: `UPDATE ${config.TABLE_GRASP_CARDS}
+          SET received = TRUE WHERE card_id = $1`,
+        values: [card.card_id],
+      },
+      {
+        query: `INSERT INTO ${config.TABLE_GRASP_LOG}
+              (card_id, event_type)
+              VALUES ($1, $2)`,
+        values: [card.card_id, 'REPORT SUBMITTED'],
+      },
+    ];
+
+    // Log queries to debugger
+    for (let query of queries) logger.debug(query.query, query.values);
+
+    // Execute in a transaction as both INSERT and UPDATE must happen together
+    db.tx((t) => {
+      return t.batch(queries.map((query) => t.none(query.query, query.values)));
+    }).timeout(config.PGTIMEOUT)
+      .then((data) => resolve(data))
+      /* istanbul ignore next */
+      .catch((err) => {
+        /* istanbul ignore next */
+        reject(err);
+      });
+  }),
+
+  // Update the reports table with new report details
+  updateReport: (card, body) => new Promise((resolve, reject) => {
+    // Setup our queries
+    let queries = [
+      {
+        query: `UPDATE ${config.TABLE_GRASP_REPORTS} SET
+          image_url = COALESCE($2, image_url)
+          WHERE card_id = $1`,
+        values: [card.card_id, body.image_url],
+      },
+      {
+        query: `INSERT INTO ${config.TABLE_GRASP_LOG}
+              (card_id, event_type)
+              VALUES ($1, $2)`,
+        values: [card.card_id, 'REPORT UPDATES'],
+      },
+    ];
+
+    // Log queries to debugger
+    for (let query of queries) logger.debug(query.query, query.values);
+
+    // Execute in a transaction as both INSERT and UPDATE must happen together
+    db.tx((t) => {
+      return t.batch(queries.map((query) => t.none(query.query, query.values)));
+    }).timeout(config.PGTIMEOUT)
+      .then((data) => resolve(data))
+      /* istanbul ignore next */
+      .catch((err) => {
+        /* istanbul ignore next */
+        reject(err);
+      });
+  }),
+
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_reports_archive_model.js.html b/jsdoc/api_routes_reports_archive_model.js.html new file mode 100644 index 0000000..6bb8120 --- /dev/null +++ b/jsdoc/api_routes_reports_archive_model.js.html @@ -0,0 +1,95 @@ + + + + + JSDoc: Source: api/routes/reports/archive/model.js + + + + + + + + + + +
+ +

Source: api/routes/reports/archive/model.js

+ + + + + + +
+
+
import Promise from 'bluebird';
+
+export default (config, db, logger) => ({
+	/**
+	 * Return all reports within a defined time period, and optionally city
+	 * @param {integer} start Timestamp as ISO 8601 string for start of window
+	 * @param {string} end Timestamp as ISO 8601 string for end of window
+	 * @param {string} city Optional, instance region code (e.g. 'jbd')\
+	 * @return {Object} Query result
+	 */
+	all: (start, end, city) => new Promise((resolve, reject) => {
+		// Setup query
+		let query = `SELECT pkey, created_at, source,
+			status, url, image_url, disaster_type, report_data, tags, title, text,
+			the_geom FROM ${config.TABLE_REPORTS}
+			WHERE created_at >= $1::timestamp with time zone
+      AND created_at <= $2::timestamp with time zone
+			AND ($3 IS NULL OR tags->>'instance_region_code'=$3)
+			ORDER BY created_at DESC LIMIT $4`;
+
+		// var timeWindow = (Date.now() / 1000) - timeperiod;
+
+		let values = [start, end, city, config.API_REPORTS_LIMIT];
+
+		// Execute
+		logger.debug(query, values);
+		db.any(query, values).timeout(config.PGTIMEOUT)
+			.then((data) => resolve(data))
+			.catch((err) => reject(err));
+	}),
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_reports_index.js.html b/jsdoc/api_routes_reports_index.js.html new file mode 100644 index 0000000..15112c0 --- /dev/null +++ b/jsdoc/api_routes_reports_index.js.html @@ -0,0 +1,144 @@ + + + + + JSDoc: Source: api/routes/reports/index.js + + + + + + + + + + +
+ +

Source: api/routes/reports/index.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /reports endpoint
+ * @module src/api/reports/index
+ **/
+ import {Router} from 'express';
+
+// Import our data model
+import reports from './model';
+
+import archive from './archive';
+
+// Import any required utility functions
+import {cacheResponse, handleGeoResponse} from '../../../lib/util';
+
+// Import validation dependencies
+import Joi from 'joi';
+import validate from 'celebrate';
+
+/**
+ * Methods to get current flood reports from database
+ * @alias module:src/api/reports/index
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} api Express router object for reports route
+ */
+export default ({config, db, logger}) => {
+	let api = Router(); // eslint-disable-line new-cap
+
+	// Get a list of all reports
+	api.get('/', cacheResponse('1 minute'),
+		validate({
+			query: {
+				city: Joi.any().valid(config.REGION_CODES),
+				timeperiod: Joi.number().integer().positive()
+					.max(config.API_REPORTS_TIME_WINDOW_MAX)
+					.default(config.API_REPORTS_TIME_WINDOW),
+				format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT),
+				geoformat: Joi.any().valid(config.GEO_FORMATS)
+					.default(config.GEO_FORMAT_DEFAULT),
+			},
+		}),
+		(req, res, next) => reports(config, db, logger)
+													.all(req.query.timeperiod, req.query.city)
+			.then((data) => handleGeoResponse(data, req, res, next))
+			.catch((err) => {
+				/* istanbul ignore next */
+				logger.error(err);
+				/* istanbul ignore next */
+				next(err);
+			})
+	);
+
+	// to get all reports between two dates
+	api.use('/archive', archive({config, db, logger}));
+
+	// Get a single report
+	api.get('/:id', cacheResponse('1 minute'),
+		validate({
+			params: {id: Joi.number().integer().required()},
+			query: {
+				format: Joi.any().valid(config.FORMATS)
+					.default(config.FORMAT_DEFAULT),
+				geoformat: Joi.any().valid(config.GEO_FORMATS)
+					.default(config.GEO_FORMAT_DEFAULT),
+			},
+		}),
+		(req, res, next) => reports(config, db, logger).byId(req.params.id)
+			.then((data) => handleGeoResponse(data, req, res, next))
+			.catch((err) => {
+				/* istanbul ignore next */
+				logger.error(err);
+				/* istanbul ignore next */
+				next(err);
+			})
+	);
+
+
+	return api;
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_reports_model.js.html b/jsdoc/api_routes_reports_model.js.html new file mode 100644 index 0000000..2a1fdf7 --- /dev/null +++ b/jsdoc/api_routes_reports_model.js.html @@ -0,0 +1,125 @@ + + + + + JSDoc: Source: api/routes/reports/model.js + + + + + + + + + + +
+ +

Source: api/routes/reports/model.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /reports data model
+ * @module src/api/reports/model
+ **/
+ import Promise from 'bluebird';
+
+/**
+ * Methods to get current flood reports from database
+ * @alias module:src/api/reports/model
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} Query results
+ */
+export default (config, db, logger) => ({
+	all: (timeperiod, city) => new Promise((resolve, reject) => {
+		// Setup query
+		let query = `SELECT pkey, created_at, source,
+			status, url, image_url, disaster_type, report_data, tags, title, text,
+			the_geom FROM ${config.TABLE_REPORTS}
+			WHERE created_at >= to_timestamp($1)
+			AND ($2 IS NULL OR tags->>'instance_region_code'=$2)
+			ORDER BY created_at DESC LIMIT $3`;
+
+		let timeWindow = (Date.now() / 1000) - timeperiod;
+
+		let values = [timeWindow, city, config.API_REPORTS_LIMIT];
+
+		// Execute
+		logger.debug(query, values);
+		db.any(query, values).timeout(config.PGTIMEOUT)
+			.then((data) => resolve(data))
+			/* istanbul ignore next */
+			.catch((err) => {
+				/* istanbul ignore next */
+				reject(err);
+			});
+	}),
+
+	// Return specific report by id
+	byId: (id) => new Promise((resolve, reject) => {
+		// Setup query
+		let query = `SELECT pkey, created_at, source,
+			status, url, image_url, disaster_type, report_data, tags, title, text,
+			the_geom FROM ${config.TABLE_REPORTS}
+			WHERE pkey = $1`;
+
+		// Setup values
+		let values = [id];
+
+		// Execute
+		logger.debug(query, values);
+		db.oneOrNone(query, values).timeout(config.PGTIMEOUT)
+			.then((data) => resolve(data))
+			/* istanbul ignore next */
+			.catch((err) => {
+				/* istanbul ignore next */
+				reject(err);
+			});
+	}),
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/cognicity-server/3.0.1/db%0ADatabase%20initializermodule_.html b/jsdoc/cognicity-server/3.0.1/db%0ADatabase%20initializermodule_.html new file mode 100644 index 0000000..4d5f4bf --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/db%0ADatabase%20initializermodule_.html @@ -0,0 +1,131 @@ + + + + + JSDoc: Module: db +Database initializer + + + + + + + + + + +
+ +

Module: db +Database initializer

+ + + + + + +
+ +
+ +
+ +
+
+ + +
CogniCity Server Database
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/db.js.html b/jsdoc/cognicity-server/3.0.1/db.js.html new file mode 100644 index 0000000..baace49 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/db.js.html @@ -0,0 +1,88 @@ + + + + + JSDoc: Source: db.js + + + + + + + + + + +
+ +

Source: db.js

+ + + + + + +
+
+
/**
+ * CogniCity Server Database
+ * @module db
+ * Database initializer
+ **/
+import Promise from 'bluebird';
+
+// Import DB library
+const pgp = require('pg-promise')({
+  // Initialization Options
+  promiseLib: Promise, // Use bluebird for enhanced Promises
+});
+
+/**
+ * @alias module:db
+ * @param {Object} config - configuration
+ * @param {Object} logger - logger
+ * @return {Object} db - PG Promise database
+ **/
+export default (config, logger) => new Promise((resolve, reject) => {
+	// Build the connection string
+	const cn = `postgres://${config.PGUSER}:${config.PGPASSWORD}@${config.PGHOST}:${config.PGPORT}/${config.PGDATABASE}?ssl=${config.PGSSL}`;
+  logger.debug(cn);
+
+	// Setup the connection
+	let db = pgp(cn);
+
+	// Make sure we can connect, if so resolve, if not reject
+	db.proc('version').timeout(config.PGTIMEOUT)
+		.then(() => resolve(db))
+		.catch((err) => {
+			logger.error(err);
+			reject(err);
+		});
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.eot b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 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 literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.svg b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 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 ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-BoldItalic-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 zcmZsC18^o?(C!;28{4*R+s4MWZQHh;Y;4=c#x^##ar4z*x9Z-izo(w+)6aCD(=$_Z zX6j6jo4lA900{6SnvekG|8#os|JeVv|9=q^Q;`J#fXaVZod00t3i={0A}aR74gJ`7 zKOg|Y0f34t$SePFhX4R*5dZ*{OY4X(B(AI~1OR}C|M&#_pgi9&JXc8RP9o zCqzMe3Yr->{lvnt{P_Im`yUX@tUXMBI355%Xb=E!j7Ku=7Be?7Fa`h=e|7`@^JN2q zNM$nrA%D34Y{DOqz)gX6ncFzK|8VL*d58l5AYC78bV=5BMn8Va`9JwB|6sTJe)7h~ z!2M@j)gNB~!G8cD1g^0)urc}J(tmu`e{wXneoxZ2w{vm^0Dk`f==G;RK#AwolD(tJ zPprld0P+9fUWDkv&BX90XU!iI0RA7$qZDg@G|+#<6mQ||e|p?V^1t&9m|nvC<-TsD zZ>+Ds3t|Wbj-YR-4?5r`Fa>K0Vs)C0=rl@wBnb6$3m7g`Wx>q@OwcRc|qNB1RiTqRPjk40m`>okPgoi z7dS*Y4q2`g!l>hOy06fc+9v6Eoc^Bant68A?-*ANQPSjW&McCZwRfceo&USTE3TsF zV!K(Z*^BSfvX+f9H15vBW5@3vXRW)^s}|{t5QwH~yqMk*{YrFU zo<>IWq;M^9Y2JAp2qWSXsT02we>!!h_J!7wsndeI5Sm`s_viR)r`-V&s`T zaj5gTFFZ8_Oq$<%2v&_t&yiq=QvIEAXe6SdA zWvRE^^lP+cKI-}%@;a~<;qcC7G;VZG^acTJ_Yfy!7y(Gw9^?bE9bkufhzI(F06NGX zkM716l5T($BNVX>xX2!LL?5Rn;e>0`Kg&L=U2+TRD|Ek8iX0sHwP&%i&9L8uvvQ!+#oM76!r_a=e)O7m(xw&MRA z3C&UC|JhItHxRrsT^etqCp0vGQV7>U=W*t}$JGv>uMT!NT2}bGWJBnUA27}AGDFZ8NTF9aqncC&d0JZP%Y@>QrB?5Q z_K@$PWQY2GpsQpGl+dZ1{Y|3!K5$bNAoV&((NGvxC@K&WjtRwrWyPA_Wrvt9s9X}< z5i)y^JU8iyz?tr{3Q#i-q7_;HMVY&S$&JB{*@{R#-ImjgKOjB_#yxi5MsL{u1>x=& z`eC+*V{CvhGYGZ~+b`M%I>-S0TOXxn03&*k)v^PQeV1%gb8~N_t8tMHEM!Y7f(cEP zCej@jSCzZMRpqjLU9p*870u2S!7iv(W04^&6b=>_i;Kni)NFpXFi(^}$`|ev=Z*8B z@$_WwhY;ou^X0ROt>SDr9?K;DuhHaael#~xkRnVSrUqAyqp8uFFZN-VzM$+%KCc-ZuK_eIE<7>q+f4dbi+fD&ZB( zj+r@^&>CjvoYyd9!_)P-<^n6>mCzbk9qbM^XPf_pK-nsRE*qrDiBuJR@7UCJpEleC zj@9bBE#c}>$xSnj?1e|4G44-lHrE1QV1V{54a>kY^-TXazYv#A<(J46i1%&N`Z-fW z=o-2Drm_T0+G2kC+-QFEZqkUBT6(ZH zJ7sg>s6ruvN~2TA?o`&bQVsh7<#~l{o5f+HJ72B4DD9E1MJ%hndA-oJyHKu5317d~ zva_x6kx{Kk*Qavj5m&9uh^xjE^KpQSy9mSZ+NcPl&2sj)9bhJjFCq@8KG>oTy zCYX66LJ&$2@SqmBDY!hiUnsl&de|N-2y*=MFNrsRDif1CFrW|-3-xC%{VxYo2gCKj zzKOm8uBfH-fB;22A!a>e2_r*&ef|AoeIrv714BcPzP^X;06{`5igKVKn9$h%8JI|z zu3nARzh5Pc4E7I9tP~6kGZ5qTL-n>GO21&H0R9VbSpU<%zP_oyJ|?&rIKm6aA!Fbx z4Gg@06I2jzJSnj8Ez=_7hZ&18jA@lV*NAh}zgXb3!0^E2!0f=pz|6p&z?8r!p)R3_ z0W8rH2$)`tuWyK~QRu~9KshyJO_ZRZfS`~dc*P`=C_1qM`oVYYH~u&OgWvx5z<19# z##hhh`*Hs`gg73KxBYJaHbf_$wP)R3e;|Ynd?cRw4u9!Q;v?ze5ebMG8+eK2H}Fug z5wcR#W3*JYWwsXAC%9O-8M+$VE4*CYZN47gFQ5Rye!>ESJ;VgXdB%E&Tc`*ao6DT7 zB(o{4F7xq*lF8pSy3MASZ!Xwuw%Z*h8?l#OuGd?m3dxC?9=(PJf=^KmG@-E?FvBn~ z|Bm!mjusiJR+rMVAq-EJ`6MhYb9`UM9_IBsVXYqM`A2SQ?o_Ir3bC0)c zzMzobOXZBxnar*(gh%C2m>6(sfh|D+hfpbd|6O|lu;@1!J;8JrY!HwvNNF69L4L&8 z?Oxa_v+rJ@yQuHpfE!G0bub{NWOyC-^&C|Tw*@hjlrECkq&ZS(Fc(Z_hy3}mU|I|Y z3#wsPLLD5)YEYeG8s{T!{CADsW6GwJ2V(x}=h(F1)Z7I&a`Ee#tjbpHZpRY|vw2$f}2 zv&^KAg4qK_ZNJIa3DzaLStOCve68I~}-g8XzRAkS}a_qwDwT-xMnZsKiQ% zzgHxPe7D4z{#1c6nV?Wpxxf!yUX^XMg#Rm8xOGviWKmw4b`hJm zj*At?74aBjlOsPWooNZ9Uy)I)b{(E>0m)#rrzB;b_dx=3PM653giv3q|5a?eh>vQP z7Y9O;xJIGs@#|92j-b)hjGnG^>(W^CIPT$I;CO1rw(H*h^a1OJUj4g^GQ0g$QG04y zR03aWOMWP#co8NFlkdzuyb}g-Vp>qUO#wWQXsUqv?@Sddi!Qd2UEAz$DcN($IWhd< zXXR5jB8@!`Xsl}SeQUhV8ml9|AkB)c?$rcN+zJ#2zq~xR91U`q`=<2Tx4Wrly8Ksm z0iFYhyHZN+^;Q|hLZ1y3lXWm<6?60gs>?*mQu8!fMp>_A6xMY&8Af5R8HwrdwDwuz zXU?tzLiWqfG1+%K$AzA_%_e*T_G%&9b#TW8T>)Fon9U|?F_#NS7TCWtWmJLr7RHZ* zZPit*z#6Q7A4(#|JHrXjE0J+smY1pgP`;NU=yAqMB66=9w6&4lEVf#1_Wrr*ZD}%} zg;tNS$0mo}GWfM?gfG`u0)SIkK_I0sugMWquUza;;`=*b z?sHDcE-CrsGP3y4&%SrWB_UsX@oaHS(yr)eiln*(ZKm^nXhq7nd=_<;q?{dwyBry7 zHHR`54@4E7Q%icpwzwXkld7t1NBy;Y^+vigUa=Q8pIqjJaSf)F^#~7JQK6KAZ%!_{ zKnQC^F~PH+2!hrO9cqJffw#08`d8qIfelR)>sVWZn<`^P{kY9w@xI-t)c;bCju9#Re_#nObA9moX}WoqcxA-!1}z;W9`uP zc{qW%j*xt$VY|$Zwm{x;aQ*0q2ry%WtE4AzeISmIc!|Pw;&A=Mj%+|ZBw@SMj*y0q zkVuZUAUtGYyHK2! zp2ml7!EedX(x2NzN`7_Wi}*2{=?Z@P14@1^;fs1SM2{J_C9Wh#Dg92{^Zj{O2G!<2 z4@w{a(Dye0-hI8q2g+M{c==^&lU8fN+NPt`BC)ijX|B|ULK?e6fRdZG1X~@Y01c>~ zhUiBEi5iHn%1?zK2n`+jQ9)5rJ^1kM2(Q|@%1(ukUh~^O^D?}WN}*4mzh4xw61mNe zvpL_hnFT>p2t`VvkP*X3l0Rw0KEbaOUV`zR@=!zM!LRoqyF_LkA8Z18y2X)@Hz2P2 zAAD-p3|zUVVwn<&I&ak4HPYSp{xE&{fD$NLk770`nS-kclU+>*Q8VOSp1y>5; zpbw|CXPYA1O%KUcf}EhbI~5gK7c#TL)_y#Lv~kt>9xpaPHJ*#f^qI98q3izXbyayS zwh~uby|(9WOT(~+;{2opRo(?2bpqh0-0}!@4M`UQ;O$N4lOs6OfqcWg&inU_Pf`a{ zgtT_e3=8>Dbisv$`1+#6$Ia7w7xRfTC6qzQ31d|3P@s@F0-*+6Jgb(lq&#FKK!G|) z$w|rj(qGzEF}P{AEa5&Q#)lGx3zfP4#m(*o;a8^J|HYTQdCTr9z(KC`Hryt^-?8Rp ze69i$hqY?eA00@#ho9wUye5|x@UHwIU_b7JKQxun?0O8kj@_fZV|_STb=v{rZoOHc+!qCfjV;Zkb_qA=-_6S zKAQpGcT^$5h1sRecx*c>mk+PqMA~`HO}P2a;d;@;Q9w&EnRiSgRKg@^v=neAAyAEL zHrzabSS;$g3IabN4k30G3x@MfPz@9%Ld^!uB{EPf2qEF5>KS04U5z4%q*v0OT^18D-B&>}xj)vtyT4!)G9l!j6#^TK$yv>mia47tLAiRPM2xD% zU~ryzJ=g8NooRN`)$FoF=JdI(&hzjqC?ncPQ=GqUwR)!SFw>c=WUpQy(u?P2V>P(V zE!E&YoL%8}xYo1Z=Y`+#01_$e{_F@+E}P-wX|`BLzWWmczj;sNYU>Snsj51FFlfBt zn_CNcD?;mCswU3fl?sn*fZ{Ph$)#2dzXrGxsuJuA0L2QcVo)FnMilgj2y`FT%tni! z5x4z%5Jmyly)Pa$F3$8{VX6}sZ0r;NF2EWfQID#d1yU(n41YR);}~(AQ9=BoHXh%g z{(5_?pT*-~IMWOJzANq86WBrYvEMfNZGFY zs1H4Eht{uE_sedtLE~-@{f6Uuic#1KJfS@(69V0nJZ{XkxFhNeXWx{Id<1{E3A0~j zi$U^mD!b4$JyNj=+VFtt=u;akdVx5KUkQ;RSYJIkC7rpN48a4JEvrgS=@onI&+6^Q zho9|0eOn}oQTNAeU*jG1o!4EOIz%0p>G-=Obl+b_b$~V5QhD2yn1KQE9?qEceiz!` zJFhTrpl_z@cUkT3F6Nue550W?>UwnY$=<;_o#J3U%8mrYh*?b0Y&dE+Y1_);(OjAf z6H+#Y75GDXv?h5*zy>(Jjz6??sPb z%`S2C_ya~8noV}eC85{gypkb*!JUSPLAb&1-OWrlzTqf|@i87Akkf1XJLvb`7;2Ya zVMi;pFQoixdJ55~T+Pq0gw>$vc)|s|ddKTwR3;OV0dkZr>p`4OHsr_1+hGb~qzG0E z6JzmTu;N*HBTE*GM?z(*f1yOj3Yj2+XAL7@Bc98lo{kVhjD?Ty-<3lCAu>=>1W=L0 z)FymW`MIBdk~>ULyH{&7U(Jy1)ZMzt;SGFJJwtiloYQlF_U zE?`ct>qnSj`U+bqs~ z|1p!Xb*J;8G^tYWGhNT|dk6WoO&qQIW#gk>J?~tH%WdUfmT8)roR{6l+zBOoLabeY z>%l6Yx+1@yo`?=kfL*G{fb#iNk!OBR038c(+P_E7%55x@7XN4q{Svtu1DBV&pnERw ze8!wY&|@pJdhZI3x-xzWo1K6h#~Fb^K+$P775>QQp;6loe>=o_?W@o3PR=m&VJFI3 zEW|qNAQqCspB;RBSq_vEh=G6p_Sz8=uy}$vk4P`K0$j)2V4`5eXP9d=VnJdeP#l85 z?<2+F=Hgpna+v{c$GgAAvVHvYsPlY`z7hy$FV>!9&a3`8WyU4yc{g;o1a3U_L(6Nc zXIu^;{@&_#pFkPKaMbJ}$crrg(xR<$z#NmIkrF2TGK6B23&Ko7lsgPxg~_7+mA#6v zsigG>6g;ao5LG-tFwTi&v}Cxf9T%-k+Gw)rc-SC~9i0bj!cSLpF{2xG5tVsC+3Ubz z^Z7K9x_gOv=i^VX9q&t@vfKB=?hgM5y-ss+llM(kqQlEer#okCFZq}E#VG%kyVJAY z;p|mv$)_899>+(h1?+TmkCA@d4&W_Pr`wqB)L04CjP3qdhCcK&`3B=obaw`5b3WQX zVkhX8ogNEefr2l;-#I@3ms1gK;`zjMNSy>vq*|m;#lfEqylK#N^m1S<G3?Aw%$&3zL*kWi-?brROGT&FMbs;JioU-C7UJyB{c;t>*teO^7=z5UzcS zp~2=c8neIhdga#m`2A}&i8{~guD{5JyUu6HL&<0MMbd>hRabEfDbmC7MQv`&wI%E9 z?}d&bUK%y3N;d0MpuItD+)RcNo3EOWsH)anm3=3cSu9;`yQ_%6j)gvCbBr||qJ}~j ze<R2=eQnzxh7*Pp_9EwiMQLJOh;M~#tw@s4Dt>zE(4$|$i+7b)~a1;%8I!@ z{LN7Eu)jSP_@o10^_5_BnoH)99~2f=08KKPEa1%~AhaMkv^;u=sCn1Y3{0E=j&GOK zX0RkoDE_1sjs{0lTb-?rX8OprtX-K_4kWlC^6H)gHK&hcY{q4TC?DR#o(tg=LJx)K zAJHPZLven5vWAbvzE-PubE#{M9f0#gZ*1OKh)DvsdMWQ0?-}W&@2v8daUh)ww$t8M$X4Bj<7G z=n;NC5PM}b_zq$E8(c=yJMS`hd8Z^welnP?*WV)+$R{BN^2t}X2`mGxMRy}&u8)V? zTo9`8fh;&}>S(AP%{yTTJd6`TENrTL%ku&gT`hwiw1M|w!+k%C`z)tL;YW}Mojv;c z&PJ=*6p>`Ny<28MT_QtD- zasNV79|0HKtUMS#%1qUbHnQ){Iu(*P{XrdvdM;koh117$)f-Zv4}LnPMS3k=%Vk5n zwQ9ZV>v8aU?2a9Oe}q1*i_=VS((-G}^|ksWZEa+JKM@fnA@QJaR3OqyB|!51w|-9HFGAl{3p zzK~6lbs>Ty3nstVI|YtM_me=3;lVnX=GxsF^{YkKn#o2*DK@YSUW2;+h~@)_$w z#8=Q-Cofe38R8AhB0CJ6d$S92nz+U|_qTlCGqeuHXG`x$YJA{a(|F8`_;B=ov7I&ZYbk=|c;`t0=1pFG$|K za&BUxEP|uv7ysIIM)BNw`(?UDm8N~!=UEH7IKvWx9P@-ZbzKOQQVL3o?% z7o;eYt;BX%Ism(ZY#ModCy)<8SVyHoFVIbWUfwf!!!F)ovjm4ClP*RvCs$;^SFTln zvS$y~mDs<&-ZA6TW|Zi6J_>r%_mJJdV6xKy3XJj(eLk)QGJvy+x+u%}h@4)>gXQoQ z1%&3rLHk}&)FH-{0_I%n8$iIGg&Tlis3&gCf@lJWNR%4Er7Jg8|cUkWE#{QR4-_nKH|J_ z?xS~6K2jIltSd|HY3yHD!)U%j6QkT92#h*BOut4GiWXaxFxP%DAqDKyhk~SOUAltA~h@O`$T*nTXn(z%?#p z0A~U!v2^PQ!;%sS*fUSTH$P7Ur1sPDQoj|8Zf1g=dY$&qJiOdKwZ0eunqM4QR*b8p zk)2Sa^Ezgn8Az$@g~?ZPy+2VGsDINM4`tjQtl>Tz32u8OPj>iz1w#dh1{4Wxc>TOUrO?*}98%mR z^xx5mn?D?0BZG9XsDUC=%#pZDrW0L8vt|3_EGCS$=tl!lkB{JGB9>7CNIgLv*OC}o z#lJZ0J&&;C^xT}huT(2*JO53UCV81{`Dv+2OP&{E-&`5>E*ecXBU3Yn!IgKNO`oUY zW_T?>f~yc8CwMKV;lDVTc|8n! z=}sSG3aJM_)W`0tQ}mHZYMD@ksZgsc5M*p|rPe+8Vfvn*&NKvtOCv?Fyr;FLm<=!uciogELSZrm%?FfNUpXNE^- zNN3b>>DhQ`=Co{z*a!Na0j}&UT0eqC84SX&4Ek3g5nSnZqC(=DW%JsU+MHFoL)73e z?E^4B{H9FU0Us0CTpoNkwodJBdj6!4B+(cOu@&+C_En4$RAws&(iwP~L^l!S+|IhM zZ2`Ed)5$KU*RN}2PP_NiM|S%6U}*rD`^C(dDLDSXl=lxK{<3m*7@VSPDx zAQ?EWnk9be`0RD!$vAh!H_g*dl-d4zpBV|~4VVQvJs2GVV>}d#JCr^;GiIQKg2-Y+ zO7Oy}A)^x-=@w+rD;zj(lGd1 zHM61_qgG%9S89sAz19Zv0*B3Rl=szm^pjKZ8}5~O^tMf_qI=olr#9Sy9@ZbnMFn}7 zc0Q7^zT}HUWUpJ@wV<@!Bn|Sz1@gns{g61i3nk+R7K&(gx;*8Q8qlwOr`OgbOR*x+NcSvi=3kf3{M-HV5QEUY-AlL#7bC0#nRDbx!7w_1sl7DU)=@UWWd=P^gzzjmT1^w0nIs7xG!xVhWnTFDgSwu02 z;N5US5YR2BM9d)yLL*m?9-L*fl%9cvq|msx$FP3wCwXqNItTM8zHU#^3BBD-AE}H* zQIlwK6wSDPp9s0PYL9Kr=&iM0A88x2RoHy5x%kIR%T%t*viGS(r!0p8tzq^dyhuZ) zo~Go8Ft!kOFj}=ad&;ti5Jni+vrt~SN#@7-qxbriDS~J7Dg1O?zlw%lC?L`)m=gIuG*}f+t_3S=fkJ?I?zH@uC?%*!y-Qb?mh8;EMf?aX(5Ec(ve8!3jb&;dS+`U|%|yMWMwmY4^!5hfk7>zg2U3iu7V z5AqBxrY(VHjI7aPiaHx{)7c=#x);KI_Nv4=?JoIOWYp7Z2@73NW)e62 zKSOs;C^VQX4;6O#H~6IRlw65^l}3fGaM79&cqMZxozHQC!dcXb4GvgGykc;) ziTBBL4N``*gm)=;`N=H%$WQiuTy~B+Z04H5k9!@ubsLK<6nEBc58HUPxmYftULyB= z>{8^uY!Ztt~E@3*HqNkT3%(Yk0acX-^?ICTIk@MtMRTL0jeLH5{>!z zo0leHM)!UrXEuGthl8Tq^Cn+4&Ngu;mH+eRUG<#$ycC|cYGtA5Ex$N-(W`W+Xe{YS{2AoZA*RK{9*x%LxUj| zJ;t7-HlsW7N|_Zl+nFwUh2_tSCtO?E@F zrO|wp<-QLtW0=_(Y-v>Cfo!kFjH8i3rK-h}Vbb3+Sd0}d4pEX{r{dY9GFd9WS?o7e z(JwzxL=JaMuz_44eN|boc4y(EE`)KQ`&4yN1G}(nm@x$z?UYIJJfW*4kmLxW}-0fuq?70&{BH%2f5T;75!P~6r?4+%8kV+n9?f&&kI8L zJgY!*8JTeTO8qv&%?*g;6P?dn3V#q>i^!+~PRhnI``A9zLq5{Yp;b(ym1Zm`Wv|0H zIZIjq*g=Q^j(pH?OQ2woJVku;cn}$q!nBc8a?8M~`U(1!jMejV2)N>xnIcvu1ixaQ zx%Z%8YYP~;%nOu`7z>H_$0<-sg$Ze?X$X7HP^=TYua=)I4JLsO&I^Cl6g8{SKRmPc|2c(cD2P_!cm`Dy|{-z z^d00=qpl1InE@ZwfTS0ahKE&&j_n?mNr|Jy%Q=!e^4Zpo4XJ$2rzL44~~m zH_$)lL8F6k){%h}a;?wIK^(4F%g%>AovQ0t(1s&}m{Ayy+Yp;=2+YiLs>N-$KRixg zPu};nI=p{}^X^5%&f|Y!_1LS%_EW#x-&daGOVsnc(u0USn1Aah;>_`~1C zWE_tAO*XZ@J_ysmYiwRro}9@!jBrnck5$wmSb-XQ!I&QFi>?0=o-K*b$7uX`0>i@+`naTD%f&K7w6037<<-<9QDEj;`ME#HzREV;^pb z5Lgpr2A+w}-sR0dcqClOX$@#Hm*dgU-TB zw6o9HDy{dOmhabp!<0q7?dJ;{8Tb7-`eY!Ra(%o=)4v&30;B?Wv-~Zi%f9y(zZXM9 zL{!yO6di@)(FJIqiHIVpVEGhI*bRy~I`fr?9Z0yPTbwNR?sPcEbP|uUo`1VV5s_fO zsC9q*vDi^=5KPdHzS!;MgRzn;;l$tuUqS71b_Lzc2*?|)E)0q2fU)`qpz4I*Rb z0b@Sw&71Kq{|LA|DE%#`vFQBv>DHp>vJyC8@U=eNc)R&|O~UC{i_b;SNKjaQer=ZWC7yHO7VvmsHFX(?QK zmek=hW{5o(x|9!F6l~8M&b=T6ht^DKHB2<4^hhvMsMU34SGh8JqYPXvgS=ma-irTu zcKc4gBd`LF7Oe+uwV+4DkFu75|CiWj_5*?M!s!4;8_QkB*M#-SSd!y>+rW5W_>w_y zBa#~POS*5nxgRHO99GnI5_YXhaarFsyofnKm5#{2Y>n(se_+t$y+gC8a8KH^mjlhL zbeDO>Ue7Qp7o&m51LXy5cFKkb?n;}P>@IcP<}rD0gNg58QhJ}8+YbBHp!UbY@TG{; zPLvegu5bRJQ8e867ijeuA=Y}Dz8DZ|zg@lhRPrRJI8VMjG7enV3p7vD<8SYh?8nNF zzeqQMElGq!gxCE>z~UhJWJfuGPSl4Tu9j~Cd9oV`BEj$!K=8VE%2Z$XQe=y3XyQ*wmGKaRLph%}V{R-jNOWPfAGiP(Ub&CjSAI`jmEYsvK#u&^5bV6WnoNm(IwX(U z$CL2V%9Jk4QN}spFauZ}N6Cb=3DQ?{x`>ZC-x0~kBQ<)?EKGOw>kaAcm#<3!)S&0i zuDmR=CPMgXraH}J9>~%o@N%FzBzFTP1yzhTCUHll!ZjPVsHXjae?>T2!4L*e-Wqbe z@-agyqV7c)@aPADZm}j?ZDgJj>(aAoCyQ}$G~;ishN{KVRJiHiLknW^By>IJGD|Ai zZTBUhnr0AQkON`}$!o#)6ARpU)5* z6vT2E=19pho$_bUc{$`15g(*fP_Z4zX2N_*NSj`Nbu6B}2n?!$*rME*6FpDPn#$J1 z&_r}w%_Jq*It+!w6kI+7nb4=3h6D@O)|$sawMWL zVTP8tv_jc|kjzy>sjg)I=<}6|^_~2+jU6`C<~G;#$E9d&khI6njI?bZITYs0HI&i}WM}>hg!CLjLJkIPUnEigK41yjH%zvgDU@?#hL_@+$jRJfs`-()Vl4T| zS4iVvN^y{ErlObu4-}A(LZVkVMON@8N=G3a??~tWdct+nPjoq5}$hg!pS45LCtF) zv(pMojCI4~V1~w>gLEGGn5LeW<4ph8e63k`ZjytXd+%{)Lw(Y$w~~*3@uqLj_vm!q z$4Pb36u+$~)AgZSL*|!|A5fcIewiTc$nbi#DY7hI@~MF6n-LADax5?n8JPSXQ9ILb z&m9&u-J|=Li$#c=H4Dxx<1};9cJaHHzuqkhM+GmI{SC0v*qSvK>Kz^$zF&!t(zR_J z&7R{OC1B!aG1&ZOSF4OpW8w?7>Kz6aJ$7sBCN7O;Y;+o}L+3hOw&RD#^G>F5nC$Od zs|q)5ptxg{Q38mQunToi3o$im+grR*=#isn(`c-=X@2@)b*r%z14F5uM$hDbgCCj{vJ&>Gc`%xw{}B4 z)zf9Kw9Im++;*JiwyCSRcgf?iPh1!0^_6w-7jMa02)2W-wXk6S(8VG3+pM7jvhLvb z41CciCIYAEdo_!aKLCT-vORl7p(l`bZYzVk&x$Nom(g@Us;kFyYObOF;PkKweCa~LLG*mauLL%P$?};u>>-OqG8_dgB2}y=SW!wZ6j8KN zF-64b$xG;1d!g(KQNq7-Ote@^*n*efBEvL+hqQ_``Ob)W(*s^kI;kH#`-LIen?_EV zCoE=k_)Xrg{qo;RY4#YHg48@+4{hP=WHp~(V1%f#q9e_fD3lr{o1Dml9^ag!W(IOiQ|2wR z#l&CU!+5I>6FoE`*>Ohz8D5x55Cz$&ANT5=r2U!sc)D}WJ(yV*51E;zc#p2UUHXg= zx!ebDBQ^`R7&M+Oylt|=BS*$Df)e(dFmfhFz^wI9l&2for{FzkH8g-ELdmKP&H^-Lmk5e~1Ir`yjaA@$OFcI}G&6CE#je3kV{2939#MSegRv>2Vb* zlb@U&H1Ie-4>|#FwFjy~JUpRC_%GaV`k@OI0jxgp(ot% z!9=pYP#g;Ef|Ik&VrHMZEX(Any{=viW52OgYlLD;9K|Zbih>}$70bKV+22enhc#>S ze*WTeBc?oT2zHCdMtz0g?DH=J^%6@Csmn!FbLOS2GAUl@cJ9ET`|Vk0B0`G+hgm0s zv&<-D1D?j(?XtoD6s?`qX}nfWeIJ=xy8K&yda@#eZ||ziwmXfV-@+H^TD|k*>u`02 zIuyp)3m;D*Jy*A(-2o1Dy!Iuji_)EKiu&ZcUya$5&AI?bW!FhWaP?qFFGeS7)YMPg zDVqPc*8tCM3=x{u+{bR^F8!!MR^p08!P4Jdd=}~S(D7s-GDx0)@MJ9fMhTZXyj&;6 zd68@cZ@5kDCwtb))qmd0H{=FlpY-}8Oi=}VQRc%48QV}D=L`BYo<8xsz|lIg(EUqc z=co9+GuF*>+2R!=aGe-itUH2}1u0#;z71`DpB*%r_Z&uuCw6zSEfJY7j<3SnL5*se z_6NHKqj3iZ=&jd$r;-#J^t}{n;Arqg*^Pp>C(m`vLC(F{oAy}S4paM$s~?&AiWn}e zN+}ZxGAlOa(Lkf4NfN0XA^e1o(G z9XPsKq;)N{#nBd66~-eKM>ml0Zk&=rWJe)5YoVedaZ=j8VU)l;+(hL*80k%Oic1#@ zOpuxV!H|SI(H*9IkXm(ZM$)p94)YI%^|JJy%i8H~jh~Y5!HYDPEs;3smY9D?^1$9F z2`Y9`LRGsIG~)|`2eTJ6cY_cHg=NI`xb$$7tncXa=$e}ChOA6=Ff&-c94eApg5VQ? z_=16~W0f?Z{m5NXUlW*&Kwm`XN6gWwuavp9?vmN!cNuZg7$3*aZF>&}%hIY7dvD~i zerr!(cO9*=W?j3VufQIkn9h2fiFt;GD1cob%(ykrYhLtc&r(tJy65qnuv$Y9(~eFw z>J7VE7GFBf__)L5G6_Fva_JGZ@GB!CQHQW8Q*m*lX7HR^-JuDUvNXLofqFf{reUmx zk-dzHVLfICBQuis(+Nlfkk)9_l43#9#)p>q=<6rCRIN%Xz_aZ$#>z*?7x1bp(hQd; zhy-L$wURQ;1CMr^i3jQOo> z@gtZPnDwU29-FtDj1|W2Op2FHR z^Z#uIegliC+GeadJ!dZ&Q6FrR?b}Jx@l-5fZ{#C~7 z$|spyp7Oph3CBn=CiEjHh7b{1^MrkMKi8ghk+{?IU2vi%WysV2kt9FK^R;1$4n*-I$1~r38X-l0?G~NP2G|am^2P~N~s>muuWkb^+ z7z<+k_1(Z)xa!qceVdeOI7xf^Yz{`j-f5IZkx;_5xa79SI_wu?p*KY=LFAdb8`WFp zztAG@4I`bficVsJD|R|R>RrRzj7~FR@uE1GxB8(-z#s|B!?^Jflof|$mDI_jDH1I+ zTk~z9l5|}a(&h3*)UCgY#Lqw20^g0>l#-AwE>qM797yDlA>NA~@+rEqYjf}Td1g!tP_GoXd+zFY?SK%EG`yPdAmTZLeC+Ij!Ywh7K60tA!+sXNYJK**Gznb|@)s*T7(w6b{07+ZW-B{79Ihsl59`en&e6Hd{KLlamAnw_xId{v{ zH*xno|0~!?M-QjK_(-!uD2f4~6F3*>HT+ou(It#a4AA{4qpK7Ic}h=B^EV20cX1Iy zz^isqULkj_v6IGtMRljeJpj_h?+q)v!nKL9*7qMGAjotufsqoFw05Y94SO`3_l@-S zs|kmCna@u;3nc6+P#KIAK^YLoTD#<^>IC+-C|j<0veL-mt8JE^MXQE_ezKv}IOufp zSXr)4;D4Ke`@PXB(JWKy;%Yy>VeF9>SZ1#5%sR*{zO>W}lAH3ix78v0ke^DT2%TND zfDu0SZ)l_jmLip8BiwxQp6LGpWu@mChO+#$R~@J^(Zt%&|Lp#R*8Nyu(+<}F2H)ebZno`MP} zuDWr@@h+ueFM~^s6H=tDNJq(de`k-b z58VegjfB3Hv)~nwos5Bv4F1Yw4_`2f0_Q+F;(BnWyUV3Cuw3=8<2VzqPHQd+z`e3V zAN}qLv`(Ib_1U%?*c_3Zr*R$Hv7Lr7)n8$v3&ZgK#vIKx;MC*{G(Uw7zZ@j)E$!|F z0qTYp6`zfHMz1yYhG0W6eXVj|8YAIwf|V==$2KL|Sp0`Zxa28Sa$7%<1^FKOsO&J# zDl&O_Nc*IH2V}w9jn5%J@&1G8TZ@mhDTkBJOO0kTs%{gG@8^$nF_3wCKMj;24z_UA zZh>%Z0x&%!OD8thZGOZnL<5!hw1rxEPno8rXz=}j9N5_jOnLe;{-!!MXJMF2BUm(h zw6-=z{M=s0weX9c5N7eO6MXvFo}=Z;vP1cFrYc|G@zZ+bEZguDW`6Gu-_`g)RNHoZ zw#acWc0E5ole`a5um2MZ8T96UX4T57oo^5Mc}z)u`mmykd1ci%mbk|h7LAy3!^I(o zo{v2jwTIvL`Fo5PSTBX>pn9mD?phi1rAuE!XnR|qG>BM(OfEI>!0D~ zG`b)nc|DJoG#cG_2=%+5VNlS}2hkYZefiIup@o3{}WrFodHLsi0yEqEgXgCoTb^7qk>u#vodK z=;18E1^M2b?7o?O($i9XPG4^bn!D^1-wi+N3U62N%kPdKy~;uZ+|Z59A{3+yL8OLs zN2<%XUNBJr7=oB6c;xlZrfxxR7#PFkWly*DAN~!Yoyz(Pd+ra?>9x8Ba49rcuW7gp z4nuoxOt-Or5|04|x&3K&>JoT>H2^%s!+a~m00SX{epp$%DF#e;A16qCCP!c`CGjJ7 zr>O6X!T0HfPw}C*biudk>PGIiGCd*idS1|jxNDJ?=C~q|MjN4NG#Q9q&sWh~t9al^ z9noqL(80(l$SW%t3Zo6YVCXp-8w{br=<-Alu}~B5p_U}%!OLF*f}SNqmk8rhc|I)l_oB| zj^K=Rmoq5=Vn>rMRi7&Iz(QKxW#(Lvg;1Tp#^WTC7(S;Ya^T}Mhs}N2X*2tzxqF#5 zsDnrMnD@|+2-W*1<@8D8L`^TqN}y*nbgy-@0`+?pVO~zA5RZ#4MCeq`(sKKeBE^3H`N@^1Mo3DQC4$2 zYE2X?&WtSW%%AZ|op88uJ>V?p@WaRHes?gx!}K9_cSu)IRt5^-xB!kye^)1*L-LOb zoM2vu3)YHv1w)qvUcR~>pF+>D^|Z+Uh9^_~$;#ypG_>pjz{OHvVu}(cRKT9B5Iqp3 z_NBSSq{IYziUHbRhpDFlqj|=19PEd3gPan^q$GRX$$eA$THM+6j)*jmFPa6UYB5Ep zjsm^qv35~Nq$Ra}!R=T6IO_HB{yXJgU-|gUW#4V8T9qx@rhZ#HyJYUr(ZfbuUpz)g zOwE32$e86@TV{5kE&r9*9scBl$FXT^QStGq%Qv(;=Daj*bVJMDnd2MOz2SE$eiNg` zc*So5B<~7#xdeL`BuQIEodXab185js75H#080ygyl>bL#dhZnS$Hd0;&CKw)QXMJ4 zlv%M^tYkivGh)3zVe&UY(KSyXTA%JrR^n*2_LB8-^=u8YS=?!^RJw^OyyhP87Stk? z=g&!wSK?;~|9C;|UG5#EEeJ9Qb7Bvehkj!)Gg6aS>P2R~!cBv>eZJ?z;X# zd7D0myg=K{@>gEFapor4ayFoL_BAsLmi*&p1AZ$eFb?ZpG|6R}NX84SCq?0}Idq?D zLo#q}TS@{u;85h&6>LZ8G`78Ut)yS_vF`mVew{5!kw=zUSc=f~Z3!{#Ktx%K z2aGThCGbi+C+mGVnU{OAmlfGVE4t)*4%rd9ZeLn*JUc{D7UT|s4>QiaEhppB&-GZ0 z-WH^f))`J8zT0|Qj0nvP*50V#!!34i>*#Zt2YW0eqHiCk)1xefp4PB)QP#_%(1vBn z8kN0*wG8za!Dfkq8H|>Rrub=Uj|O4Q!A2LRPJ48_*rI8_ig& zdDQR)BT6gEZx}g}Z#{nCu)J~qqqNmggXH&@Z`%3mtv`YLed~|QYHK@b#CM}n%U=*Z zX%CX8v;T+gf>1?uV=vSJjhM#h!5of_8NWFJUS}eQ| z^mO3t=VNKRx!RJSN@*(zVx1QBF{z^7j;&OuA(GU2NxZ^deY-x%ZeY@Oo+0-bLkmQF ze`btw=RA8IYSdH0$Nb=Mh}t?Y$oj*hJEagb+r9Bp@etMksN2Fy^M)P|zdVHewu< zV0wV*4n^C~%zGib_{qgDpI(i{J;$22{l+fhIN~MK=|voqUko%4zpi}5h*@`4k~?be zi_N-kmu+-e+30`1{V^V~_u+@bZsy2N=hiLy?&gLoam2e#S0_HOK#i}JGlQBQX9g{> z_zAS1k{uVYo1bZY7{@n+9~aO#z+$m5y@#=nKgl zhuwwj@F#_}Jt1zade+6E;p%nB;WbTC@XH*4oV@O?>u0ZCHD~rc5BU1@Dd^w7k54!} zbH&m*vu?R{W|r5Rm6eyrdgbsSm~WYAge}ejYZLV8L9vOj@5y@b0mXQY3SBRR+T?4VC`MwbjsPVFDPtAs!4@Hhr|alXTo z;`PZ#x_!R@>iQJ||EJIPa?g-$f9^XAa=7Xoy!V@LlyTCEKRr&$432B%-XQht4s!Kg ztzaQ$=Qk`^JwOXEiGmuIc{AFE> z&<2A)z@Go_?|6VE)V7?pf7O1J0U>n#d@Nf-1pPiB<(q(%@*+S2Gy#$#qzJu^fui3B zq#)x^evv}DuBlfB++oOlC7)GM1o(g>Z({I`y?oyggKw0KVepluI_R$=973F&q7&Hr zEeTQp{>`6I` zXN1$Zkop_3v}V=J>N(9ssk<=qv=NGMLJRIu1sTU`aMkD4`dc!tw{ly?V}T!l^X-51T^vr#*)Jaai7yUb97j+; zQpsfr`;iWr(AeiAz<;Ga3^i_c<%^U=q02WhaB71mp4sCA@M`sXy-9Ck-_Jm=u5?QD zd!g9(GZbUmkE~gka@HZ=nT$_ie$hht{(;dEgP$i~Y}xV*$qKyxZKZA0G4-Cx)8JR7 zp~?PwCq{Y~Y@Z3-D>D`azC?$?+EYzir@@@0^c~V80#?n+`fOO+Oq2+^(2<--i(6RM zIWmH^HVHgOJBK5bCS344*gwJBom0$CpSOT^CKjOJ9nZ_BJ~#k3dgQHoBhGZo-_^}n zvH9lrfNd1_uR0!SeA?NZ+lAn?{3HO*@d6w zBq}~*3ppdSvwQkt&=Qsme%^#>gLgdr4Gv_T+D4$|IeO90cu6GmJX^2R2t2h|%Kxc@ z;L+0F6rg{za$n}9o~-j*H5yHf2B-i#W1&TeCVJ<&)9i!*9(clOr;U*DtRK?nYj_?u zn`75=#j`i1u5Z>Uk9*loND{M#5C8^WD))HlFuTZ0tBp|Z)zB+9B+-jcI`2kbG z&S51co_@tjL_g4cZ1wDe$Q~c47!0IGM_g5;NEo?IrqFAHme3^{HH0lPB7z>0(^cxs zL`BM{3>L9EHnIvuM*fMBb^dgWhL;a59z1AZp>mGfCnMd%N>n=UaT|aKST1vq8~tjT zZnwHQLU(D=vZpTJJaNej-|(Hvf5(;&Ei8{PoXRLk7h(H0NZq%?-F8jrZP$!FK2UcpOCh|m%T8%< zcXCIPkVF}c#?tWJ`lB&*eh5?kXnRcmm+irh|J$D65wI!$tIc3nktsS+{UhxWuu$Gq z242Je1EyXT^8k3-V_;-pU|^J-l@}a%J)Ym@D}y`-0|=bGD#-<-|GxPr!ePx`%)rdR z!N3F(1prZ<3$%FJV_;-p;OPC^03;dyzWMu-!J5oks=Z-l#&KQ4xxAmp@@VY#FG~hky1hs z5sx7)QYaoIr_w_S(uPt(@ghBxQY6?+-|QL);^E`%{xkpV&wD%S0<%K^WE4=Ad5q~d zXu1s}&#Cvw z6S6?2$fDh^(q_k=(MKPm#&0dVo~g)Rgz^(5H%DD0DTHo??>h+jy-?M9ALN|%0HHsO z&?9aOC8=KPcdjKle+v8VYivpb4SyUBIWrrwj`uQePE^f&)fu#@t1^vIJ!$5o;9SW^ zEXfH1-KN^-msnC)CXmNwQ@$WjE0*4+Y{bug5`nGDk?k|bwuk2ix{13wjSSZcGKS~g z0?LvyyE1Nyx@tbFmbsLyb4uNfyo|gz^bS?}_J>-GeREEA2cw*A)7wW`3%2DI(oqk+ zw>5$3>b&ivk3*Ot%iQ0QALiIiVvBySJ5}?L^)>YyZ`lw34xV09(TChe-*3ZDFb`%C z1+Pm#+i?zq#5qLVw<>$|q@Tl0>_2vd zi71Ofm_?KsHOewX$sgf}cdP6t`<0AsdSZ6i(K;NOKkn^`^J+zGdboU8zD+60y%#Lyf3 z2g0oWod9^+V_;y=fx;+;CWd>AF-$^CQClgI(W z84_P4JtP-NzL1iTnjp1L+D`h2^cxv288w+hGIwOfWc_4&WFN_~$nBH+AkQUlC7&Qa zP5yxVKLrzoRfsr+ z3vj@7#(RuU89y^&GEp#bFiA3*WOBshm#Lho0}w`-7Mb<|;SDo4vrT3v%q`64SX5Zr zSb6{e;z*U&000010002*07w7@06YK%00IDd0EYl>0003y0iXZ`00DT~om0t5!%!4G zX&i9^7sX|8AtE-WtwM2E2Sh2luv8E?X*yW#AZdyyF8vDEZu|ikeu4gsAK=RK?t87) z)`b%8%X#EIU4IagUwP5fVmMqWU zaXeZDgD0?TeHc82Ol;BMX`IDQ4W1!>Hh30!d*0wz#O;c~Z}99p?4X7!C8FG-j1nA* z&$~|)poJ^kum|OJPOXC{N(vs5l!QS^tWvv2?-u>)jN@RNI3!!0zQk{#2^UAym5Cf2 zQ{O}zTeQ?A^SFktmOwm9JVRO<H%h3t#CwMB1XN_5Q#vNY1vYTJc?p(T&jM zCwlzv>|uFoa;m9DG7;5PgYOWR)U{9#?;m$YB#aQ=UN_@_I`F?xUQfEJ^#y#*z1*aRhIcz>8p3) zO3VhQlap@B(uwZB^R17Feri%##_{Q=Z~Ywgz5d*BiW$6L>;8)6O3hVT>wPiX)a3Xb zY-1OP-2ATmA1dYvtwnBF<%!JKq_wK{1F7EOvmv$=bEmP+Gl@*^Z%cmyEa0)H004N} zZO~P0({T{M@$YS2+qt{rPXGV5>xQ?i#oe93R)MjNjsn98u7Qy72Ekr{;2QJ+2yVei z;2DR9!7Ft1#~YViKDl3Vm-`)2@VhyjUcCG-zJo+bG|?D{!H5YnvBVKi0*NG%ObV%_ zkxmAgWRXn{x#W>g0fiJ%ObMm5qBU)3OFP=rfsS;dGhOIPH@ag%L&u5@J7qX1r-B~z zq!+#ELtpyg#6^E9apPeC0~y3%hA@<23}*x*8O3PEFqUzQX95$M#AK#0m1#_81~aJ= z0|!~lI-d}1+6XksbLS;j^7vyv68Vl`j*#wA{Hl2csfHSc&MaS|^Hk|;@%EGd#IX_77( zk||k|&1ueXo(tUMEa$kz298P&*SO9V$(20GXR8!Qp%h86lt`)3SKHL!*G!?hfW=~| zjOer|RqfK1R;688(V`x1RBB3HX;s>kc4e8;p)6Pao9B$EskxdK=MDHm!J6u-Mt|f< z_e8WS9X5kI6s&J4+-e_>E3!{mU1?R?%zwYF>-rx~rl?c^002w40LW5Uu>k>&S-A)R z2moUsumK}PumdA-uop!jAWOIa4pB?622)yCurwR6C|O`;Ac|F3umUAvumMG5BVw=u zBSf+b0R}3v3>5!4z)b(~ z|6^a^095~jQsFgz|AYVAZ~$4#;V(s&5ljxnc*2xDtwc4s6GDa;XMPT3|!!;Uj-vEAnuW1cvvLO z$7e!_1a-StfkUTdp!c$}k zLY}scD3DW7SdC}jKIma3c^NHw5i-v1s0)e5ubx3#?$GUzsu+QR)zw>{+TE_c`G7y) zc(eBl+=n(*hCTWB@^f^ja(+9M3Z zaQfWK!YL_=AB8@r0ehkiuv+$P#z)&OIAg|wY_8_1<^$0=KIr{1fVlv_Pg|nyj&ElH zDvcm-guj^pN+X(wMVYKLxY8A4bSLTCebS653qv0e0-{iZYw9nFX!SpU8oE1HC>t-nm;{_v%YU!F%sw8xqR1=oWZv4p6fYyi>6{;S z_FW2+4zSp4J!-s|-_GIi_;#5mDoc=@l~W>($BZ^eD&Q0Z$2E}DTB`D;8W>IpWc?c^ zg@R+ErejGHB@Zn=gD!u1?ZkU;yb6b4`}pcvO3=47<~{a1GwT_#Ken=C#WXXFr(AzB z#cbCKXO4Q_iRv&*desLodh{)%E<@^xh@)>uTEY-I23E=($bS3|-FWpDS=*3UAGz48 z`(?^%P@8J31g?X3BXOJ=I)%%%3Z3jmNr9}B&emgx`o=O!ud|#vDXUv9=oWl?d{&It zj}afoT!M|U)^cBFIavom-Q zODu)eTrhnX2Yib9;K>F~V8Sg4yESi)zSHl_Z=>T|Cc0)&(jMc*lbrsyx5?5zWB$iq z)r?-78|T_$0mIBLvkY=SH-q(pfLZZy3rLr~5Jhhv3p#g(Lv1Hx>q~t05Re6buyW=s z(%&FeWdf_B9wKs1gSJa1CXLP6% zgA{Ne-g7l?C12Lma_36ASOvs;Z+*iaeZd@;iuE?7nmWw;mkeYhy* z)}GaYLBwa&00Sh8R{3|XY=D56XirYtX^DnI0D(fo{|z3;a*>?&j5wT{T%8R*Z$hh5 zQ;y{EAg)1)7($tQqV|p0Tz3n8GdSiWDb?U_TYE5Tv!}M2@#x=mw%=jkuAHk5be%Bx zt$pOD7VPzF0S(67y~#>`|57&uv|%5WNiZYkY>LyB&XTa@QfVIrnxIMrk3Y6vOBgd+ z=!z8bRhsTY4jz~;H+9gr&z60PhR=CGqZz6MxI}_c!qs7ZmeB0MAzU=6@sm^q@b=Jt zh;;o1KT8ZX=r`vBX*_*tUwcY=op78;LACGFxf(xA z7Foo}TJ3%4I@Py`LmVs<2|46o?G>(`wY+GtsOL+Y?gGxI6bAjyu|pur7)S_DeQMO1fcpRsn)cl1kkWmkc6s$RLU~tZX@M5 zxUmKapwT(fbfOLNjFJ3^k*Ua5xkk#(e z(Ya`X4)$T=2y+@Nv}!sV{(zJLkmg7J@*(?vt}vR9A9h;T3Ul3&-$P~DwhYYTt!#r=BnBs*L4Ja7G#I-MjllIG3*kG7qU z##;!>C+M!?X^mB64Q{o>5q!mmnmWh|E!d2GI;lY5@Gpe3bSU5Pf<=uA9#p+ce0I2% zlZrvo#hdw6UmilCifx{{30h^-2@hPd^&@OAEoK-)0|QQ|x;h;+gt;V4LSaqPVLW*4 zi<3_K*;+kOj|MgK(B=g=sM~592ELY0>wvqSu1g3uLv&g!Zt@V(u0+`LL3y2Nk3Y_6 z>OoIGgK}=I=XaSBe&%GhoPy-4mN8~h59`(;{RCr5nr|w(&nn}2NLANYDY417Lmm|S z@pBY=v7M}g1UY)|3d5n1Ppl7A(E7=kVdrv7{4WH9yeq?POg2c;c^`zSsXr4TNK+Q1 zQ6vvZm(zaOO1Mo-zs1A)v%%_9tX$KZ55PmG0UnWq*Tf@71cgA$*zUPg(ff1;-|1as z*_RT$YvebO-gf+x@OfLZb!%HD2To)SLfEn`=y-vQm^mQzErF2a!(ujCI~hj6PEr<^ z-BAsD94hIM88!w@?s^V4!fBNzpT>tn zu82asn9`Q{Ln=g-9KrU`qCVErTnxt&-%fMq)VE#ZB@_E8CjB4`v2m674{;cq+;6U;{yBb! zM#l_5X$tAE{-e8;WLcIh&<97Fln2DX-hAmNLh?yrCJHy%mJQ)Ep>!paur%A`x1rqz zIu1A*D(ZdNorkn0+x&yO1A_01IcXSk8jLg^N2f7|bW9^6V1zV>Z<7956=-&4aL?|j zoszFwh|x`0rPFe4UB8sX5at%JG`|Vb*brqL(WuOR1`$b*Gwfh2t153*FGNpSFV0jj zd2t-N|BN*=PKP1FiHaL2&PCPB)7Gp{Oe_iDR*JYnmzaeVjzU{W%vlw3p{2#f#9Q3x z$$#9vas1O1HNJtjft+-!bg5cmalG?L&C#K{A5Yl2;8-o`Q>V%Si%Z>SWS$V!- z(b==6rmD))e`6%(1e~&?3=JIkvS|$3AmuIS(Cud-3{(IspMdtckE_1%wUYfP@|y&L zXj!WOWKAXLC`%?hO+R(HPA~zhyQZcBEBvkIszVN_JSJvI#G@)H` zruJbO%myhwF@KpNl*DYfxdk}-<0heIX<7L-blH-V>k8Ry0u~4MFL*Q0*k%fNYRDjx zJ#~5L?o9L6qLnuj^}lI+WftXVlSz?etp?H&nMM!J3R&|nnFQzV3qQchDM>Aibm6*= zAhoJ-wH7LrCNh)2s_-Pt^>jo($2Azp(qD>HUbm?s#+9V=Su`_D zo(d)ENtMTWpia(=kkD>~OG(3~yM)yz0U5=N^EH(*hroJ*IqyvCs`yAw+Idxp|O%w-g#VA{T?V>wl-;m&@AIo^O#cc zzel#UBw-f;ABNO(NR@}+5RlmG?h+s6zUVoTaeAzm4tbi8sS`aH=j8O^{K=g~w5%2D zt$nndke4s7-FCocaAsJoK$t;z-p2kbxLH}sWu?tcO;;n;{`1xaO%wA=DVmC%wFGPm z;#W~u2KF9~D!`Mjm3zjNMVzn?QM`=whLVD{&o=^h{OphTaFEAu_OHzMon7#IAfrUX zJeNPy48RZf#mE+(q_$C!I-{8Ur?ho@V@G5k+Vqe1apdedlP0cz zM7`sQ-s}4}+1Rj`;n*-6{B?%WE4lRerghnh#7@^3ZRs6JR|C5{{B>CGH9yN0yqCLT z*MH&lz}-V4sv-kn7)T%Uw z$hsDs#Up1ugbDUiRy}3GO_)Q~hulo^{LDIyQ6aWGhTMX(&Y`E3%IG#G2yDx4w1yQw zfk#(PU0g|rqj=cXqa2$(A_SPUm>-A zh)6h|XQ$mzd8>{WTnVZf=U2D=J{|5hGo=t)IUA@xfnJ-A=t@ZOP3qM!1o=lq%BU zqEIfo>0i*SgAfCdu}2~;VnYAWQc?%7@#OwqjH1@=6(^oXPMnfv=ngJ8o z!~;rmY!a`q!*50b#W#wGye27jN>8R5>5Q*7k_zUex53cI?RG_V)nz(|9$vg~uCzkj z)k{0PlG*(}+uLz!DDpTSB6(?7hCVq^*!g$_eMG9XZ^tE;kB4{75iP2X_@&-3x21GV zY_b<^bs3X;++D+n9)}H%OI5TfTitr#*7L=L)PRU|eD-F5LWaKzmwJQv^_6?BrQeRZ zXxOUUCn9=T(k`Z!+aElL7W5R35%G8V!Jm)%kpeAN{PQxbXn?QYwi#9Sd(ep^am3e7 zr1vR9u=R;${u+4iUIb>~m%h1lZVjQ#156>13$OTcV;6!@na_+ZaGI2v)9{w+Gq(q#D9XDO+x4lc;F>Li#W+Pveh!sZi!DR+}YTd zCz=hIC3TX94~S|RR_x~cwSHv03%xjl+b>0leVUq_X~yF;Qw*qaRg{V?KGo#3=!w_P zuMn255zV8A5BKuycyE_2J#)Dpntr=~`|+hXQ(A_{Zke_u;J3zwT5&3Yy5o3WftV2Q zzp#n2WGZ;sn@w}4TEW9aaAsqIV}tXl7lj%Yya}$-MuQW-K;D4=bFEsUI!V2@Um1q- z=$rxC1m^TRQ2?bcJ$%G!_m>G3otm5Ybmm2}>hA1vU~5Xt6e^bOiQD4RWkPHP5APp> znBZWS&IW5?>YWl$wU}J=` zK6)?*!ROt!y3X{c+VBQ}*5Q^B>J(&|X0v|NFnKQG=C7FsJZXc9VeRvhwbdOFmIe60 zc%H87CoMhb^1&R^2<*ZT4rk!+c5fuip6y@RC`}aI+V9?P6z#24>zFiHh;21M(DqOq z-5(Kf({ypr7pBv#qOrX5(C}1v6SuU}L!c$8(?M)ohaBRzeRV&8!Qnks!9pWpAqG%2 zkj|DWYo{d1{~P9B4Pc=wlmi_eq8I?MmPxj^2>Iqp7djc(h0-|ahn_J6_M)$1%&(Cl zRIrg$8Ci%m_U7#Arh4-TVOlJKG6QkHC9oJY&#wZtGoHE}ggC@?|BzE#G`IB$M(2}zZu_) zF?u+2$1(@96*ztK9Ko@P99Tn$t`<=ofgugmx32`!qHs!B14&L?mAS&!Lho{D#<}(HJ*sTOP zZRg*dF^Rlr=^llZA6sG^@!(hQNMUlQ36Fy!QdF0hs-)sT{G_6DVt{5%^_kcqqmyz8 zRP3n;_fyUgGww>NWlM!94QEBnS2}j@{su4nCi$hjj7!OMSwUsGybAEoZD}qK;i7Nw zprPb(oNA!39X-NejeK53kwInICbx?I_NnTx|#KXh*;YKru zBn5%Q-`!c=S9URy*~lsk@DqzC{xNmECXdEz&$^>WETmq~1o#=|tRR&Ia=I=fRQZVT zP>?760rF5$fQmxDd!g)Uz{j3O#mL`5oATL3a zI%*foukAIU* zKnY(`iRbPOz91a{R$>L6Xax(RcW#9eQjo4T1?Eitx?XZzcI+1P;@@}WsVoNlW zDK@f%1n>v=j^g2Hl^`ss;6ECCHq7~9DlkL0FM1CoIFxXdJX6zznIjJ73GH{z>7h7F zy#bGm+2owsk1J-E_R`M;i~~0u7ZKQlNf#y2j?XLCHh9?#e7#|BX7H{5T&A4E1Ox;8 zUGmSIOQpyT!;k+OxkFIJD?czU?LFA^%|iL)fCp)Lyt!N|9E>M^g7-mUB!_4^c zT1yzNybJQV-G`6(YH$Fkv03|5w~WWQoiC3WNz=X)HoqR>?wSde*Y}%abz8iU(jp23 zeb3bTsJgY2l_zOKw)p$kf%H>=L!!O>l=Ii!U3+ZwU%@DrrmPu`sqxEL%t?_)4D&aM z*wjspiKZkLL2XzuVavkCdx~Ob`;)0AzG@5`M~TRqXW7D5T^FI za+>CBKBYp?$=SScVy80a23Ajgz;!2)ZD(Jno=Q7GeYwj|G(65z($9oGY0=f9b~jm( z+AWf(Rzj$#)-Y$bkoSc!IT2sg5Bxl|g4kA`Cef{qlmabyEN2Vsic`;Bx?Ue6puZEegVD!FBW>hm>kuE%` z>d1w6Ti3*|UjEw62SBBf^l!FC-;|}j{2e)|L_ABb-USWGb8%l|Thsi?RT(|bq3!xzgyA%vZnz`t)o3SD`@Cjh-#F|p$DGCrCv9>CX1eyE|p#% z=wy1do6BtaU?dE?waTX;k+@N+I-*X{TJL49OTEQWuC})#4#Vd{4p7>vDm;NN%s(>X z3Gly%SPFklFs{BO@=U4)Ya#re)uAfl(@WY)?d2}KnfHj2Z#j_}43Cr)0#uRA`y(@V zY9X*c-#leRS6}9Y3hYpfkF(G~fKk-Tsj7`93yJ-i>T`K0 z`rpVEWYZjtSN#5UlDUt$0qi&&!f#So)c9m;$&Tsvx(tUzW}nx@5F0%Kk=hvKW5{o4 zq_uYB43o2jKZOhVv|!4ce6bP;_n$A z^-be7ZIt{Um0?fWs(0=FN2YtCo$52FCG9q0jwGD%)hS5o2VuNUZz0`<4Nc3n+)Je8 z1RvE9rnJ@zq)LlIHcy5gHN;|S8qM%Bk^+k@i+Lx3Qt3U4XJbf& zr96M*FLQbHP7Vr#je-cHX8WUd?icvuS5!$5L6c|T3smmv$qRnr=~h3~IS6a`U0^pg ze)EcG4Gv$Lz*sVZ!aC*ec7;cU?2hV@5`7vo}tuoGNT1=w4{9_w_ z$hX*wBE^sJt^4O>V#=(x6KIy3Oz{$L`E8+#*5pqo3u~aO=vzIEW^D)D+JQG*v2Y|c zJNDO1j-%`!4AxQ;#k8&Gd9p2Gjn3jKtcc|CSGBMu$<6%koVo=69#bJB+J*=3GbCkT zwv@bY1sr5?5I>tyZ{BB1Bz_cNi$+u!2sAG#TU|571>k8`71O<+PlP@4GvZ&zg9o#GTAa zKbn4U@DfZhybO_C92JPt1$5!}7+kn1;nHq-Mz`casPa@{&C6}E9E8&hPTeRj*w z9$?8(h9R@W&5j3Gc=c|dJR#?I;zfomA+8|HY?6rBc2y!aNrL<*M$CQQL@#{!MzY!c z!ZN*%vL0J8-llLe$iOSNBH>`WYLmDvmVn8h&-W6I#4`N+as{o6yIHuN#+S2NP5+jS ziuJ(S^|qW2E!Ju-ItzsB2j9KDnEC3~xVxD;f|n+SVS)8SZUvF@6BM_w_NLGxH58sK ziXt)(_Q)A%+3H0Ze|zesxE>en5payQ(L039u-~U!p_)Ekggu-@yQKE{p;Q#cj`!;iIoZPL{-EU#D>AEp05$Z= zEG1o~b$=4*AT&k-mg@9|*iRZk=4C0yY_t-5yJM4FMu3J&(-qauPc*0Hs)g}N^YT;M zsshq2Q;I7qJ6#of5~@CQTppTK#Xm!98GVWP`wmM6?`hgD^HRBx%kAXFB*`#f(iUj< zbeb>OO{tQ3S@5IBr0OMb7QUt%Lfqt$A_{(n*{V>yf&#xGEx%9K=JRF#iA%^H;c{B9 z(wgU2MY&f}ZwCU5S=-&8gnPAnw$Ywi5p8LM9>#4!g)1uLo}U0W<~DP$DYz#p@>` zjM67%;c!Vi>6y_-W)`6PxW53!xUgmLFY`w3rlv|h=>c>w;S?C*gQ!zUkd&w6F_9r0 zfxn|^e-+D{9-`j7Ag&?Ok*wU@%kG#=O{iU%f|WM~<=n3gLtoY;T{tFaqMh5|Pl=4C zP2Wp+G6;O5p*(;5iHSS5&eUR_qe$Zxa^K?m{KGP45mk38y<;(%iZCmyDI<9` zszvPqcAAw?Bw*f6olhnfaW+2O;rF!+xdRecB=WU(QAZKBtSLstbwkKdUGf4wS}O2B zr7tA{7v6eQH}^z!l#-Q`8=FyFU%AAxCU$&Y5-!WSn0RU(n2IdqQAC5Q>>3-k2_a|8 z1bEvL?4$a9B%~Vgm&OO7vkN0-Bo?!gLIfUjXe6Z-=tEUHgme+4eyYd*%&v9iIh$lK zh5XDqtzvT8RIc&nL}hh0>HB?7&>=M}MqS*jY*clYK^w`ZtYrB0p!44BK!I3f=JQ`X z^#4w5HAJDAYHPAL_+O7V`L70rq+@AQ|zIP8DMP*^^roWJ-Ki^foM8TbJ8AKr}bu6>*Aw)%PGy4hW(_ zpArQasCn6#7^a8SneH7^QY~9BMHEEi*lx98g(rPM!#+!Wavau|(&2Yl8I2;84S^#H z&`Y|(t@3#cYDE|8imE~tq!{V_i9l(Fow|x|utaRyJ7x7lk7E10%c8u524zR^w8crV zOoa^7VTg5q=#{}Fd^fd_b}Wv9vY%6*K(gkLQnO+hG&9$WR8gBF;m}e`_7jUYod zrQ{AP9*D7!$0>hgUi&$cq+ou(A-tG3%|={t)fY)Dphap05mSph>$D~=6ZB$t>DJmj zz{IuC4p)H`I>-~gY+uu!rQy{B7lAYJ%P;Pk;qif>Oe;#E{+!00Uh<(q`q49_fbXR6 zJCG`Dhz~7ZQIuMn-}q<(ZLf+R{;$!_*uZf4O?_fi4y$5#Tdbs@)euA>6u{%;k}xH$ z7Q4WDmbu(Wv}-~816}<{@RQ81uWD68Sk88l;ll`-fq6E*4kFXE=)bg~-NN5%ebz95 zZ(TxDuvPS)LA6|$ia^cppRvqt59AT++?jf}km?D%z|!afgKohrwCAzKnxa=o zBpy=d`8XrRJ)ZPumGL1Avufak)a?R?2Ab0ruUwipU4Pv&`Q9aNhZ#89oo`tbAUAPz zbQPLue<@(-&))z_F&+;BzAw2kSN|A;bfSewJjA827|WQew`0MS<}ZlfC3ikP<$L4D z-TUQlZ&Q5;AT5&0d4P549oM4He&_Bpa$Q3!vx1~ zBmI%K*5_p5U$7vHbokh_v9`X>LoB_;o)_|nKDYsqx}p?7e@XO_#9~j@q;l?bzEL{x z;K$uK)AVlg@b1Vmf!Ok?Z$Zw|4TjG@rX+exHHd<3pSd1n+@;@KUYB^OYz|%U@bypR z`uh+V=PZp5E9PdA9S2Ajsl3fxF(dC{QJRS zzr7vSER4L0M~F*e1HCjCf5{|GG;dm1XPFwS$(A>cRg~TSO(0Us5?pqJKb$)|Z0SYX&RLZV*>EvM0)9%>oR zgOo^eK^&Q{ESf1q0U^*F>{;u^w9_qn1R6f;WQ-8Vfw$36Vx1vi%kr{JH00Jx37n=sIeg=L(Dvcx^s^EmH%S1pz80+4 zpL2Cz>Z?&=5t=;HhV{FdG;4h_Wfg^=5hYRjE+Izh9m$!c%;<$Aj+;W&jJ%D^^D*v? zzY3%84Lda3?QY?f5EV|KnyPP{ znI=b#~7+Y`wvU%uZm{10ZHFJy!1TLPpLdI&>P*NH-*ZQ zx99h^tjY%}cG^vd5!BTy<#rdG>cqwJ^3~k@Q9XN~?UnqvJFP9hymox{RkMY$1|!pj zHcDeQPG;v0fvbC}7>8M%a34PhuDN!E>7ZzlOCy%wr>Knf7LEPETwI-qr=B&v8L6ul zm#W|16`!}vFweo)^^EUp^El;pYMs{JF0EK!U3k<@N%$Z%HtTR0Y=od7tnL28_OmKs zZa?*?*^(<5Fpqrks82W{_^SeKLna2F>yKE}fa0HS3n^UeS{S=RjM75EYy@BB=hxyL zv)2(xO#U+tabc(WyRsk#nV%WW`*u7Dt%(7TM+#}!Eb1xGYqB_e5)bHI9C+s(cg4xI zJD;=Bqsb+aQp-F`_9mBJXZif1m}cpEc5|CDcIOT#A zq0&vG=usRvO}s^I6Wazc_|cVpUsf@`SW81|V~UOZ=wUzo#i#iV2m6bq2B!=ae5qQ| z_2?~w8~jX?Uo68kmpQ`sw(05iQ{_++A^whSr5|cN;~OmWYvlt0UHC}48#YSa=b-iu zv~b}ulbFnBlGh4hC-n^QeZD7)3!b2=$3OzHZe{_PMfqhs1$tkh{sk0Ns$zt(Rdgz6 zd_|-Y7wdrYfLY#OA^PDAJ`L{FSrO5n4)R;k%^Lf6CUGUIvfwn1+>peVP20xQaoNZI zQ6tDlzLRXEO#=?;|a@lfh*AooX5~K z#VqLumOwgc=G!o{-YhmrTL(!|n&jYQ)VplnK}SmNDiM;Xi9{xJBzo#}F>Z9zn=17k zJPMf`s(fW=?ALmgXVldUKam%%m2DC`34EfxCjU>tF-S#bg>q#*FSmiGF*NO%rQOlM)z?l{$GEdb_HN05*{#8Tj?+CI(#o^qHVv zIf8gocJwUOzLP{k%}K(FfU@lGD00t4^1UDEjTk6Hhh9K`k1g1ZnKDBs=oy)iM|7eQ zK$@EO__b174bMji+Huu}dL90D!QuP*kFT}KqlN1;EB{?q(2-fGC61)^`C{+ zY(i^IG?O$*t6D`S;zf0N(lE@E5@X6RoL#KZ{XLE4U!*-imY`aW2HZQzCUJTej?I(4 z)?1yR(h`ZT%gbv|&BiECi_#iF^eMGJlS&f5U&e8$r0y{c=w%MVM9^m~<(=k%Zk5ta&s@PhKqhBdXUqC@igP9x2O4JEaSm@`Fpwq! zWPrwS2E6T@L*S}qPutLSs}uG^(@8!qEt<5|N|_%f503w|z?}3g2|Iy0;oAR*l3D$d zuFkOrz2u1j5E5aTO_(`i_et#G$+AE^TX zyA)Jh*YNa<#)e5AhRVT)+UKzNXvn58lbn95^to-IT6Mo`bshxyJ1B zahd$2-w)mzusZ3E19CX47Mi^G$(HG(!UvwsVREWFl0^13?C^c;h|&g?wBAp}yv{lo z_hXtk9Ls=l%$1vn7<$g zzv+>3Y%BaQKo|-5_z8PR3ML}7eCK=>EpE3{m&Csu7dQKJ#y?*(m#%R;K<&qF!v>uZ zqv$IHX{#8z7;S!EHI$2oDQ9BiW!!w%DD@z=Une<1G=}lD(QkUfb9OF@yRssLC+z+b zG!xg-MVj*4pyttDAM_xjm|)d&w^hP7q55|-yHes_4mU0>K;xf_g~d>QC9gwIe&UEX z>E;m!FahCy-MJ4XdDAh-Mxy=wtpfF|s_IrWN3P(0Z?Skwio%a(_*U9l;T4?l-Z9(>tvjNJc#}qV(TcX}ej=b1hqM-xq);CW5%1 z!olCTcyj?NBJWz!qWmc$9H4V}mNN8D09jf9pn!bVb(kBQK{Nk~rN4%sAt`>)8a0Hca3Utc|$}o!Jg$PGdCYreR&@q|DB*~`iXHD5kP@Vk-;8vr3R3> zL(+nHV-Ea-6n?U&I&%E7=xg3cr9}&bD4Rw_l5k!>E3aYi!()<1Jh(?$qH&@c2!Usj zA%edP#|5J?FceAkT}u%ygah)1BC!bNyl_51j0*O3xD9=Kos*AN6;pw|=*2kV1oSHn zv55g6dl6{S*9Ys=xcaqTqy<{O2N#i-dC=Qr3SEN zzfP>K_yMeDSvoUc1CU{(2ts)30^m>#c#sxr`~Vh_TE@#iSc6e#i65Hr?7kdh^Hwr? zBu>k7tdXp1NK4kotk)Lhe>Xd;1Y7NxXTC)p?pza=*9!tGwJK4i{b<|$iHQeWK}5`4X&iJ zt3#AVQOep#C2r}kG?Ru#x|}DN(ukC!Xy)pbmrwM+J!oxFSq|&tNGcWyvvvVEm@~SL z%Zr?Na#p+qjECcGmMmFZ?O3H`qSr-}BE4F0JG*`y=v}Eh`nk?r@aNP)UXfj8L(sb2 z#C7$?Z>t*Qptzqj`IWHpdXF=U<#Z27;xckJQud9WslqmJn)L&yFvsOGpUwT8t z$Q1Qo8yBFz7dUQa+PT0vSp!t~FG7Kcn5U@7Js*HK^bqfuI`~gqL^dwBP--(kHh`qE z*D4?*y@G{SNE?9fW7}0WK-$W67aXCe1dj)t2vGCUUaVU#>Ne_A9=;!VzmD<3|sk%HR56y|q92FlM{5UL+ zm)P^+{&9L2rtz9m)dZ9YRH?A?gJa`K?O@RGKIEV|>XC(e1f2-!-fh<+DYr}|w=Tu0 zgq%ru1{YJL=hbAM!}CZR{XiKN-B!njxw4OUhS;y(W>(OcBdJYSatsyzm@g@{T^{Q? zqqeAbmpGfv|X z!(6A#gL@r3JpKom#7`l#5(IB+V8ol1}~b-^7#MhXqh^u;wuJ zmt^TecM|YdY&g1%X|uasq~wD7Xty z>!{U;hUeuH>!buTY-Q7nkZU)+3Wf96ZWuz!^!0ZL_T9iFcM&q+Y0ei66P8if#XoXZ zS~UA(`AtFk)G6G1IWEk`#=*KcEa7dPrm0YW2+lqkPN7IpNzwUVAwfD&Lj6P-Wfwg* zb1gAEXv>zl$H8!%@M&Cr9*RWR-CGPZo|j~H0z|p^ zBM%J#lYCYJLx+Lzv`dLc)J?H)g>%Y$(Nx>QWrAsgCHqxK*ehft0g9{C(FW z?MjpSQL0QvSaLzrr%YCUm;(LT>VvUoMV#{9*E&^|4C$JHN6}gybr|x8>&o#`kCIId z^qv)Y(klPni1cEj0sFbajF1CeVD-on$6KjsSG{H!n4=F>PXtqWGVTkCRO8I>Vn+wv z@YUri;s5YjTqgb2RZZlAhL-j-q9w!A+#qh7x~*T$&}h?i=?FhUi4Q>{Iy(8_;jOa@ zm5?Qflnq|^1ZI0nYSB*TD2pUc1KbWFl!uVV*vMFGz8{cuT{q8|Ze1 zOC0l4VHPhz-rZk`0`7&j?bJ5_KQ{-L*FCmz_62H&^nI!tOiMjJ4Ic-8-J*ft#z8nS z5P6}OgfocBw)Zz!Bw;IT=OSxLvPEVGhW`j~*8F@qWwWKBV7l(b$HW{%_IHf*wFd8| z)i$O>{~Kf7uR~t_hOXc}9kfF5%sCD~JxZCVUkBVVTr_oM>a=>4z@tFGN9Gq}i9L0Q zMEl=d&=Bzz{aiUIwS*2w*DjDwLSqMvroTsGj^dWqP`H${`%jt?+rBd|cvG2axoY>!*`8FTx(#EwwGL!HhPkJ=b0)OR26LVgtC#l7Li5vrI~=_dOM~=4 z-frm@`{VYMI*t$L_Si$psRR0&65(|6_{JT!b@XgV-s>0ayV2@A^4 z{To=cPneX^hf+-~u5Etmx76jcCG9hfWBD5bIexZ?z|MNzsU!7IDE+f>P9N0b7&Y3L zD(Bhd--mAU^hPzZ2l=88WxQUQQ%H}1ajBbOZ&rxzB;{Mj7_`KY*fgUsv71H;c(O{y zRcW$e{@55oWr~Z{#f&@t=o@a3=`4V438Un_%<7n0cfHmOiez{b_x_?pO?tNJk>jQ7 zIS^i=1580|HuW>Wbe~tCrD>*#D@Qa?CGSdTv5zVTzHltuB(?2l3KP4poL=dJn-6ld ze{Vl+ma0DXp6PBs?iPB zQ3cRUwIx%rpl8CN`B?1 z`T{Z*dvEjox<5l4-S4FZheLZGc|U!2IsEGAC(L#0Yttedfcs2iQcYyQcWanx>nHt$j|m>Rjv$DfTrGNCQ}24ujr!M!TNo7wiLE$x?6o3#UikdvvyPbY~FDb`|+ zDLc|~ai(pCgKL!aYk&xVtBo9ACN15;-Hiy%@Ny-D+ucg8e&g70DGE@eqM)6CEMS;J+c>Lp`zk6Pk-hVEZ=`q;>%c+s(aM3zrTEw7m%P@eWWERH%K46@<|RN9Vw!CIc|wX7i=!l1ZHf z%`JppOt+8?hql`5UpXPnZ~@yi=hIFR(Qsd+%WvyWxSd$ch>k;LqTTvLD;1$r8tI%^mRoky-L@ zHZ=3qfn$MRT$mfOMPoF*PziB!t4O{^dPTI1LK7`cY=_fl|Ut8mgkuk`(NK3Kf|zXU;F zm9&OD#Vi=$=-8rzj5H)Ts``fa*v@I9Ax^5+!=U~U+*D1NrwV{z=M0h!{8AvXpyCEXT#);grV;X@ zyNgb$#pmf!NeWiuQa-ep3Li-+Yon=RZj5)31cQ8x`Fp0w)Xgf&#!c1#BQ6yfj0+I3{Vbh#}iR(9El;LO>FE z)ShM?9)bee(Xo&`sIU|xglL0JAh#9+WaKQ5Ab#Q*ef@~)MI9qJhr&!ILokR>7Fdo2 zxa{p_RBcGCzAs9;{rUWwX38q5RhEgA=#^bFQaL_RDpj})%MkMXapo4@OeWZRm@>Nk zA{=Qu52W~NI3}TzQ^j!U=EPXz&5J$_Q*)-54WCug;FQtR@JvYXvOZk~YDA-- zE*h)EaL!IySRcV^4ypZQWpn9?a)E14KouZn9oeuyHN}E&$|prDz3WXi=7(EG8sQd_ zS#W3aat82uui%Qnl?iLFL@*`T=L|*vNkwX{PL+*x2~*YsZ(O7l<}p%5(1=U9pojvb zA?PLAm@e1|yRh`55%9ae!!cexhFq}M#7A?#OAhT46cd}OGXkYO2Z<*J4Kuw8=j8^I zQiwt)0xcscH^<~KYxHmeB?2tD+0+vZ4!w?32^1mN@}G|2#&-xp`Z2~BI3${Z_%?%o zqTesLLKe6~^KD?rOVxJ^K$=#2&f;dJ;;S|f#}mpp5lT0uIkCgPwKiP<$fr|`Y04*v z(Ao~$05Bl>M1%%ng+Z;0uEA|-i-r{HOw3Q>gxv$*I6X%fD|3YsXTAYiE6_HGf`Wx~ z2m~wo5sQdW4 z@CX3mlrkoBtPD{xSR&}g_uM8uMVaNDCuP-XJoJR;co^TO5ES{4L<*W4R-%lnDbFgB zq37Y?1AwdG^&RKY&3%JbS>e4)J(CqNb+jPig#Z~Qcoy$^G5YmSf>s>u3r%_In3JG- zS$q7>ECo|bkD)GEW0VBQxRDU$V|NRm3*~i-HWgxuaQth-;ih@d02E-yDD1J z4y8uc?3F*P0}zz1@HW8uu@v~I^)G7F#yl^d;3dEwan+m!lj4B%2pPd0kpW*OPStB4 zYb}B_Q$U~SEL_U8k$EHVB$YgmK_>_h(@I`A(wCb=foTS7CBTJv<_Ihsrz@}l27RPi&#by#n8F6IX98x1G` z3KlIh?wb~j;f3AJ)^Iq?f}u=k2(0}P9T`Lss)%tQBZTY%79=J_`loHNJKPzJ+R3Ut zD2|sR!;>T5w_OnpxSH*o)^MCK*`ZaG*sX-pwH?m9Tdy|l%6N$tj@aqlx=EB`3~P-Q zYYO0-s)xgv$8_yk&XgGz8pX*`kw{imP34RFMHOl7uLzN*$jKzRqF~mbF$qEPxp`5< zXF5PHWWY3Yjh>bLA9CIO^mffo9Y>wU4TkWu7krUNWN`so<}K7Xd2NY3Tj1D|%r|%7 ztHKJM4EW~hj%K~9e%leyeLX|x-C#ThKB4TiSV$QbA-yEbgYWKT zbz>@J6&hd-s}l^oCzqb@vvDw*cu$IiI)NNdL>F%fShy3Xfs#60MSveLDUv)Q1hMi+ zR(8RHV+c?_9#MX?a*-`E$%s%*E+mWy3~{F}N--dP&;pyIP#>W?sdjkDr6VCy9S~=k zKECdBGu&Dfb5C_(ML2}#R5&dKc^x%u4hkf{4_V~hk8i7+r4!rJHg&jU8J;p|B1>GEhu0A0dV@l~q$zWA zG#@`VFT!889tn6%>dg5Xn|j6>r|zm{nM3zPj2~ql2LrfVOsr{=lvP-NO2AODBPSI! zgVo$bm=g)!HOm&-dS*wJ8oqvBr_rlztm1H0vL*^Os&PQwMF?^_56apEQ;l0N3n`ja zLzUnPPMc>sAg=<5$5!H|JDIK|QbKfquxD~b4gkRb3Ewn{5%Cs8l)l0jxSd1>P`?2m zZPSXD(7;GoMBKD@E$x_msh&<4_lW8gdCYW0Yfig*I zub1hP25d|CL{)&$eM`sMrdn{o9-OvhNg~`1dqw(lEs8G8CC=;RuwVR?i#y+SE7g!F zfs`Pk+Je=uTx1`SlbntW*DMz9;wM^&V*)WUO)hZCIw>h)wx`Un+*^PiH>_$kp2P?S z+9i7=AAK{i6cb;-ML7*lwGqb(IF;=+ffDb1u_0FUSZl_K^-NYwTwQrD+qTNXFfvW% zssXgH4SA(<4HSq$BHkd5XsLg02fqV9L-!ddu*0K@l1e-040xa_FCyDIodPrx61eEt z6qr(pP|QDrpZhT2nFg2!Eu4NY^d`zR9fKjD8)vdv8+qRe#LEdjoJ{?HOzYz)>JO-m~$|RyfK*(8& z8M;XWQ5PVk(SsEVMJkdmYBgbWV@DW}HP&Qc^iiFW43W@-#@TWMstz8t-FDe-LwJrV zi>@(|ig-ru(POv=QIoyk3u3Sj?V1VVCLx!A{JWA6f${oIDN3{w8+i7FH;2 zwpCcT1#1VWTnY!v3N}ys%{JhtuH0p9Va8*ct4YsV-l5VV66Mp;w&_LTZ|{O(6ATJ= zopS{ud;B=}=H@taMsHi9j-xQhs^)L12+MkW(5W53_G~9QaVm|o)PkO#@cGn`Rl=)? zWjyAr*d18;gJY`QywtwUS+t5Nvh2Z+J{m}#V4)4;pSm)@s}0#=7RHxri)?4%T+ory zh(JhEqt8^$Bp!s3G4r#@FuF3V2@OI>j8-eUgZi|?_2~>%Q(9o0nSe>5b0R|bKxR!o z*n+Z8o~eY9`5?WgKIp$Vn54>jYF+0iA$D=txuXYKW))Mr=Q6WcHZLoxl~V)83gDSz zYYgF%{*pSmvjy!}0sv=7VREtHp&u#doOr?!n_P$1-#PP0* z*C=Nt)|G#Tx13g+devX~lQXu}Fy32mOL&6~tz$=%CbY z;IA!IiRt#ZMNBho0x?G)PHa;vXG>TT$m4_b# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Italic-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 zcmZsB1B@t5(Cyl`ZQHu*-MhAJ+qP}nwr%fS+qS*?_RF7_yqEkvIjOEQr@E_WlA2DY zU1dc@0RRDhn?@1<@_#l3=70SE`u~3u6;+Z3001oeWpVz4p$qV*n6QZGFE{k-`u;zaN}4#cm9;TJrV-(X@UcBa<99LMh*@4q%a z658XBslMZHEF8E7&@{N?(7eZpUmz@dN=nOQrz{c^wS0FnX#0PY&N6gaW6HT=~n{pJC<@{8T1$@+6^ zeYf9vRsNfg;6DIk0YTa5TO0p!6u+9~-y8)juwn@9Y#p5d0MvdZfN#I!0Tg>&FWEU5 z|Hi6+{*rP3;X#<_($(1DH)oCi@&o%1rdRT{zZUQp08_jLv;Wy~L-D@{>Jz!cCiN&yEV4`qxM9cFbYFoBwRPh0IQ;|D4fE`%?=h|lqJ;7JoM{9rYwt=vI{#0HXKY2! z<#w}XvnSt|MJ*d;NbJ44`;PAe&RTb+XD!k2!R=;EE^{LFESrNSh`nAZy zJdKpdNx@pe(!A3+AV&BXQYU^V{&dPr?JKPV%ePh+S55%E+dBOB&H1bBof1*H_{a-+ z!cgZ+Usy^o=wE)TAy^eIT?c|8O0}oLlvPLxS*Hr89LbxIiVq;$a;9EcXAf!ExFAv9 z$`UV`>9;72Jk<4jKOIkE5eE@faJ z39}&EG=8uhA^cB((f&S2FWCV~4%n|(SqA=b3_^_sJrN4?ceLlQ^nbEJeEQHU#H2z>}YNxKUs)6R0XaYM?<}-!OVDmq99p>I#LC# zn&y8e{%?p3T=wS~o0C=39sQ0_$>}1?-VzM$9F+AGZyWvezPCBr&7@Wvy=%}7mCy=i z$IP5_NDZ@7_FE{j!Rh*3bH1g}N=OZ?Hg*S_llA{XpllUGmk!coM<|PYbZqLlO&e?i z#c1~36?63{<)oTK^unXh81*MMn`weAFhKj1gr?(}c%+@pFT`e1`6h4$;Qd&)e$CVn zxQ7|xI0Pa4uv{~fH& zO5R*Js*nq(QtuSBJ(YH;RKb2kd08RbX0hMs&Qs|wOnstj5zVY`UN3OzE|95Gz}Ks_ z=xl3zVpJ*A@vdBX!c{3XIGIFyYE(Q5gvQU6oJ48jb?^z`iQA0YMPBx`6U^yMVzC8tg1CM9Ub z4eRvu04wxgfAGci3?Ug9-rheb7$892K7b_ZD8`gVvZfw|!Qc>}qtyF6F#L(4U_A6P zK+PHv0#O2i1~tJg&V#NPpwnV8&w016PXP=9Obe>s@wn`HI% zP4o?LMJ}cJ`^)1AGV2Ft{s8k!jE8yL9v^*wI;{~^SpC<7dV35n^Sfr*0Y z>Q!I;_g&1$U`N9EM#aD|13q5wR%ZjO00lDzAk7Dh@jv71>6!THVS!Sgasr8WCbJyWCZjCBnLzab_s?L zV2Koi!}O|u|A1$XLNE3Llu<*}ME?0B@JH|uSj8lg2s*JG`oT}_5B?ATqwoIDz)#N) z#&^%x$8rBSxELOem)&mvHh3qVl}Fuue*m~Od<34_4u8pQ!V~G@5ecv;8(5o)C>cS2 zPz?YE3r&^PB~F&sCQp~wCs2Uk08xR#K2n0hKc)tUd#DJ>391TJNcd!uA z5wa4KW3&{NWwsWVXSf)d8M+#qYrGttZN46#Z$SS){e=1Ydx-J!^NjWOcaY&Q)>qkE ziKbJUU1sAA#gnQvI?X0m@6On4HrpM>8!=a&E;n1Fa!Cmp?!5;3f1V>7XhLGtVTNH~ z&W`j}jusiJR+rMUzzt58`NS6(sfh<4(4k45G{(JWVz?PUE0%^|Jz`&Uhk>J3C{D?6{ zy_xE>-@d?yqo2OOd(3ThP(T3enDAz9>)FcYt_z|l$z3EdiF2gTpw5`g_IdMTL9`eQ z=2XKjgxWX|)ganMG)_m{_#f)M$COPckHq}dFEOb>DLD&lK!{$vdlwyBb@6ReAOvq&Jx;_yo}aRk0nNB~h{26H5vgdkPS6QoqY8B2!h6vl^T zf+?_JJ(Ud>bl_86Gfh z|EyAS%42~k3@e0cgclA<`D}?Xl~;i>8KY2BIl~WKU6*dOgq`It+&RlvvM4T1JB!X+ z#m0!?3cHW7$&eqF%(R5kuSm&Py9`ga0H-tBQIayxdm{llrHN-(f~zgnLlxO9;-i}8 z#sZThtWhYtLtV++5;U5a($ke}T^WfS$38v?98b;IbUoOeK4RU{tNnCQX0@NnYfVjy zh~rCc$qt1VEy6@%@}0Ydb;2M{O#jhplLN~on#!mCH&eyRqJwQ{+cv8zDSaU^CyGD( zqIl{`q`t=ija4nSZ-v)cV|m0Es8O-iy&BJnTY+Nlo15#JtxgW}(3DpDen0g>m-ogl zz;gh8UqY$1-YO+u;Jtxjybh|UWQLwkb(KI_VwNh+DDAn7!n*D%#VF)CBR>6;+CEGC z!r65|$bQv1CjEiuu+S5`*@REPUM*;|4(70+BVeNuz1c)9>U;^o0{d^Klqw+4+~{er zt-6X8NS*cHV{!O+XBgo{B{Ht_@-me#%Fj|bJ)b*&PPU? z%^{3M1Ca$6)DrG7EiMP>q{=GWk^d~-ypZmVR_uh#CYO0(T!JX2-NQmxlqeclCvQFodqT<`EIE!R)o_9Jec zh&jWe2$`3AwX_xw0r#nPth98mN zGSs%P;WS7LqEzBn zetKb{BM;TD%(A8x@oVCvsM;q}Mzw7kCPVO=IV)WLt%{jhnY$Up;Nryur(od3Rr}uh zMtSyWYsCR@usC3n6|iZSm3p*wj9OS>&m;@`X**tW;QHbD{hebUt$FeS(&K#@YlpVW z#RqkFCfEgoPB|U-b19pJGOAx9PgX<@DU<2$S3Eic3fG}`? zKyt7F<{=B+h2#X$O%%F~j;};c?>!P^^Xq9mC6lu#1&d@uOOLlie&$0@@zz6J3q_0f zFgkn>dQXD>`?XD^;9D2Ah#$R~Cg;09py1mQwx~-(^pt*A>_T#s-0!$O-=BM}Uv2jL zp#%f~{P_WZcUv#^hV)txd48Sps>PAcXgu2@GxtEqYdRZN7KEn=Ed~YguuHB?`Wxe* z@wXbaezUcTh{ymP5wX5t9}t3qhU%i>yo0Xew4>jm%mS@yple-5fjN zrYrsBcQ%G4cf`8ncJ4tiQm zv+g^}=eV1i8w@@=?n*sDxTz=3*4W9wb_zHdTOO$(yYjv}oT*?aH#|a}eNuTpaE?MV zJHr|CmO=RM`*?K`5`&W}qWq;7T*f*4j%Pp!NN+$Lln9}~t~Wxg0w~r~4#@H%hi>t> zK13-5x&?z~E|T2Qpi>9}By?y1~Jql5MMkc0eh zaa1^kiL*|^NXnJMG!P8=Q?pUrSDYV%s53+I{VbyP)HC^Fe3y1Q6Mz_9n?UUAOYIOosKNo5-dnMzDQ&lv8A+WcKwKCj;EKlCjk( z4A`!>4~pi}=H#g{Ue4mmj$2~3B&?*oJ~w{GPslCHlYdRNQdKK5y4&m^dOA+5R!>qN zyiji@nCu0lX)$r1#p^jDO#iYg%b3&O<8S%c~^M)T!)2ug)OyKPUPCndXI-Pr@xY292t>V!kuU%R2 z9t#D_jrehm9H%+T{d51|$?@_q|ikmn_Fi1ZYN|O7a z6Cs9iQR%ajYh)}e?!^#-w| zi78Sc`kU8rLHzVmyX&NE^j4#QkLwYycjjSij8@iN=}8M8yWRDO0*;FAB2)F#CU^7S zpN@{BD!DqR>wm$4k<=fX$}WS6s{XmNwH3Gu3wGv{tY(|A``6X3M9KG#P}|IDedKg{QdnvSD-Vq?4!J}Z zGGizB_1WLS!YQUKL#zebLg+Akgh?{=$+g(z9Wol~6%G5tW4^+wDY11) zy2k}qnfq|J`%Y{6Y>2d0>(h^|I+L!3QgL4QYqS~QE^*>sGJNs%hbS;Che09X^1NN* zNF7t*Tuf6?9;dK8R7FIOcf&C!GF|`RI3Mjp=OOz! z2^JcCHrQ%(i|O+C&iq?4qv>YF_fq&-kK+Tp)fMveIx&mglR)n4w0nyF+SkgFn?Qk@ zvO4ri_s>#MA`g>cMhKT82-^?LrF1O`wuA(->iHJf_9Q`$YVHk@K0DDh(L3{Q`_A%01tznh%(Z_Yd-lg>oBD>IK3A2J zDIJPMI*^s5&}VxaQfAA9@jzU&{^mxi6~2 zQ;{V8HmC*_L;|5rAx{%Ry9f^5tXZRR*@`hkpiHSwlH5_GF7#owQObn8826?}p~MIvnNJKs70^;2D!1JS5V1eZL(-&BrV>e>B_>5+p4ohla%~_W%(!Gm z5e;+UeUI$z{b5w~X6t7pm!18&f(qXwg2&?JON~FJveWK0{3bPemHTTN_{DlT_=OA{ zFFte?p->*VsvhT=70HEdmK(qdPC*|okw;kg4~Zb_Wu-VrJyBgITHW8e{rL##*cgW) zF;X$|P8>4RfQfxJQ{jCOSuPGi8Ss6c_Ov^^d_lS*#n!PiJ+KP%wN8%b(=Ni9fHU6& zdepLaKGntt@dflu&Dq^2WVTeF4A+|?ok_b%&`$~%n-*)B#2=a;D4XpUT^Va({R`K$h2P03e+P%m@)%?Jv7 z`qfr8-ChU|86d7Gz-&M);NpBKTaOp<#xZ2L6G)ETSG53F3QEMnp{61h&n&!0m>2|L zZW7SdOsrk2bDU#?VN@lTX(?EjwCK06!^uE$d|nmZ#>WTTTHnWaZsflwS<79YV}ma& zH1Ze?zp$nbP1GyI*+d(#Q~fzYYFj9-g4tzIl$b{|FVv(h#nEjtUlyf*55#@O!F z_Sa*cjqlaDIyyoxO;C3Bu9xLdhB81srJht_K!}z81UP8zP%Vjz+!rKOt=E(-W_Es8 zX$($nT67_i`_ZKL*Pc2F8*n^I54*gkwVtdwsABuqgCjW}Ux-eQU#W&a-=E#^k2UH#+piE%L*lO_{K;>sPOAOjrRy^( z_(oz`kdSb5F8wJ(Qo1_^N-n7|IXo76q4s+@9hC(hW3N(N@Qsm9c!-$t4J)9G7;0!y z6?=o}SBd}Rrt(%Q(yLL{t&Qi502?`n`BQhi5?nV*f%vpTYVN?k4WW)e>%hlt&}W8J zSdU??ncJ`UsNdePwpD}at&>+K#QedYUNLMBdX)BMYq8sK8dsqZ)mF7xKOnDG{HZP0svNo$3&P3jUO>pHu*68bCh3AUbd!80aY#QHy|JXGS(+<}x%N zt-ut3bR-B_VC`H6-IYnjI4cYGqrh=71L~c{Vbp=j!IAC z@=qhL>`K_KweNQqqdrs~rJg>+Vdm!F&UR%64m}MZ-cExTMC(9gEoGq_Iy0fkL!}7g zeLhg!&MG3RJk$X%_3i6n3*#vRsFTQJL0hP^LX|5KzOf`36S|jSc|GCzBZdXSGnCf6 z9_26EvYVP7Jx^k#@y;DNwIgZomIMooO)42AC>j+EndvVWVnHt)^|V0FPn{oJj5>x;~JZ zQ^NY;`yuXur-jIUO+!wm3(NYB>Df~bcWeTswS?;07#<>~NEW7e{Z z_D0u@Q!FPJJJx%Fo{i!zd#%O60)D^^d3ziS*_X$+WussMED5Scb0bn>n2lLiVkqR9 zO_LX!HuJJFYMZuzSu&5uyC}zuW(V^^*ft+M_5&VR1Ez=IbFy0*K)wH9KVr#Be_SZ6 zWvTwzTs%hDdv}!=amVi&5>GwW3~XvU*7Wa|DN% z^z$_|ZknNs^>DgrdA|gIyErRrP4A_4n-!<(`+i=$t$9#Tk4+YU+o{peA{P&wm#GKX zQQi+;fC%~;Q<&ylq{F!Iy31z4N)`x)L*UtmF4Mn?7i;GcAVC)t% zX{WW(XlnnSc$35Fm7Phv6L<3laq3Vn{e(pKeLE;?yIFXO*kY;T`C5Io2a}EQiTONe{C>%is1@;&T}_nF*kg+xCzbz%xYj-RGAnbtG`1IAcq?!E zdX)zo0P1xGU?c@6S6AQDdV(a>b))Hb_VJGRvyD2qJv^6%U`Gxa`~_SINpcu3hsFS& z;sOVZZRF6d1xJc-0MsB^tbQJzeZ_4Krght%jh~(9o50T*TFGC|tDEh*^1#}g+Pm%k zeL9mNaZgJ0;Q>GBV%P2TdW4_Qd1F_Uo7n30{jQsE%gA3dASgQNW(%Vi(T|a&xI#jb zyF0_u)To4ILdnwevvA?v$bLPV{((K7QiA3%rV6Ch89t?~rx4LHdV+$2oEh^v5y)G& zw?=!x)+9*y;=4*|C)w3S6nnc2a&D`VJT zYeHXd_qsR&ak)mHi%qy9X4SGti~6ifAD0Q_Nj0}w7Ng;v9a1VUg75}02aaF&XxvpA$EdXwHjc%Pw3}UHMjk&a5jUTXZ+3>ekLT!cNGPVzAK!~Q8Kbv0g2Vd7KWK%35(w(c441CjmRw}L#w;N7 zBHt^@R`0@NN))$jId9|Xe^+$L{tN+jeg@#E)7)6CTzy)UAXiarWCGe_%dSuX`McFb zalQCx-C%LfU;{`s+2OqGB0 z1wC~RdZUTg!G4la)8HSIqwoj@4R`rm0<=oDyxbhEcW6dv_3kuScn+{y1csqr8sriC z6k}6jqg1(UT{3otN@`*$2l>W@z$+b+AP5xvdb4`FkNtVoe6{@8f!Jue>%-ofg|4>t zKFsyL$)(Yrn6|d8z*O%%Z*SbBcH)!!7R1>wEM?CL%?3>js)T&Dq!-!hvk4d)Ork3> z&dwUeF&R#MmmN&qHv71V=lvkpl(FXM=aoS=vPRyv03%36NWcQHf#LSQzd({8P>Kx0 z0E&nQ)HYz$j52BbV+{PyE<8PNautLv@-V-#UupvSd*YiV8AG1Ll|QYMKgMjR!K>@3 zPBVIG(811-+VwnNT12+_OdphbMEUCb2FpfaV_U2x_WjbQ25v8tThEq`f#;xWUL#rH zwI*W6NP#VEP=-|sCe2|qMl0z+hp_M{7d~sSwr9Un{C8iF6@l}ZO^&xCXFTf{@+sk0 zEhxWjhbSMJj4t&jaeORYFCQ->`k03VNSE_kll!MH!S*@P@$jMrvuAQ>*xHD5{03mz zXi!>>H?J@gT&D#hMXpUEu*QguP zvS>4Q=(UZjzPKM{ztt*f#W4DWa~mA{h<1vsR!VI6%8E`aHHQxrRQ};iyMh(i1nryK z$*8{+Wp*#vajki7F0ZF6w+078FNjn!tfksL=d(`Cu=G9feRuUhaWj9U)3sCr5Z$YN zn2!J%NCwKxL7MLF>;|~8-c%HC{}&cBxFuT;@e2VZiy*1)N7aM}lpe38Em}X9l@2tw zUuPs$v;voGemt2prSf=JOJsePCSOYkUJl$Y|FKHA%jyn4 ze0gCJgodNadJ2caviT)@1eE8FCwW1^hqVVPDSYtfxq3$26V7-vW>I;>W4FIuGT0pA z0%TVI>Vy-f6R-BN*1jR;lZGjuhsxE^6?EGP)iZT{izyYJ2F{MPFKSAqd>qesQJ3hY za{E+eFnxDN=Am_S_-^@fJX&bajk6k@M}8ldZjKg1?%q1O-4(5dfFkD{FjUP}`5J<| z7Hn9US_T~SvMbH%h#ls%T`N(@O)U=`UNTe2KD-csF1D~x{k%S0=3pND{QF(A0rf7m zAE=$eH(EbX^9js!e@fCSxvh&i*wS7;ZO*06`5nECMyKTy{9WSA;!GyzQM$$Cqy2}- zBEtV6ZBb<`+x6NI?eS$1D^$Ap02z}|5$#4p#csHt6%9q%kdA| zgQ(X9-(^O(hY}p(o^{LMh@HzuEnyT!zKmB->sOeElCki2?1c_N+OEvxFkY>td%a!s zY6g`4cs&VfKWT#hM3v^4MY^MMx6W!lCVAbJPx@rF6GuJ6Wh6EQ*uy9mPy-^$5TN?O z;&%ZTGyumVCRq~U#KSc*B9K-BapxCByLBqw+XmqQFT7@Bcs-rsw|=)B#b@6mzGY?W z&NJkhPXxhYGV5HT-VghRs(m|rV$gXunvcgnkVa=Bdsv@eAM)`(KPJ4T2d3dgB+zOV zVt}vfmATeoK4gJHdl78!^-u1n)0cr8mg7u7=0~^^_jg1mIT{oc5}6$p*lZ2{el~f8dNdhTLFI4!PV>8yJGT#P)z<|5WpUlz9Cc8&Nz~ao2mxf}K zNy%L0htQlai-%g zWU=Qx50fADPW*7+t-#8n$kt-W-Ct1;4|)sT=&pJAJb%T~Ylja`{1v6aW3Vx@zY^#% zQ*pa4VyCNQic~C6danal!Q<_G>rdxyRFH%!Z9BLS&3+ws_zLZuxIjNbJA*}hu`lVI z6t%@;c91#~t-yW<8lWUdWTZe1n!hojGyu(=iz=bjMG@~ii1@<@S2>?RpuXwih{nAv zC&r}4S+?6Zc{+Xk{_fq_K3-YEq$y95q<@0g~ z(*qHD0z)^8mjkwIq}~#T;fEPuMKPL*iPHVio{nqx`lbePYo9iZQK3S)*R?t`xHub> zeUav(tgrIJ=WJ88PX3d2i-C9b6g7U6lh&{H%=0rIU1y4y8Unr?Aa9#jfqPmlhG$EE z%NrlYD60k*U&2t|IWMNy=tWHT>J}^2A+0yWG~@J=$Bp0pxwE zxYBF0i#j0{Do(*ZK-KyH*m&|J9jxXe;qPw)tc(jJ1ahSXAx}WrpWx7L%2uAyFj@R# zF?saOE@A$QbY7p4#^wk7uC+S=&W_538fkBaNjrWX1E$LAJ{s148X2&dKnH>J*9xghgxf+lUV0<~K_gvz;%Fy(Yra9hzl zh!9kIwhao`a8uMN7E=c9#;3sI>D>H81Yojb-) zjFg4EHRO!XL*SN%gGJT>6DErMu3i3FVnBEpQ;;<;WOJ{tT5O-stxVswM`W9-OxBaN z@Tb2OFVQEXUOwk(UTse|w%sveT?DhbZ9b8o56ICM?E1J5%(glpxLcX@@UJ?It#{pA zR^D;&=EVi(B&{#qg0{{}T(IrKFaLt&E_@?zic8%A^6ZxBUv)AQSb5O7Eb-~g!D1g? z&$Z!wclJD`X=S4*QaKq9296R#ze#SmmWE$|-hsCld#?{2x7T`AywE%NM|SoNT`?U@ za~Ez54ddc{+4@Lu4Vn!;EJ~ib5wAjZ{Y8$ z(R|}ZS-ux?E$;%_a|)MFo8$YPNqjzcP6A>r)<|j#)GBjGJP1GtF&&gI@RJ|0^m}^} z3VxuBx(rHvyC{sv1`y*U_LeW95o|zKT(`U_%RY)EYlbpQ2-4Mb7Dq-d;jp+HC|<~P zOw?HV@SNeGQnLY=9)(`%*2n#?2Czeu{W81=ugX4CYQJXkxvUsio)$aAWooC1vsJES zcMu0I13P;$g}&3j65%pOx7;ale{*{tK0?8+D7$Qr@l)37vGj4Jr^eA{cNurrB{Y_X-hEr_unQ%EBpL=*1`hjp8l zKAvN);uqkT`S3q~AiWS@2XH+Skx-SHmB*ZjF|TT~jXfG4N@?1Fp3Z9fb|eheU3*L zo}5=?U^|>7bbqHo9y9i9sDFo7*s4MPCB+o3o)dxp+*g2PdvWmGr~yaJjQ(bnpDu7r3lkVy=j%VAmyeaiNEs?Vz6TI%OO`*u#Qt zo_r;5WEf?O!?@yLc)r|(YubfGihrOGtdbP;?%`Na2th_gQ`dkTw@k} z=yUg82Q<1cyLw=vq5&qhquRZdgvDi)I|0ppdrFc##9%V&9d&Niin*JskR#=qDBT61_Zi7bqV_E1$h)+C<8MC$x(-)5m z?{^GnUacp_h{OB+f-eHyI!w>&7c?51f^A9_W?~9-4$Sc2(O^FnB35M{0{u*SF>sIk z++C)rW=$8-X1mO$*wN!8*)+%HXkUAmi_*4Yi=jx{+t6yGJ+GFfs%eVU`PE}PKkOef z)zn;97hDwdVprIIaC34cT^$N&6n*Ib>c)wHx{4JOCD7D|($+Ds<0a76k1@Z`Ea%H+ zWmx*JAW0${7<=KoiLU<-DtFD4g?R0{TANvvtAmG2py_!?!AC?$a-u5~bIWYFy@<$( zv2CVhY%F|f&n#;@rtSfGorkkW1f*iXrs7|8EsMlFVO9(!^lK#yrjt2OHD#_cPm{Ag z9reS$=)VD;ZpNa^yLWgRmM~nbA{?Ox^IJNFd?3%HR7rLuSV}x%z&k8*jeFnB`w^P6 zVTE1#Vd)5~gMGx8fek8=lc;}0WbGPOmlkzScPM{|hN@|eHP-EGgL+FxT{e4{zvcfe#oS8OEVbn~GHeI29DF>?pI_EAs2c%ZHT z9FoZn2p4hrQyU&D7c1r7@l3LuQs~Z$LNUnaFQx-q;s+NlUM=esjBYkHfPEVcMr5z$ zrL^aZxgJ`3>>79w>L5_oO2cBS3ev4_fQe<#N_lhNXYUOLxsI?zzqWo#evvCzZgH zEfXHkf8EV2_RRvueR=!w&?wtb2;6S&n)pe)+=maR#fem8Nz%J)+@Ui2?jwonj4%Ek zc+B|T48O#0%|G7J@>BnLCA*nw0236*$>IU#6;~R{D<~ukHwtXhI>(gOgWRzaKZRLF0Q(w(2-2i3~kCgY#)J?is4%N#HoSe>NGi!`)0}_|^rg z`?)ulkVPKCUY*JIwdZ+z8qd1Wk|dQi5btUM#=3Mvr8ZyN#8Ayp`Vm&XJ^tYUM!$V0 z^+OwTZS4Ajwbtm%Oc$-iXf_98`|<(x?k~0P3c~9u@(N(ymkRTcaR!MC0+RG(UY(oR zo`MSrt}6Gm#m&hZ`9a31cz2n#*m(+_Ut#Jaq4DR%=qOe}XwmDTLJgRU2!^zPM(GmQ z1kk>*LJy3!a`sOa6m{uj9*l4W3<;$i-den5u{Oq5|9o`JqvaR_PRa9&epBjI(*k;< z7o%-}S%51Sl6cGTkf)k9Y(55}jjQ&;7quAMq4eq3G5*i{`&Z=0Qj@hWwk(GyRBG=} z%;)3V%ONkhDc%q-9L~^I4mX9b+iBkC$%)%Ze|E3$KsV3&{gv*{PyWt7sW%E-N5Sof zZ~Vj3*`ClzS$=BY+si*$4rBaL6SqDy1Hllc1Zd$R&Vz8I4N4*>c~Aiqb|bvq4iIP%BYNVafMQjoDy2`kwsFtEF@0|#xoYic&_)3MQLpO( zB=f8#?FzHxvbYW_N%9*5@3Rz_Tb&Iu9L$BA?1gNmr~fkE;Zlr=`TA zg&x|`uAM>dxD~oF3V?Qq*Q`g_tWpRp^nFM6l!xy_!H<1|Gw-?>?^8REeZ?bg_Z8mC zv{FNK=MSob?@iogv2?Ichj)qkj3sW@*Zh%`XVP4ZD8Pd1u0sWuAi(UKP48P+t#=#| zdu;6wIx^XTyOF`j-$Q!XBAckbTD(!3NFg4`=pxWOS{^JYIC^>I$f$1NoDBX1Ka>p+ z0Yw9nf+#7g5}+cvp;F7;*Z$m(j~?DnBqEolCd&E*6DkkCa2|Q^NNi7UIp%&IE$_8Yg?79RO11_TrTMSI9p#S4B>>3Q9sNDyfz7X3YZ>Jqn(jNJ>oA0W3l zxk22<4nFVk#x#ebP!9DsL52zf5)u*?l9e)99ian+{bKHXb2kLn9kex&rDhm@{O`(y zGyD8{a}-|UnA|<_D>&Ql31Z-5X!(kVFY;l3G6XGzV<{Dxh(_&isttjYPz)%a578Y@ zwkiz{HqKVtx2Yay&6CCH%~whrG9k;JG%jN+i;~tNuk}wz#hfxvP96_?Njk&FFL5Yv1~6H&QRF+Fc2dsMX6 z>+($P*4@v&`?~N%bkyf;K0?o#189|=(NK(1biO*y(jK#)b9G|ymkV76pG{umSR=;X ztpVSuZlZNUpYYod$cc8JJZ-7iPg zW_&eZ26^I2g+u!i{$`nYQiT3Wf7=|zWvu<>L9$Q3gUPvrPrgehyRZt^#DSeUCyqy2 zMNcGTNCCmG#s3{Qct^*i%j%fJ!DIRso#Vx7SW>S?{?%wnt224npT!&W?X-XVY&e$~ zwmjrD2(c9>-Kb@Dz}|uK5uvDV23d&@A^kp*hvq__4-ry}%UPDBM2%0IXkQq+&kUi7 z&9>FHv)8{qjh*>A$}I}rBwPO49CMdivDMQFp%h5HA|JfPtI0ZJaGVLZlI3ou)>EaFu8M%je33E6;a6oeay(H$vzgx+$H?tCZ!={|Opdrha zwsqt*o6jUI^Wq-2{q}DjPd;&-(q;AdNLv5!Nz>u(vJ<5By^p?GURuh@_|V&QytwZ9 zc!T{&qpQyk)?#(-YV1}xAel1G)Skev(a=$dQiPl8C0d!l9@!n!e&8R`owyL)_v)h3 z#w$xbfgM34ifeJEA*rx zGr*XZs7KxhJA$Mty@fBss$EG&#lR#!oQhnmt9Hx&C902uijOMGotX5A!FoPr7A)MZ zf6bHTS#m+6?;5P%|lq9Y79uqo6P*n}01EDwV=WEKT_UImrlN4lO&&8-6Pa$V012AC>WTU~lU?_h{eCC3mOey3ThqkKx*HBpv3uGdn3#p)=icwg3W-(WX zC>w=fQuLxM<)gt!#+J(VBya^vvrklY97LVM!gLl3FIa7|8+B8Dx!{u^dUs=(n`u+arFX4TANeP6O<8q?!) zwo-t{((*>9KyqUCNJ%v@T3-=e#>;D@D1p|!{it-brHSwM6}VV`r%opGbCKqs!_W5J z;CX9Q?sd53Y4Y9UjOUK70;?%iNj5uXAi0Olw$eLTQLs}l0uyNgNQ>+nJO2Q&ysvGp z9W>$)!W6RJ-&+PtvqsBkr_L6jX09nHQC1~f$?8ffl|68NgUfk35HSa?R>(j6(BVT2DxxlaoS)6|FU4ot1A=0*K?3kUOKEHwkZQU zOl|)+r~Zd_(iPf=C59}5W!2-vvKL6W7`6N!UM9$xwls*$VHAK`^U~BmM6G>%!0WaC z*Wi6<0=kjnLCdJ}VI*ArvQl~7IN7_vH?^YTpGix?nP(dPD3KO_g4}dq5hJlu z0gv7UD#?S$i@z&G1N-&Z(xkr$b^zpkpx8F*8w)@DOdNyJbhVOsl)ev9T5~sSU$QeL zVdj5-lPA#VejU#{)c>ox54+qx{s4b{3-uzEBDYSYZ2}Kk8@GnJ5Ds~A*ar!yy%U{F zD75pi$R8%UPC=Q4B!Pn)AAANytIEW*!?2*EpvsVh0i~C(^Ozp^hIsuwZy zjuCV(Q;mbhFRcvsLO-Yzb&j%1h8r(D0f6L}T=z&_N81bdY|a9qr&zmWuqzyv7AL9X z5BK(z44zWs0=6*h4DBUCr`FwEHUgkp(MGK1sTHtL4zSDtd_h+H=i<6%PLmJX&eN^) zY%%CL`yY!H>=eLFH=x=oSca^`c$Y+@XYvXJOIx z>OzIE^EDup>)zn2k@edCS7C%eh9Lgnf1`tSgR)N>Mt|5=OXo#IJhmY3aAuW&>6aNy zfG~S_9}kOmn=1o$OI`eb*xr$L(cPi{IQf$$$N`@JfxfKTr)F&p#>X~fY#jpe)Bh2$H!8AOa8CF%S_~)EbYvB}#HjB|(}!pvQETrG z@s1K#)ugV;yQKGoc7tr#p!jDv1bG@$A`LZ;0#?A5f6i|99BciY>FBOt1XR0(I!wUqAecgrn zW(Um1OH1j{Hqa9*8@R2zTfJs=jLyp!dkoHVEqM)U{A`Z6g#x`u7RiZ^~MUWY9m_l0OfFh2Q6KA>4$Yabj*n5jmZ%SVHU&bb}c z{|TfSTju4S{=;djQrIE}${_pX(DM_W7G!7u9v}r3^J0Hl8bovSDkgT65_F2v6DKK` zKy-A!L$uXYnAJah;Ak5TcmMswo+I5#AD%lgb++f@qtA`^tjeALkhN#txI$O%_>x@5 z%(5j9M$6wM)AHZ-VH4*Hj<-**tLr_bV&X~d##qHqdr~RsXjf{3LYxeXqW+RGI)1 zS!%4(fKSkMH5yF-3oXMUq%#(|cOKY|hPDHZkWOgCQ#5*X|E0~)Mf!a@hKum&Ex5dG zLg*C*h5olLAVgyzDiors1g_AI(qXOE;>SeKFbVC9N#SoA-;R*J1EJ7P2z7HhC`wtG zp0u9b-QAKC9of$8+o5Lc*dyVCTkxv!A+%e;E8~`R(HkOEz!oZ10G$wqj;=F0{q8iZ z9gC0-EOec)P;kgdOQnkXcB|L><2i-L8g5ztnZF>^qO3osi;N4-LnHHkl)8l7f+%%Zuvt4u*I9 zm6TaX(CV~;t{Q=MQxSDF&9V}ms?rcbv|4@?y$*^8meUZm8ja$xp7S?1<^Iw@h^#~N z1EX1iHnmjk5cI^~>eQ`I@9u7la{Kkp>yzh6bLVu=p}t*I1ikvwWYDT9qNp40W>m^= zrQo(3k5ZQ^b?I#pU7cFMaC@T*zjpSM$#DxJRdb%2xcuR@*Vc`^FG-s}CvL@sC7b0J zh|N9SvEF(&qFFY{$^!|78^gm3Vcwp1M zhZeP-D{0(p_iP*1{1WcAZN~Cv<-hG+u#g+`+P>O({qrb)$rjp2)y`jolr6vV+T!|tYEh!btowFP8B;myBUwbqtyFu^LXwPma zvcMe)(ziv5-Mb&5ao)STClgT$!|gp_V3{QmR|i^>fQ@NaTj#zce?wbTB*EQMTnTY8 zkX=x}cmXH63&2WO>qhxRVoaomH`?eZjfAs^Hs~&UwP0OPL0|nCx{0aw+f&JUxF` zNk<0_&G_)KemLY`UEnOf*-L>F$f3~NZQC1zg5X$!;k?xa&T08wc+l-l4&+Wa48M80 zBA)L8$w-}LKdj>lJ%eD?$n;i52Wv**lrD?TT|q3}B*rWLb~)IB`JxM=zMk}KAd)UW zFFr1oDqD^q4ffK?TY|ZY_6uQv?hboOlD(&+r>iH8^b(V@!)z`ayV%U%(yr*KY*b%1w4Pt}?UtF3IK?4Djo0q^Y{BA(7rwXhzWb4%9(;-7 zZ!mh4D*lEYq4kQ&@73O6qEYEUb!fy&kYV*GYG~Pgw1K9SkoKmOjLt*&TZVM*R0(PC zREdd>!XORZyCu13ay_b7bT1r&2y%8C1HUi`8iC&7lBmBj^8T>$Q27tp9em?sJ_%uE9o8h1S7SUS8 zKz;_oNs(TDRn4>(n?dS2gOZ}@m_rpjM`n-@sm$@Vh|qBF5G6H(RNw;$f;5UM42v>_ z=GG}i=g=dh-d|%dqVh(`%Hj7h`N$K=FTjDPb@bae@Pvp2lR>Yeu@%qJQvN{0pK>V_h|n)yw@|euNux4O--i#iOiVVbryZKu+^Okr z`nc*MIZ}n>!Fvkos&C)-7od}}cR_Tjc@WVYe>;gfdS6rwDXNSuT`2^vO(LTaJ)vX0 zb@)7A)ZWV*+PRn4?4hmD@VWm^D=9@d59-a1erAElixKQxJBt2QV;VKm=)^%!kR?GZ zqy9G;#WC+nqark-#qC$-`!Cs7ovR+jdAscgytxYf+B4pZ)~^2hE6z;4^Y@64ewj~=VV zI08ONJVvzWM-9eN%~yn|v>d%&fD+oqt`-K&HA*DiE7j>>ci!jp%ITKu=;`bk6Q$Tp z@Hgz(t^;O{PwI%A<86Ls4vw1J@8dEVGZI}LLGxw#+L*%gD~^7&t?hSMUpDOglIBO{ zm*n?T_!SMq)|Bk=kvRt^-8=XBvrEY8x;MI;zWUB<`Fz%bFHRiC#m|2}XL;kYm(D_* zoaWp%jQbP}*zeYE!UM7P-Us>D_AOu3tFS$H?&^{|uVE+aDc(euHfJ{s(}F9GuLw?? zQ$OBhGEsE^Z>;A(=6)3I;9W#}BlHr-?!}`;K4=yVMhFBB2F~Qh&cq~9a%R%1$FMle z{Wzm{^@FqLY+Pd7<*Mk$f81;Bl0i{T4M|fT%47AcBnjYtDmEZ3Xd1gWHmD5-aU=Xb z0fz=BBy@Ck`ip@if3Y^DGxzDzDbp6;J8|0LYOg0PuWydWD;%1#Xkpca+69v{b8|DZ z`uAt&S-6D%m`@cxh3)MIYMTcq9pru-e4yl*EVK#RVm5|`C~YlPY-KHBJqgX5J58SS zSVH&JL%2c7!v^QaclU%%?elE+5rcE1x_ct0=JB66-Ok>9FiCJHWDStz&iB`&&R5j` z-#+6ulG@*RCq9=A19$IM#!1z`d7PvVj9bASCn|QwwQ|4HEtf0N8~n{lS!NHB8pNst z^_z3J<6$4*5c%mxm2<>87$3s!d5ZN$(c%6plGs&ItjSVBl7-$9WuwKirfkBilGlxE zc(71t4Xe1>gu9*lKYot@p*V0W7!EqxO{#ngjZ%^WO8`ZNB%P$wY8WW`T{H?pcI6NL zURCmD{hk!xg?0pA#NFhkCKrp83++wAnUH=tgTDpVC3qGec%9a!6K zBInEs!k+ZdOgK{CyEeL=3}Nre-`}oZhC|mVTjvIjC9g%;vhv30qc{jVA{- z9;m8Zdw2@+dS7i?W97I*^| z1wK!Mv6}Uwm8s|@?W~H3CeF2^5Ifrt1aTBZ0ag*zq9Z;wCOV3ive2uLSl=JL&L9yd z>XZgeFy`!+LAf~ELHg6qzpQNdWkSkjL)`8)Ukt6+FV_AL(pWOO32SkrJMH0OMb?&)FNJN& zeTpPkG&&&! zc4E#MW~DtSQLF_n1N0|uUG^5?&k*lxBER@Z>+$`|c<~hZlFY2G_H8Fg8HMsla>4fj z>ETPo2Z!|XeN1Ujefh!s;P$@WP`_nm{-M!swDW^+yi9+L8&mi3`&x8$`P_wIYK5lwMVyPR|1XM zqM09~)kp%i6T3e@!Pao7%NjtMBuh9JJ-=H-}UY-d-iRv;=-LTRU-Dm zS^cvL#zbD0}EA*X&dK!a^Hjrr%4i_Bz>uuhLtbvW6%(CsCV2>DyPN z{RsonK5tlti>PsCBGIU=65)^qB#fi?+fxSU5rWlfJW8t~^r|DhM0j3Ps>2$M5-Y(r z(;Tu8O8l40q_HcJLfFBi7E_k^wJ~L0hrs9d@7I@}==EUHGGz)-Q96x^A1Dko8VvNC zZm{S7v>(EEEqGYV^?&@Iwn4P~g#N#1ulPgiwN$ zLxv1aMI?lP1R6R?kyIo@$dm>oh=`OBf`b$h=_XPnLvaWhLdhVsghJ^MB!p6mWN9hE zp$H2nsYNq`M>^_KrlgW)8+lVhT)z%9udjICEf+D$ zZAn~B2*aWNiFuCa?Qg^-ZYq-RPJ@~l>sK+M4zR-cnrj+asQHcV(ZvdO*HfeEX$hoUSj$l&iK8+6W%FD zHhGsR({QJL0v-0d;T^e*>Um1NMV<9w{}N@gV5jj+7u|Kx_dBpVZb!TjAI1rM7=vD= zZ+y6o+=aR+UW^lXLC@GX1bx2)OT-KDVVsc<|DoqA|9rTO^s$13crlK6A)blK9=4Bt zd(M10SIK*2YAQ-y)bD`MI&h<^40zv2VgxR!73y=Y$$R*V?qe?0#GIE!nN))J@)>1P z(JSsyTXbv$F{xE4ER(P|IeaL4)59#!o%Dx%Bait$_xKNzPM3z+sWJz{2Kwqj0WZed=)e1Q25iyVs!OB>4rRt44~)+?;v*kaiB zv3+9KV0U28VQ*o-$I-`ej8lp;iE{zx162id|Z4+d|`Y=d{g*#@m=Bj#-GFgLO@4gnZQ562*Gbcc0w6K>x5nj zGYC%*ekP(NvP@J-v_bTon2uPJ*gCO);yU65;xoj*NN`CcNvr_EYm!EiZIX|qw4{8b zc1XRD&XB$#!yuz1V<)pq=87zrtdne=>;>6Ra$#~Ea*O0H$^DQwkdKm|A%96BL}8V} zEk!Ox8^sdEMT(b{WRyyj7Aaj&W>D5q4pFXAUZ#9TMMfn^r9ow#$~{#PRVURn)k~`X z)U?zh)SA>*sXbFqQ$L}hr7=O{k7kVK0j(abN7{1QQQ9-KFKK_%k%`x|}V6hMY02rv4asU7U z0002*08Ib|06G8#00IDd0EYl>0003r0Qmp}00DT~ol`qb!$1&yPQp(FkWwHjdoL0{O{tghI^$I0Ow>-~`Z9aRyF+D0n+w3rs*r$lBevv-4)( z%&Y+{;Q?_Ni8%lsM}Q5axC?L$N!(~0M+LVUCt%`5<0-7*P2*{-8YzuuaA(*W&tlDZ z)_5LU#=FKzoW}ARFA#_E7jYbW)%X$1@okNtV8?6NMH?*+pW_-$G^nNlhkJ*}MIQr< znS=5=r`5zgM;10R9BGX*Sf_Q5-hKLY7{^43*dtrbj>PYy2MdR^HHl0d(cZ%l`*K@{ z9xjU9yK>&(?9nUDG08C_EE78z5p_hrQfB|jsY(2y)}>gMFhgF*N=H~fMQzKh>g7wW zN_m&7hfCV}IGd=ABl(%)HRf6utH-$|(R|SsbfYb|xnfZ|g8c>a^~AR!y2APnnZ;xc zf9{3qr%!7E8~m>1vv?k5yP9hW>eBPSJfFD^B&(*>y+z-k2bRR_vN~1CrYV^O`H#Nj z;nPo5s>nDF{eoSTqh8|o-e!4&{j2WJSe9sR@w5|(Ii#h^cThqZ2kd-VUcQQX!qYlC ztnTskD+;Vidqvcn{5It*%e!-23&_(e{Eu=U3W%(T004N}ZO~P0({T{M@$YS2+qt{r zPXGV5>xQ?i#oe93R)MjNjsn98u7Qy72Ekr{;2QJ+2yVei;2DR9!7Ft1#~YViKDl3V zm-`)2@VhyjUcCG-zJo+bG|?D{!H5YnvBVKi0*NG%ObV%_kxmAgWRXn{x#W>g0fiJ% zObMm5qBU)3OFP=rfsS;dGhOIPH@ag%L&u5@J7qX1r-B~zq!+#ELtpyg#6^E9apPeC z0~y3%hA@<23}*x*8O3PEFqUzQX95$M#AK#0m1#_81~aJ=0|!~lI-d}1+6XksbLS;j^7 zvyv68Vl`j*#wA{Hl2csfHSc&MaS|^Hk|;@%EGd#IX_77(k||k|&1ueXo(tUMEa$kz z298P&*SO9V$(20GXR8!Qp%h86lt`)3SKHL!*G!?hfW=~|jOer|RqfK1R;688(V`x1 zRBB3HX;s>kc4e8;p)6Pao9B$EskxdK=MDHm!J6u-Mt|f<_e8WS9X5kI6s&J4+-e_> zE3!{mU1?R?%zwYF>-rx~rl?c^002w40LW5Uu>k>&S-A)R2moUsumK}PumdA-uop!j zAWOIa4pB?622)yCurwR6C|O`;Ac|F3umUAvumMG5BVw=uBSf+b0R}3v3qbXp#P^D03fHYtnC?oqAXB4pXEPtQ@F04-K3@(e4#g+%6N-G)7R69k;^X~m7J7wD zk*{&>0J#ZSzcl!MiK38*9VMW5cvM44v)>(BjH<8MrZYPjvwjpu&Q3pL>);RR*DKyH z@qDZ{afz8PV zCP0jeS2CRY(H&op+Dlk}ttn~UDB>NE>(cULR}Y&dUzbBYejAQx#)?Oezw-IVIUxx} z0!hZF>-judJZIiE)ZeEVXMMv(T(%->=n^Kv569oryCl(A=LgvcJUxl1%G%ZkAF1<*9iwq=Nfx(O=A zZkHd&7oBs-T@DQ@e196d*b0%0x<(DEi|Ig2fkKp0H8Y1)UHbT@hBxDCOnJGO2ObLF_FqZV8m4K$RwW8s9`Cp_dA8M3dBEq zq@H<=#9DU4bbd+lVfKUE9 z`^27fB90gWL5IJd4c3Ml*28-Vrz#(~lJtL|ktS<(oqaP3>27#%sYeyVE7o%O@)+Rq zd`N#cepv>10M28irei_PAk*ws*1=Zll%rL}oW7g7FEXUGtd#25=JXhd@@-lvV!Ca7 z*}I#fL+dXiBvl?X(&M$_Rl?u2jmXLzcZkSx9!|EABF>De2hpQ%KVumed$_&d{_?aL z)zFlqww|-Ay^dr)^3=*l=nC_OSiN}FZ(KM3;q2)4{1%6=aYO;u1o#~0@#T@#xlP%O zav%NZ;xPa5=+8jac=V-UrfNUCc(|&zJ#m}hQ)=UxmJ&N@_YH6kDFjs~BbvqJA&cjQ z#zq~zrSsL;R$h;)WE@`wdZ3U2PEoMu;Dk^!q{g$dDp_2=Gd}#2=P8d&U=(Q@P^({6 zXZroYg;vVyAO!R)-9w8mZQvImz#I})`qQ)?x3d;_h+L|R*l*pLOww#D5E)DO0qIUK z79%}@Y{8%ry;K(m#ui!GuWk*vMVpg}8>3VA2ZB(8RtaLgujj=JD zVEVp{dDMtkkNIU?>EdnFq=?Tq7ZKxmpZ*wjhaZlt{haex4L29`xFl)l>c<~Yb-2}F zTy|XDSs=70QFS1QbjZ|oByn*fNN~zDaVAM{A+&Lcs`|op^HoxNJmiD$LEeIK)*a(4 z6Y$5_J1PtvwFQf$5|0FAcf5qdtcV*bZas2>#L#@EO)B7SfTeSb<9)?iQe%IIn9&_b z9vNK_Wnv^P?;^m=?(J_Vt~FyLFCUr%?98G*x^akMeirRF;QfKW4RThpIwdOd!Ryf@ z;M@%-*H0ZgGGQz`o5LgaR-DrIH+78K=pr3eOJS`F&lSZ1)K(vjQEoZBbR56aj7&BX z$VrEwV&KT@XrPX6Gz;uV4pGG)h7kPt^ug7an79{0j70E!gC9%rR#C~+Xh~#Tc1>`K ziM3MiW!hm@DfWX9sW{O->ak2$jxaFM{)-5G3{#`S*#QDB2B;YTvA2LGNjoUX;3Oy^ zthCj_eev`v8vZmPy7ke|4$fRJ4g{$8IP4?}HNRQdvhV7)8?t4jgv2Nazt^kh_A?&B zIm27qCF{H13>!aR`*Wo1ZR^94J^5D33yAWagK-z2+%9@{(d17BtwS)KNQV z;G?C}Qo`F`h|xe;`wg!?lwlfFo>oP%$hfcJvy!N~yo zn_}W|MFSiqtR8PJ;kWFi&MwvR{1dthvFFXsY|GxFQYuql0k05t(C*OpTQYinldpNc z!rsPE1v(wK%0Y8c-9u>k0$oQMI)QM9YFzflfeOKaGD>v~Wh%IKud_RmJaR% zK%Wb3y~G16XgIQ8Tyoe6$Ak z*N`1G^P**h^EN1Z)a$2t%RATj{o>i5{-l&Tp?zFZv~3RmaKUqaq$2;01V9qeJ8fCh zfac3(6As@dO&=!st1$C(@|ZqebSmT@;F-4Y4iUpTos>WTeZDS|$Q6J?xdEmDA53z-svdbcQB%-6n@oR7mygnt1s6@_8| z(cs^6(3f9GPgT10FM&KrdPvVv!_qvaAhASpjdY6I3TS$uNf2J7rK9@KTqH`iCz z#dO1dgMUgOI92G$Q6ey(`kxEM<*;^+3N}+yeySp~)d1cIC!>8)`%XJUV{*wvN>SSVCIUf<8neJSsVKtXqB$Oh zyDkA>GU4bZj3HWtl(KKuC#XrcI8y?3FnjKpg=ppj$ZF?Wtb%AZU3T$Qg(oDJS6mOJ zw@E);-Xibt@8?96o=>>3Q?VhoZ^S1P`NSvCDfZD^Mx!*aT)zu~V$h&V;tjGC#X&Pb7K0PcOvn5DtnWqM)d}_`A0z_fuT=QX-e9 z5^E3#d)Bt1Z{+teR4#T{+*39R6nBIz;xdTT9FxLvP5)n$o8rU8SrP#zY1FXOVVAQ9 zEekG`%!y_~PLU%*TL|Z8H{7ZHhzqJ$#T4t=wJnLFjN7-`d+SpOylxGf_itIP z0v!_-d7hyn=Sj2-00xz(caJ?=I8knI6@X7oj!jllRQl);jM@QGda}<6d&5kfUtrY$ zSdmsoe65pHtEz9bnvDXH%+3Y&^pFnQE=4IEbwMNP_VRLy*TK4 z*voL~amDYl1?Rp?xVKmkV9*O3D=X6JmjBDebYg^<*gD9@B$~)A7b{5UWow}@rb|I1 zfnmCrUK-PaBB9WO44_LEbS3DHWRv+|h?Q(>8l^+-FD_49j#L}@8)PUVty6|@AAivr zyNQcFHZ^YTCCk0d2bb zhNVBMgAX-;$(Snr5|RDilrz?=gNeynSrqTjm?at2#GKNZzL!Yy3@yoO*ye29_9RrY zv7pRY)6_U8j|~87B73EKz6;#xjT!tsBonWQYBx=!_w(tNWXtW6Qy?MwG$wOwu#WsC z<#C?08di*H?ObplX`}PI2Ijg^7@+6?*fbA^HtJNLzEFqFBupKIQm=&?f~ij5R!g6J zE}p=HfXCRM=%~Wleq-eBhQ-cu!DR*~T3%saOzrA!*~S2}c}MNqVK@TdQQSbF1EzH; zgo8n~S^2;z)B7lAwxk~8LauX*iMWG;ab}pE_Z@~o#m0i|r*JyXO3%(n|T0DtBydU5q;imD4 zd{vqAFR>qWS-&dlKDfds{1&Ix951qr=>J zGnDbZW7KR^$o{PVfVH(@>N@p)$I9@?e6?ZL2^+^6dB6-?nf+M8o|qeM5Zk}K?EX0% zNnLuohUq$`h_HMEwn0@L0(14t?Q6`7b|>T=SZHt~30&KORwHM$ql(UdJABu)az0gx zc2Czbn>{dBCfBT($&$J{%kC{KH6zXZQ$F+A@X_~O zdZMn+rpGa6(`b6W>BFReqJKHfSD9ZKhD?VR6`V8Q%xLY3I~*@_y0s4ZW0NYCT$rz= zzU;k~yJtBnevLB90d&tNL+R}WREAt8_tC*k3mnQr9*0S#YeI`7*M1;!vrropLx2)C zl8A2v2a(!&;A#aQ{GPtuv3-~NbY!u|jwybneP0eYo`t%yvPqeiBhq=$d*R?VJwma5 zU*46Ops4*;a3SShW-4f&Sr~Vr&VLTOM8Q;u6fPuQ5p6F|0-D42Hb{`-4~@(SGqb4d zF1_cc)U-~?rjgH`hl-!4x!eOca&$Jvcu0PAl9pZqr#oQkf#n`Js@B<^2roZ%y0qhH zgnO?@dv-D$d-=S@J#kB=RU!hkO7ZQ3o+%>&&bLp-7IVi|4+I3jq=y^~hx3-Ii;)ll zsgX{)@6Vcmn+8VaS7R+Y0IvDSp9Oq$g>=Hgaqnk2u*PYXP!ZUclW)RIU67t^`-J?y?@*v#;Py3NaO>#IEDeN+ z7Z>sghK&B`ScjV`+5e%N6-h?t^@uVz_gfv&fo<-TZ47d>49KRLemgU_NAjlQ|!@++*??9{eCa6~AO$5WX*FaIXE-a}z z3H@DapFDV+{^uocyuMG=c+*=-XVBmmK;QqF0z$E`fb z_@#BMIpb^nf~KzYDo(M*BEu}XI*JD53OelwCN|mjrc1q$p!YoM`xR;tGw1vVWh3piQdumi07? zgOBG@Bp;Ud3YaR*+$8M6ebml~UvYnDf&`{$+;>WN8wn(lA zMK*^4cTt8L>!zb5!du_CAwns}s-eF*AAY!SpE;9K*B{JjS0kf93YfmOJrb)dHDUxV z4^cgLl`O6SJb2G({p(8|dz@Gv`!pbRNI#kbsoZ=yQImAjtO2=`mW|yI3$C-pnjZZ| z;&`2m4q57sBXUhxBaQRk$WQnmjSj?nfGU*PvFh1IV-~mE%M>YxOm7Dt(W@(;^!I6{ zJ7K`VA6QJzIv|B()|b$zc&##>r*NL|D}3B(hA8-Uo=+*$pQYq%ZA+9?l~mgj%D- z+OD95X@Fu-N%|}ibEX>f?pk#zZe}FB+qe`NWS&Z7t+4E8#H1_RuOb&RXOKEMfH3piOrG&|!9^ zCTJHQT%_t$y7PqVZqU}Y)$O2&zR=L9oj0AsY<2vcw^=pVh%dXOL+5LQ_V9u31|I4< z9M++IjdLw|Xu#AccW-f{j(g@e)yN#}(uE*EA$Oe)+<_(PMzrpNHoOYFv&*-ND((f5 z2JRWzr~gX2eOwn05(h0>kMV|OJu_c3k|6yR&KCH?JVEg;&6Aa>oQ(L1tj0tB8SGtz(bM|6bOf;wo=$LOL+-MVG39b3cEcHjZ-?3ZfL>bmSGRCS1KdiHH*?k}< z62WL-wx;9VQLrb9V@CX`0nQ_E?U4wg)!m zi^DRaU~p9o)_|(N<%39W#u^2l>k9OW`147hk{`Z{+zVMTWgs+8EH!~#S4ScTVS6_K_nvjP4D(aKnGXlil1T}EHe zj@M)ATFSiQJ^CPUmWoFm!81$Smeo@_7`E5?4aL}x+u%2ER&d1Tg`$JPE`MC4Q)G_@ zS{|L2Xc|8I=!f}YR4KK?hSmK5VmbiE;3o&1i!pBDkUHV-=)uE8S@J^Y)mh<}E^bZmDve~ntRYa3+508Ef>^E#ys$%Zd^7#>0+9|pS1bF9%*Qr7NR^AcM zmKzFRRLHfQPgv(&iZ4Clo2FZD5Rz_9YF9}THt_|1x5NxGZx9Qj@LNX42Fk>kA;ab| zxy-J=zeU%S%6IsPjy2l^Y6i}00g-0Z;ZCn`dJ*W$d-^{2+pk^vtI6#Zq=U=d8H&8s z7HwxEpFhbdq+1Y{2We<9$Tih-CPu~JLxQmw=BJubCvkQ5ro!xlYLSz08w-%Y^+$`q z2>vfr@5?YyTjE*@*}=S9n0xrjRwDbNB_ra$mDyH7!`1V4c4lJ?=vrIB1jurkBXY=* zyX+4c6u)J#Ro1vSvOjJn5ELlVr16`Vr_MqRT6LD!MJJrfn1k;zJ`yMtV}(*I7AkyB z-lmezWqFNd(y&3spo(bI)3Z#EAnDVy`^SUWyGdh!PK?=y!nX$eMyQ)C61)_VF2s$^ zwxUn_(fwx`_9q;?6ua+^-9@t%w+JPB$Bu0`w$-OMkyfNY(mK<&!pgqv<$&V1Bl{%o{QR)yVor1)51hh<4ezWFQwBJafo$S3g)lIp9&Gb^P0sGd6 zI=a8~7iALHo%ZMLv7j9E9*hwPmaOuivV6CBjJaK#do8IObHN$ar7uRYsD`Q!&^UKY zP=vV0shZwzqVKU`aM8H-E8`Qjl-unjuA7$N;_BR#YN_$_3`Xi|ObvZdE>*}T_gnxA z`NN!snbgqa%YzsK_$}i#Wx-g{6~pBXxG4DHQXeH>IJL8BJ_E9_&xvzAyABS>$pv{V z=GZow{f;_9FB*wl{^HMbGd33BP>&R^St*Mvr08lkTC-FQV=Cu6M9Yp0&-c<}847k9 z6L2^!CD zT~$mFzM;#0zU1&8mjnp~lNTzCKL}4So{LQ$y4f>35nrIJ!U}gq^H4$a=D{ewRKGKI z)_KiUT)AzHffJ=LXfwYQ?@Pdc^6aP=qD8$z0&_AL(#H$~KI`1VVAYd(1%UWJlI5^7$x-?=+{3n97$awDg1C zrgfYZOR3o_LW?gS%pyltOyI3Ynp#faDiTUiD2bwyUHGnOIP5_5R=}cdAydz#U4_exp<^!@JhlE>qxeSTp|-dIIK3bsi_i?mKN$`vfo|=Dcejp_1lDBGnP(#2Zd+6*Z!KaQv`2j4c<2(BtEgE7Dxwq*1{=uVJpE^+lZDCyW!_EQ%VD zu@7FCoIC&tjeH~NFMSE;Sz-)cYm))$ep)=Szc*!Ojag2;kIso3%&Se>+?x8(2wiQA zl?4^gIF1X7$V?LpDIdE2e$n~zgRc!is;o=Gk7g3L-j&Aj?pK$Ub1nj^NMYkY{1t>x z#T8}B^v3TBcb+Q_+?=yfGtFJbn@i7Z825v3S%?s<{(VlrWk(h$bjtL-%5NCZmQ-31xD|zXePwi9KCNaTXTtx{ffA#Nf+A_5`pt?p8wDmJ2vr4_7%InmC@Sy*WULVh@MF@}sF`~gM&J9G4z!@&7d z!Q-}Mjx-F|=1o{*jM>Mo^lTR!!o(y;wwRDxMvO(;ji*b1IRW6}{daCKQd0z~T z<{wk~ZBc}C&fSN%2aPA?`hT_(w~dc;fM7aljp-InF$L#{$&|ztSXoTo@Fc#8_V_7o6@}gC-cc6kO9;F z+NX(VN{Fn2NQWL0~shS5bmFaR+f)~m}VVVmf;_Ne#=2jm?Ryq5KDa_EtuOvh*&ZOOJV|@gf!?k*eau9g$3K^=21F+iuuvc)5L}<`|zwh*} z9XuE@%QNS6ej)yI;v$R36~^u!!-N7@P7vlUK4E6>!G)h~6*hfg z-R|~W%F5i7h_(i*@DF~Dd~ksUA;Awf?43gxD2?+t1%)j}ld3tx4LX{F-m#@>-w6Tk zSlT;lZF_xvmYglJ9&CH&Bj$&05nc1OzP_!XwbM2baFC5{dL;diycLYvPl-c;> ztbIvMN0{*SL0(Fb$<1FDBjp-!p)|erCQ0$lWhX@%6ctQcA8#sIA~d9(&O&#N7u*Ct z&k$PlkByZ1ckTV9Ko5hrB)dGeK0nT8JZ=rbw84qZ43&j{Y9A<5^te9MZ2=;rAu#?0 zW*?e}Z)6h5KNk&e^bc+Gkt3X_T~K{ZiWzA89{taEwkaYoGCme~Es3HcdLm7JXsPs^ zG_u6`l{YcW`c(>PY)6XKhCro@0cHKhAhaGJaS_eLzuy#G*)``@ZHu0MWxyB)jsT5P zJ6i6!*HGDFm(>?+L#I?3j#bNt_s0$#Q&e7vF>yK3ackUs(A#{z<1hOY$}e2jX#OQ3 z@*)161`~#4*sxEH*DiQ+T)|?!0G2<)D(3(DX5_A8&zhq-PJdL zor*uQ`#2JjPlvR7WvKtPjI83`&BR>~A@oYz;`(wxAOe2IL8FbQ+`ID0)9wzM%4b%7Zy>dbE}}!)n#>9J7?> zINhAkAgKV9cAi75;_zMHZSrxOH3nxYhu7p)7l?=%uQqa-4^u7XyYon%{6tA$7U*Gh z`Dg!=#VzCQciS^dGKj&m*;1HREGiFm>_CEX2FQ`88x z`M5)R?F2^Y5YBljjf1s*S47Y6ja5?f4WIpkq^oEZ>EO({E>E!~xHEN*VP^+dH@h zzBN)ProDHRI{qm%_H8sS)|si-LU6YBaRiP{*h;F)=*{bCch-Yt!=QLae4lWo=la~$ ztyw^~pz>?k81()G5YfWPR-QH2iq^fEdRmV%)PxXAONIhg@Dv00rKB}*2vHMuF&L9z zaWUiN9kvGnfVCbL@xUrpj>Q+{bYu65M`}i_Ph)>-3It1l`M329p)zqaSL*Ud)+v^%27TvOc zku9fgE;G!|6zjE*FJuC>sxW@S(|kbxlURU_-J*);gn!X0#l5UNaVAlmMam4GRA~k% z**)#){BRZ^K+dDW+>%m+kyzeMZ*B?anhJwd@h&#UVs0BFc&EVGoBFZ&C9TK6T&o+MS8P(EPak51t3G(63Q)(JVVJSIDimVgD_0ebdg z1N;^v1%|2$O1@5!xmQipa02;+k zg%JHs(kqLC^>!guhK-!gscDy+*kz1A=7QG9J>9_L~Cc0^BJ6RnC=- zGDbIy9ilSv2_Q-kiG3qaJc|3bXPv=ooL=X7Z}vf@k)@?+^NsaH0 zslKG3x~SINU)pOV<%0}ZH&$6}#Ie9wx3$ZJO3f^HRUY$g!9b@sSG9ORGaUw|f`3gz^>NZ}*K zEz5i;x^V~8avk?e$K8-<838+?`0CM7n(29|F{FBSj!gW-f9VS&3A+or`bv>>tW>8* z374bfNa3%m65hhjT(_z+Y{XQ-KasYF>Wo)yCJa}ua_@6!90x(vc2J_AkPN%YgM-fU zzknRFFV)zx%iFpK{3Hh4)Y!Ikn9S3BaE=dL=kK?sPX2r-;&Bk!Hc!&`hk3^WvL`A?~WUDddQwqpIrqD!RJt?J-1oL7HE`OIv!jrLN+zzpguB`PnD*IxX zVYXIyo3x^Lxg9OP&N4Cl0Db+WTSv!7??a8sgaU5mm(_L((U`I>-AOkiK$gSOlHN{*K$IRrS36w8)QAqLTFHa6) zTI|%i^>FOWqr&zg5scIRmT;LbR$;Ru6+^{_4)a)jFp`=avk7-D?wix_FnrIOp`Lbb zbk#iPX=>b$S>;%HQsStQVz%qZRgGi|0Aj}_(1N0?dtfemmOlI zFYA*-pY-}VBawYX4G`&m%nzn-XT#}@$|hhkodcK$`A1%7Hh*lYJ@c@2TtbK!SlcZY zfq8o@8*^Yf{5?WOG)yz$<|OO%M41y<@A322HT`ce;+eC_41;`|!?_X`MnU<(?y3@- zRykU1yJ>^ZqWVkEpyU*;#~a8zRY&xVtdijE8ujjyd1zxeXRYmi*Q2*WTG0m~CNRz9 zenBqz27}3@^$OFSm696wfXl8t8YWs+cTh!eDkeMMmh&MwVyE=0uSN}RsFiTIV$7a( z!(w|@=G2-=fJ!=my88?BFWjDYoiWvfJMphvh2T-N6cqFw4oa-{i6_eD4{^yFZnQ9* zA*7lVPln2=NbJia6bpjP??3Xq64apt&}G6sx-NzTg*Dg|jZ=r547A*p*@?Hm34A?y zX^N~Llu_+17Vrj3jZaAbrsc)^W+inaAhVjduH|$r`Rk$S)=y8)vzycRLgh!}4cpABENa9&U(boj3n?--f)nY3Sdg$-r1;c zW7tg|tytDwlX4s9jmBWi=ZsEyFMsDO>$@keP9_(t^<7jPA9K@uCHS%z$#HL9tWTRz z$opaBW#*J8J*=NCd;JV5r}gE@JOD|<+cEAS0&@rh%nr>b+~_QaBgTHc5(zZ)uiL83 zrmLkdM`7TT33=Y_yXKw-Od`|+Ouk3+pBK!eSWZ4=|26VM8GeENU54*^ zlC-B9bP&gsKJi2+j_yhFL-zr3;)#ZJ^F5Uw2l`QKZOux)B0(L|#Dn9TZx*V=T0c7w z8?%Z9@e}9O{9K-5t?0yczzjaho*neBJ>%ohXmU+sLzV(-_?Cv9ka1ZW%wR7Z{g`|?pdyv);#uLGI=^b)UVWXSkvG}LqU z=1Bmo0lG-$U_9b@7N6>)E5s1XYbHmS;T%$CucA~&gK(WEmwgLi)SiE87NT1(+EYF9 zkt1Px@%CYer9t#**fH!||m=*Rqy@Ji-c^2x4G zm8}d2@Bv;T)bo$=lfEN;XgQX7>64ap;db}p{t&|LPr1gLMR|%^W`kYWlB0JqlP3uV zBl5mSC3QV%9+-+6p6Po9(budYiX)j#tOZbv@?Ea5c$*C(Codq(9tF#tZAeN`bG{--l*Hn_)Yw^ovxMiQ(D{k zLg;d+_&z->!}PiPAnoHDAjUyPJe zSb%bfud! zzL~hw@sU@*lNm=OMk=1bkc(~xI!8rp2N-s(HCf!jNNp%asp@IQ~otJ^gY-Y9$^tL&CY;oD}o|iwSbW&@`}GBUwj*J`3V6#9|XW%$3m~k zdp6W!@5UVS8+wI7nDUFg4D{HEW1)!oJ*!b{blSiwb)cRJRq+Spq)<&CoD5|H6)C!^ znv^O%GY9&Di8#og_*5wi(z7S6*oC!bpWiP~j(SUf(h}!v3{}C<>rbl|Y@3 z!UKW;tu5Err_b$;i2`g)mINB?Sc1nUyz83%Rw<(zz}KI%Ty)eCp-8L5kNUcz9&sfN zX>Y@raLE|lxE|4%pC$)kC+%yN1uyUeiHE;_-Cv%$&oZZu3HKR` zgn?=6!X>b$Njdm{MW@Gd3uZ}m{-Lebf3dVPd8xhWsw5 z&%!U8_rZ~^v^;C8&_enKKNx3JK;b-;ZFtc1;z6O4ibr1{O6w})k=hfoO0$h=?A0$| zTh0oKYx)%vSgy6Jow|#oVV?MdZL*t3+b$-W8#8%T;ZwK$(2?=!u}0E7L=aJgc0OV+ z=qMp)yuWnL4PU3;%?MTSx7R_d$3a=?a=0|$z=+izMqKw1r^si7U{;JN#&;#hH1=OW z54U4)4hv-RSxO#uug3YMc*ftVxUGUrk73pvvE=@M2TI;8wx=b(cFNpe&3l_cZ3`vo zO#!v8!y0d38JvHln7{PcpFa(G|Gr_{Ap|CUFfhMhh;o1~$qnD24dfLfbs(mhQ~qnA z{9fe=CYETI66WPs17h0pp2+0$#=_yE`7@TjuR`PS=;1`+P20L(vhVOASb{?#kB~bY zWzn6@-5ux%Xap6UU@Gt>FR#0Z&Un5g8_z+IvOpFOT-q8$MZPCXNx6v|sVf$w6SL0~ z=8q~DSG~3;eBjOWA*a9!$Y&X#Z5=bFc0XlFUKFz+;gl-#PQm$6;SO@s^0Fer4GEP| z^d)DiB0^CAX@91eaE*aJXaIAeNQPuQmxhcvHQQIJYNenmG{baHqoBB+lvUbed>hlC z@{hyEe2OHo2`N}ki>()E&qZ|2RZK;S&WI`~CvHl@XL+^U?KeBaMQ#ZNSbC+w z78}nV#hJwAJovkny6I<}G!?&!=Q7OT+a9q)8frpu^J%uQW%8UCk_<6t)Jbj2wNw1J zK%4?=Y3Ln7%@TMw^Nip)odZmcrDN+(y$j^0<%{6)i!i`V2z1oY8_{hK|IS@6`*H1p8TpHz2V*%1(WZ zT`0YIL^>{3Hh4-dAv1$uq&Ci%e%pA?6li&vMnM)wK00Z0h;C()4T26;y@ggCl_V)t z^Tl2GnSfi}DSVjm$l`VG)3b(l`CK#_73IV}Uv2m61!Z&O4%qk`5{=r*Z?$(2Ds)9+ zdVU9u*#3ULtHazGC~R*_GUWT~wad)m8uxYN^vq4L!LHJg$OMG_l~{cEY^hGja#^BY zsJ&X)TbjcjFT>M8eT|U)+0+;GEiKtU({?824N-JwI(`nq7C=T60^DpI9UXRe;qUQU_Iw6f@BGOqI+uW zfU1A8h*25Vesd#Lr^jaL(3FKC99^zPP2(RfA2Z!ddy|;8p)Y`@-5ZppiBu`7kUk8d zFw&A#ogtxcK+G`Fp^ria?`gFnxI#z{mx^t*?5e{J+aC$FVuf;f#wxN*)fej z+g#HyV#dgwQ^B67oadqdM9Edm9R z`=p$O3{~#6(ngK=1b;32&zt$Oqvjg*n$X|q=JHD;<7v*e_oaVfv(o(}yJO*efz=eT zt1S?#y0YBTEf+C;l*j7`ikgBP?uo}K zWQ#P|v{={ht5u77G07cTqDSN$9-yTXv#Q_}i}xW*0*m*e*O#RrFtHBj+CzG3jFRzJ zkpRc?P2!$(Me~P(4(`mHTmW#wgQlEvwt(#SRzISiKkneiPJD*^pAw#^QzSX|$Vd#G z>==BZNt_abQd=1tGHIjkZsSUQ6qJ$6lyucfAE{#^5&0yEZGUELVMj7bF4rNDR|w9x z@r`ZSqes$|38F>EDKnH>3Q0K8->{R<$PX2N; zcs-H=MG1uj#^;(y>%<|7$MG?iF~+@|l3-A1l! zSL~>e=g1X{v|{?|D8(z`-s>`IZUqa(-Zh}goBx~(+DeWVvX^n2c7z`V?L?77%m~f- zi%nEhm+2fv($47{`8mu=sJqT3-TzZFX0I6_@pO5*-H+558F=Q(h)^ z^IKoQ`%G%dsklZ~jW+A@5%ZRdL_9g4iRCtJa-5}|-aU;p(=Uo8wP#1}k#1v6EYCf& zo9}ap(bDB8(Yw{bMt@KmI(`gMd63fjpQ9U1zqJmR`LjXwOf{YND53c}@AAsC@fN8Y z@&J!!7m-dX32>FY#Ixw$`O@MFOqbJbn)0h^6y>Xi42BZVlo}W!a?$?@ybDA0qnD?W zcEKy; z3kWO!DZJMf+jrl>mC!mVLx$|gS*-y;y})W?GJ$pYyFM99TbZF+awQK+HkPbDFh#}! zoi~6wrL5cBvG6QTvrhnQV=Swso{X+XOZJ?RpnRiXAoWMfs2fUwP;5}Ulr(730Y~f{abNYd9;Vqt|~lD`C4@$^u|#D%ZJ)NLIHk5L z(Zzn8yl9aJx7bwWm??8ZV@5k{&{7^+{GUx1rdFywh(egck}E^xGA$dqkhu&#KM2 zA7l*2d4f*YBpT@^o1APG>L+=1@fTjW?4LM{c?3AIQ3CPhdw3?F9bDw1Ft2a#gchLK zsLXqyiyEsMv@tXxUV@v}Uv(<{vjR1DiXkDiZBE9S3-&_)p2`EA7&k->O9Mo*?Ljzu$V~qIirmc!&uDZ++XX&7uAe`3Lr*EYEGPK4hlbK%F^O< zYd{e`l4?88^5NetjdG4@_Xn|}=BfK=D z3+rc#S#uRH(D3Ulhccq?mO-dyd92KIHqK}3qhTE=n69UinMT8aK}wzJ3-U?L0t8`@ z4g3>O*BqHb^wIU;4cI;N-^Wh~lK*>PgO3{mM!HP{chcvND5Ltd#&Hm$FY z2y$s~gItJ56$TZ8B2e8VQxN)CKpJd^N-{OmF2@ky@ zcKrlvbij^glKPgT2XKHw3eMb<4+m5%&J&r-6Q9Ki8Xk#w!YdJyY=odI(5EE`MH)y) zU_k+K^DM`aiX}%xO8<}sN50)4SN6(==GhhkD>LB0TsK%{0I`ktKopD+>LeOjV;skU zcq?=U)V9I+Q@X;sWSoi)pNh$tr^p~JBgDiau?bBg1Xo-X0ljz7`3Q2cL{Q`b(33dX zA=_0f;5E|si3&1Vw2{;ard+QNs<+ij*IQZg-((H`# zy}g#t!Luew=KV+VUgTY1!v+Q=0&AuhYH&&CI=N`mQm!uDu?D3O0^OM&$?4!j#s$Fk zhEa!c(w^r0C%7FB^hr3Rye3G{g}qq94a)SkP7pRMyJ@$*#5o%+Y);V~LO|~l0>&4`$NHEaQKZjlFH;j#P!=b0G_VuCgAC9$I?1ko z_=h4G=B`4v1NP!eV-r^x3HI=>Xj#;?@~9PI_6+o6273pS%5&F=h9m9r4l_t~x&eKd ztql>3{gtv95b-R*?xFNO%8*%+*Bw&PKS{vM=CSg)@^Dj))uC9tX}wpx+`*ro|I%0& zqEaxDCF$`+3gwd@qE#*Mej%jbuy9ING4jm+9IbjiJKS~60!RSt5u1<`s6}q>Px><^lesFt4+g+%U%EXedX8T)&H=k&#m>Y`XNPsFPu)|wh zd>l`rMo(FM5Cb3lYnzLMYwD=`%*gYJ3At^$%kkOy=X1c~L&nd6vgtPlEZqR3oD^Q* z&OU;tfS^V*y(<(xHdg`Y!>P2-#cfKYkx#C=kkaUSD`q?58E%PQ0RFjP;u>{ej4OH6 z7zFu`v0DSA+o@038!pniT`j%KOb({=Qpz_>Y-ZfyHZXxu(&I^1{*x;4lW;A)iNV5c zy9ClgqEv6SV61b1bfmhhqFg{+O`+s~P>R&=Gq9Lk-uSe6V|ryFi5T}7S5oD?6iDFw z;6*Z!L=6w=NDUTGM01v6T^BO>G0mjsGG&6=O!#SI0|bH5moS628sp<>+rsbNfC&le zR80;o@s~Vl@j47Od5T>wWHipGVusH>?p9M+LU2exf{@7(iO!s&@eD0=*;OdnkeAvA zz-t^q2)H$-$wWcmz$8@>CYCUfSXHcKb=+;5?4=KXC=zuVhIY3s%)wBDE3h@LfV~tJ zRXE7I<|9NoqqouB-NqZ*EKWz02uc?FCg^+>;E!L4mgn6D&E(&*XGDOErc{=`qqP4j zEvYYKvEJs?ao;2T3OgBV3rSxEj@v*li4IZ?^U2~~dCH;Hj8?(DQ~HE#Kr*5Qx?(2S2N850iFkzhxc~ka_}7QW<_H^>Ia<+7w`dt z(T12zWpKBs3%)W>H*dky2r*(WP62Zja3o%A*l3b`W!@V7VJ4mffDB6!;0(Om%r6|8 zUoa890HR1JEIJ4XiFk9V5t}8)~L_wpP literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.svg b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.eot b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.svg b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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
+ +

Home

+ + + + + + + + +

cognicity-server 3.0.1

+ + + + + + + + + + + + + + + +
+

cognicity-server

API Server for CogniCity

+

Build Status

+

Coverage Status

+

DOI for current stable release v3.0.0

+

DOI

+

Summary

This is the NodeJS server which runs the CogniCity Data API used by Urban Risk Map instances, such as PetaBencana.id site.

+

Run

ES6 Support is provided by Babel.

+
    +
  1. Install requirements from the provided package.json by doing npm install.

    +
  2. +
  3. Copy the sample.env file to a local .env and fill-in the required parameters. This local file will be ignored by Git and so should be secret safe. Further details on configuration are described below.

    +
  4. +
  5. To run a local development instance of the server do npm run dev

    +
  6. +
+

Configuration

Server configuration parameters are stored in a configuration file which is parsed by index.js on startup. Local configuration parameters are imported from the .env into src/config.js. See config.js for full details example configuration. Any variable not defined in .env will pickup the default value below (also see config.js)—note that local environment variables will override both .env and config.js. The following environment variables are currently supported by the configurtion:

+
    +
  • APP_NAME: Name of the application (default: cognicity-server)
  • +
  • API_FEEDS_QLUE_CITIES: Names of cities used by the Qlue data feed
  • +
  • API_FEEDS_QLUE_DISASTER_TYPES: Names of disaster types used by the Qlue data feed
  • +
  • API_FEEDS_DETIK_DISASTER_TYPES: Names of disaster types used by the Detik data feed
  • +
  • API_REPORTS_TIME_WINDOW: Time window for report data queries (default 1 hour)
  • +
  • API_REPORTS_TIME_WINDOW_MAX: Maximum limit for time window (default 1 week)
  • +
  • API_REPORTS_LIMIT: Total maximum number of reports to return in a single request
  • +
  • API_FLOODGAUGE_REPORTS_TIME_WINDOW: Time window for flood data (normally 12 hours)
  • +
  • API_FLOODGAUGE_REPORTS_TIME_WINDOW: Total maximum number of flood gauge records to return in a single request
  • +
  • AUTH0_AUDIENCE: Data API to be authenticated
  • +
  • AUTH0_CLIENT_ID: Auth0 client ID (NOTE: this is mandatory and no default value)
  • +
  • AUTH0_ISSUER: Web address of Auth0 instance
  • +
  • AWS_REGION: Region for AWS Infrastructure
  • +
  • AWS_S3_ACCESS_KEY_ID: Access key ID for AWS S3 bucket
  • +
  • AWS_S3_SECRET_ACCESS_KEY: Access key secret for AWS S3 bucket
  • +
  • AWS_S3_SIGNATURE_VERSION: Version of AWS S3 signature to use
  • +
  • AUTH0_SECRET: Auth0 secret (NOTE: this is mandatory and no default value)
  • +
  • BODY_LIMIT: Maximum body size POST/PUT/PATCH (default: 100kb)
  • +
  • CACHE: Should caching be enabled? (default: false)
  • +
  • CACHE_DURATION_CARDS: How long should cards be cached for? (default: '1 minute')
  • +
  • CACHE_DURATION_FLOODS: How long should floods be cached for? (default: '1 hour')
  • +
  • CACHE_DURATION_FLOODS_STATES: How long should flood states be cached for? (default: '1 hour')
  • +
  • CACHE_DURATION_INFRASTRUCTURE: How long should infrastructure be cached for? (default: '1 hour')
  • +
  • COMPRESS: Should the server gzip compress results? Only works if CACHE is disabled. (default: false)
  • +
  • CORS: Should Cross Object Resource Sharing (CORS) be enabled (default: false)
  • +
  • CORS_HEADERS: CORS headers to use (default: [Link])
  • +
  • DISASTER_TYPES: Disaster type keywords for report classification (default: flood,prep)
  • +
  • FORMAT_DEFAULT: Which format to return results in by default (default: json)
  • +
  • FORMATS: Formats supported by the system (as comma separated list) (default: json,xml)
  • +
  • GEO_FORMAT_DEFAULT: Which format to return geographic results in by default (default: topojson)
  • +
  • GEO_FORMATS: Geographic formats supported by the system (as comma separated list) (default: topojson,geojson,cap)
  • +
  • GEO_PRECISION: Precision to use when rounding geographic coordinates (default: 10)
  • +
  • IMAGE_BUCKET: AWS S3 bucket for image uploads (default: testing-riskmap-image-uploads)
  • +
  • IMAGES_HOST: Endpoint for image hosting (default: images.petabencana.id),
  • +
  • INFRASTRUCTURE_TYPES: Infrastructure types supported (as comma separated list) (default: floodgates,pumps,waterways)
  • +
  • LANGUAGES: Supported languages
  • +
  • LOG_CONSOLE: In development mode we log to the console by default, in other environments this must be enabled if required by setting this parameter to true (default: false)
  • +
  • LOG_DIR: Which directory should logs be written to. If blank, not supplied or the directory is not writable by the application this will default to the current directory
  • +
  • LOG_JSON: Should json format be used for logging (default: false)
  • +
  • LOG_LEVEL: What level to log at. Levels are: silly, debug, verbose, info, warn, error. debug level is recommended for development. (default: error)
  • +
  • LOG_MAX_FILE_SIZE: Maximum size of log file in bytes before rotating (default: 1024 * 1024 * 100 i.e. 100mb)
  • +
  • LOG_MAX_FILES: Maximum number of log files before rotation (default: 10)
  • +
  • NODE_ENV: Which environment are we in. Environments are: development, test, staging, production (default: development)
  • +
  • PGHOST: Postgres DB hostname (default: 127.0.0.1)
  • +
  • PGDATABASE: Postgres DB database name (default: cognicity)
  • +
  • PGPASSWORD: Postgres DB password (default: p@ssw0rd)
  • +
  • PGPORT: Postgres DB port (default: 5432)
  • +
  • PGSSL: SSL enabled on Postgres DB connection? (default: false)
  • +
  • PGTIMEOUT: Max duration on DB calls before timeout (in milliseconds) (default: 5000 i.e. 5 seconds)
  • +
  • PGUSER: Postgres DB username (default: postgres)
  • +
  • PORT: Which port should the application run on (default: 8001)
  • +
  • REGION_CODES: Which region codes are supported (as comma separated list) (default: jbd,bdg,sby)
  • +
  • REPORT_TYPES: Classifiers for report types (default: drain,desilting,canalrepair,treeclearing,flood)
  • +
  • RESPONSE_TIME: Should the server return an X-Response-Time header detailing the time taken to process the request. This is useful for both development to identify latency impact on testing and production for performance / health monitoring (default: false)
  • +
  • SECURE_AUTH0: Whether Auth0 JWT token security should be applied to secure routes (default: false)
  • +
  • TABLE_FLOODGAUGE_REPORTS: Postgres table name for flood-gauge reports
  • +
  • TABLE_FEEDS_QLUE: Postgres table name for Qlue feed
  • +
  • TABLE_FEEDS_DETIK: Postgres table name for Detik feed
  • +
  • TABLE_GRASP_CARDS: Postgres table name for Grasp Cards
  • +
  • TABLE_GRASP_LOG: Postgres table name for Grasp activity
  • +
  • TABLE_GRASP_REPORTS: Postgres table name for Grasp reports
  • +
  • TABLE_INSTANCE_REGIONS: Postgres table for operating regions
  • +
  • TABLE_LOCAL_AREAS: Postgres table for local areas data for each operating region
  • +
  • TABLE_REM_STATUS: Postgres table for current flood states from REM
  • +
  • TABLE_REM_STATUS_LOG: Postgres table for REM log
  • +
  • TABLE_REPORTS: Postgres table for reports
  • +
+

A few points to note on config:

+
    +
  • AWS Beanstalk: If you're deploying to AWS Elastic Beanstalk you may need to configure environment variables for each of the above under Configuration -> Software configuration.
  • +
+

Building

Run npm run -s build to build.

+

Testing

Testing is run by Travis. 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. 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.

+

To run tests locally a new database "cognicity_server_testing" is required on localhost.

+

Code follows the Google JavaScript style.ESLint is run with tests to enfoce style.

+

Issue Tracking

Issues are tracked using GitHub

+

Release

The release procedure is as follows:

+
    +
  • Update the CHANGELOG.md file with the newly released version, date, and a high-level overview of changes. Commit the change.
  • +
  • Create a tag in git from the current head of master. The tag version should be the same as the version specified in the package.json file - this is the release version.
  • +
  • Update the version in the package.json file and commit the change.
  • +
  • Further development is now on the updated version number until the release process begins again.
  • +
+

API Notes

Full API documentation at https://docs.petabencana.id. This documentation is stored in the petabencana-docs repository.

+
    +
  • The dbgeo library expects timestamps from database to be in UTC (i.e. not a local timezone)
  • +
+

License

See LICENSE.md

+
+ + + + + + + + + +
+ +
+ +

src/index.js

+ + +
+ +
+
+ + +
Run CogniCity Data Server
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
Author:
+
+
    +
  • Urban Risk Lab, 2017
  • +
+
+ + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/index.js.html b/jsdoc/cognicity-server/3.0.1/index.js.html new file mode 100644 index 0000000..4a1a37f --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/index.js.html @@ -0,0 +1,136 @@ + + + + + JSDoc: Source: index.js + + + + + + + + + + +
+ +

Source: index.js

+ + + + + + +
+
+
/**
+ * @file Run CogniCity Data Server
+ * @author Urban Risk Lab, 2017
+ **/
+// Import express, fs and http
+import fs from 'fs';
+import path from 'path';
+
+// Import config
+import config from './config';
+
+// Import DB initializer
+import initializeDb from './db';
+
+// Import the routes
+import routes from './api';
+
+// Import server
+import {init} from './server.js';
+
+// Import logging libraries
+import logger from 'winston'; // Application logging
+
+// Set the default logging level
+logger.level = config.LOG_LEVEL;
+
+// Check that log file directory can be written to
+try {
+	if (config.LOG_DIR !== '') {
+		fs.accessSync(config.LOG_DIR, fs.W_OK);
+	}
+	logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR :
+							'current working directory' }`);
+} catch (e) {
+	// If we cannot write to the desired directory then log tocurrent directory
+	logger.info(`Cannot log to '${config.LOG_DIR}',
+							logging to current working directory instead`);
+	config.LOG_DIR = '';
+}
+
+// Configure the logger
+logger.add(logger.transports.File, {
+	filename: path.join(config.LOG_DIR, `${config.APP_NAME}.log`),
+	json: config.LOG_JSON, // Log in json or plain text
+	maxsize: config.LOG_MAX_FILE_SIZE, // Max size of each file
+	maxFiles: config.LOG_MAX_FILES, // Max number of files
+	level: config.LOG_LEVEL, // Level of log messages
+});
+
+// If we are not in development and console logging not requested then remove it
+if (config.NODE_ENV !== 'development' && !config.LOG_CONSOLE) {
+	logger.remove(logger.transports.Console);
+}
+
+// If we exit immediately winston does not get chance to write last log message
+const exitWithStatus = (status) => {
+	logger.info(`Exiting with status ${status}`);
+	setTimeout(() => process.exit(status), 500);
+};
+
+// Catch kill and interrupt signals and log a clean exit status
+process
+	.on('SIGTERM', () => {
+		logger.info('SIGTERM: Application shutting down');
+		exitWithStatus(0);
+	})
+	.on('SIGINT', () => {
+		logger.info('SIGINT: Application shutting down');
+		exitWithStatus(0);
+	});
+
+// Try and start the server
+init(config, initializeDb, routes, logger).then((app) => {
+	// All good to go, start listening for requests
+	app.server.listen(config.PORT);
+	logger.info(`Application started,`
+		+ `listening on port ${app.server.address().port}`);
+}).catch((err) => {
+	// Error has occurred, log and shutdown
+	logger.error('Error starting server: ' + err.message + ', ' + err.stack);
+	logger.error('Fatal error: Application shutting down');
+	exitWithStatus(1);
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/cognicity-server/3.0.1/scripts/linenumber.js b/jsdoc/cognicity-server/3.0.1/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/jsdoc/cognicity-server/3.0.1/scripts/prettify/Apache-License-2.0.txt b/jsdoc/cognicity-server/3.0.1/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/jsdoc/cognicity-server/3.0.1/scripts/prettify/lang-css.js b/jsdoc/cognicity-server/3.0.1/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/jsdoc/cognicity-server/3.0.1/scripts/prettify/prettify.js b/jsdoc/cognicity-server/3.0.1/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + JSDoc: Module: server +Core server module + + + + + + + + + + +
+ +

Module: server +Core server module

+ + + + + + +
+ +
+ +
+ +
+
+ + +
CogniCity Data Server Module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/server.js.html b/jsdoc/cognicity-server/3.0.1/server.js.html new file mode 100644 index 0000000..603ab98 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/server.js.html @@ -0,0 +1,141 @@ + + + + + JSDoc: Source: server.js + + + + + + + + + + +
+ +

Source: server.js

+ + + + + + +
+
+
/**
+ * CogniCity Data Server Module
+ * @module server
+ * Core server module
+ **/
+// Import
+import Promise from 'bluebird';
+
+// Express middleware and http
+import express from 'express';
+import http from 'http';
+
+// Import express middlewares
+import bodyParser from 'body-parser';
+import cors from 'cors';
+import compression from 'compression';
+import responseTime from 'response-time';
+import morgan from 'morgan'; // Express logging
+
+/**
+ * @alias module:server
+ * @param {Object} config - configuration
+ * @param {Object} initializeDb - database initialization
+ * @param {Object} routes - routes
+ * @param {Object} logger - logger
+ * @return {Object} - Express server application
+ **/
+// Function to initialize the api server
+const init = (config, initializeDb, routes, logger) =>
+	new Promise((resolve, reject) => {
+	// Create the server
+	let app = express();
+	app.server = http.createServer(app);
+
+	// Winston stream function for express so we can capture logs
+	const winstonStream = {
+		write: function(message) {
+			logger.info(message.slice(0, -1));
+		},
+	};
+
+	// Setup express logger
+	app.use(morgan('combined', {stream: winstonStream}));
+
+	// Compress responses if required but only if caching is disabled
+	if (config.COMPRESS && !config.CACHE) {
+		app.use(compression());
+	}
+
+	// Provide CORS support (not required if behind API gateway)
+	if (config.CORS) {
+		app.use(cors({exposedHeaders: config.CORS_HEADERS}));
+	}
+
+	// Provide response time header in response
+	if (config.RESPONSE_TIME) {
+		app.use(responseTime());
+	}
+
+	// Parse body messages into json
+	app.use(bodyParser.json({limit: config.BODY_LIMIT}));
+
+	// Try and connect to the db
+	initializeDb(config, logger)
+		.then((db) => {
+			// Log debug message
+			logger.debug('Successfully connected to DB');
+
+			// Mount the routes
+			app.use('/', routes({config, db, logger}));
+
+			// App is ready to go, resolve the promise
+			resolve(app);
+		})
+		.catch((err) => {
+			/* istanbul ignore next */
+			logger.error('DB Connection error: ' + err);
+			/* istanbul ignore next */
+			logger.error('Fatal error: Application shutting down');
+			/* istanbul ignore next */
+			// We cannot continue without a DB, reject
+			reject(err);
+		});
+});
+
+// Export the init function for use externally
+
+module.exports = {init};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:29:31 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/cognicity-server/3.0.1/styles/jsdoc-default.css b/jsdoc/cognicity-server/3.0.1/styles/jsdoc-default.css new file mode 100644 index 0000000..ede1919 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/styles/jsdoc-default.css @@ -0,0 +1,354 @@ +@font-face { + font-family: 'Open Sans'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Regular-webfont.eot'); + src: + local('Open Sans'), + local('OpenSans'), + url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), + url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); +} + +@font-face { + font-family: 'Open Sans Light'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Light-webfont.eot'); + src: + local('Open Sans Light'), + local('OpenSans Light'), + url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Light-webfont.woff') format('woff'), + url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); +} + +html +{ + overflow: auto; + background-color: #fff; + font-size: 14px; +} + +body +{ + font-family: 'Open Sans', sans-serif; + line-height: 1.5; + color: #4d4e53; + background-color: white; +} + +a, a:visited, a:active { + color: #0095dd; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +header +{ + display: block; + padding: 0px 4px; +} + +tt, code, kbd, samp { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.class-description { + font-size: 130%; + line-height: 140%; + margin-bottom: 1em; + margin-top: 1em; +} + +.class-description:empty { + margin: 0; +} + +#main { + float: left; + width: 70%; +} + +article dl { + margin-bottom: 40px; +} + +section +{ + display: block; + background-color: #fff; + padding: 12px 24px; + border-bottom: 1px solid #ccc; + margin-right: 30px; +} + +.variation { + display: none; +} + +.signature-attributes { + font-size: 60%; + color: #aaa; + font-style: italic; + font-weight: lighter; +} + +nav +{ + display: block; + float: right; + margin-top: 28px; + width: 30%; + box-sizing: border-box; + border-left: 1px solid #ccc; + padding-left: 16px; +} + +nav ul { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; + font-size: 100%; + line-height: 17px; + padding: 0; + margin: 0; + list-style-type: none; +} + +nav ul a, nav ul a:visited, nav ul a:active { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + line-height: 18px; + color: #4D4E53; +} + +nav h3 { + margin-top: 12px; +} + +nav li { + margin-top: 6px; +} + +footer { + display: block; + padding: 6px; + margin-top: 12px; + font-style: italic; + font-size: 90%; +} + +h1, h2, h3, h4 { + font-weight: 200; + margin: 0; +} + +h1 +{ + font-family: 'Open Sans Light', sans-serif; + font-size: 48px; + letter-spacing: -2px; + margin: 12px 24px 20px; +} + +h2, h3.subsection-title +{ + font-size: 30px; + font-weight: 700; + letter-spacing: -1px; + margin-bottom: 12px; +} + +h3 +{ + font-size: 24px; + letter-spacing: -0.5px; + margin-bottom: 12px; +} + +h4 +{ + font-size: 18px; + letter-spacing: -0.33px; + margin-bottom: 12px; + color: #4d4e53; +} + +h5, .container-overview .subsection-title +{ + font-size: 120%; + font-weight: bold; + letter-spacing: -0.01em; + margin: 8px 0 3px 0; +} + +h6 +{ + font-size: 100%; + letter-spacing: -0.01em; + margin: 6px 0 3px 0; + font-style: italic; +} + +table +{ + border-spacing: 0; + border: 0; + border-collapse: collapse; +} + +td, th +{ + border: 1px solid #ddd; + margin: 0px; + text-align: left; + vertical-align: top; + padding: 4px 6px; + display: table-cell; +} + +thead tr +{ + background-color: #ddd; + font-weight: bold; +} + +th { border-right: 1px solid #aaa; } +tr > th:last-child { border-right: 1px solid #ddd; } + +.ancestors { color: #999; } +.ancestors a +{ + color: #999 !important; + text-decoration: none; +} + +.clear +{ + clear: both; +} + +.important +{ + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px; +} + +.type-signature { + color: #aaa; +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.details { margin-top: 14px; border-left: 2px solid #DDD; } +.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } +.details dd { margin-left: 70px; } +.details ul { margin: 0; } +.details ul { list-style-type: none; } +.details li { margin-left: 30px; padding-top: 6px; } +.details pre.prettyprint { margin: 0 } +.details .object-value { padding-top: 0; } + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption +{ + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint +{ + border: 1px solid #ddd; + width: 80%; + overflow: auto; +} + +.prettyprint.source { + width: inherit; +} + +.prettyprint code +{ + font-size: 100%; + line-height: 18px; + display: block; + padding: 4px 12px; + margin: 0; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code span.line +{ + display: inline-block; +} + +.prettyprint.linenums +{ + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol +{ + padding-left: 0; +} + +.prettyprint.linenums li +{ + border-left: 3px #ddd solid; +} + +.prettyprint.linenums li.selected, +.prettyprint.linenums li.selected * +{ + background-color: lightyellow; +} + +.prettyprint.linenums li * +{ + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td.description > p:first-child, +.props td.description > p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, +.props td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/jsdoc/cognicity-server/3.0.1/styles/prettify-jsdoc.css b/jsdoc/cognicity-server/3.0.1/styles/prettify-jsdoc.css new file mode 100644 index 0000000..5a2526e --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/jsdoc/cognicity-server/3.0.1/styles/prettify-tomorrow.css b/jsdoc/cognicity-server/3.0.1/styles/prettify-tomorrow.css new file mode 100644 index 0000000..b6f92a7 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/jsdoc/config.js.html b/jsdoc/config.js.html new file mode 100644 index 0000000..7c08919 --- /dev/null +++ b/jsdoc/config.js.html @@ -0,0 +1,140 @@ + + + + + JSDoc: Source: config.js + + + + + + + + + + +
+ +

Source: config.js

+ + + + + + +
+
+
/**
+ * CogniCity Server configuration
+ * @file config
+ * @return {Object} Server configuration
+*/
+/* eslint-disable max-len */
+require('dotenv').config({silent: true});
+
+export default {
+  APP_NAME: process.env.APP_NAME || 'cognicity-server',
+  API_FEEDS_QLUE_CITIES: (process.env.API_FEEDS_QLUE_CITIES || 'jabodetabek,bandung,surabaya').split(','),
+  API_FEEDS_QLUE_DISASTER_TYPES: (process.env.API_FEEDS_QLUE_DISASTER_TYPES || 'flood').split(','),
+  API_FEEDS_DETIK_DISASTER_TYPES: (process.env.API_FEEDS_DEIK_DISASTER_TYPES || 'flood').split(','),
+  API_REPORTS_TIME_WINDOW: process.env.API_REPORTS_TIME_WINDOW || 3600,
+  API_REPORTS_TIME_WINDOW_MAX: process.env.API_REPORTS_TIME_WINDOW_MAX || 604800, // 1w
+  API_REPORTS_LIMIT: process.env.API_REPORTS_LIMIT,
+  API_FLOODGAUGE_REPORTS_TIME_WINDOW: process.env.API_FLOODGAUGE_REPORTS_TIME_WINDOW || 43200,
+  API_FLOODGAUGE_REPORTS_LIMIT: process.env.API_FLOODGAUGE_REPORTS_LIMIT,
+  AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE || 'https://data.petabencana.id',
+  AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID || 'auth0_client_id',
+  AUTH0_ISSUER: process.env.AUTH0_ISSUER || 'https://petabencana.au.auth0.com',
+  AUTH0_SECRET: process.env.AUTH0_SECRET || 'secret',
+  AWS_REGION: process.env.AWS_REGION || 'ap-south-1',
+  AWS_S3_ACCESS_KEY_ID: process.env.AWS_S3_ACCESS_KEY_ID || '',
+  AWS_S3_SECRET_ACCESS_KEY: process.env.AWS_S3_SECRET_ACCESS_KEY || '',
+  AWS_S3_SIGNATURE_VERSION: process.env.AWS_SIGNATURE_VERSION || 'v4',
+  BODY_LIMIT: process.env.BODY_LIMIT || '100kb',
+  CACHE: process.env.CACHE === 'true' || false,
+  CACHE_DURATION_CARDS: process.env.CACHE_DURATION_CARDS || '1 minute',
+  CACHE_DURATION_FLOODS: process.env.CACHE_DURATION_FLOODS || '1 hour',
+  CACHE_DURATION_FLOODS_STATES: process.env.CACHE_DURATION_FLOODS_STATES || '1 hour',
+  CACHE_DURATION_INFRASTRUCTURE: process.env.CACHE_DURATION_INFRASTRUCTURE || '1 hour',
+  COMPRESS: process.env.COMPRESS === 'true' || false,
+  CORS: process.env.CORS === 'true' || false,
+  CORS_HEADERS: process.env.CORS_HEADERS || ['Link'],
+  DISASTER_TYPES: (process.env.DISASTER_TYPES || 'flood,prep').split(','),
+  FORMAT_DEFAULT: process.env.FORMAT_DEFAULT || 'json',
+  FORMATS: (process.env.FORMATS || 'json').split(','),
+  GEO_FORMAT_DEFAULT: process.env.GEO_FORMAT_DEFAULT || 'topojson',
+  GEO_FORMATS: (process.env.GEO_FORMATS || 'geojson,topojson').split(','),
+  GEO_PRECISION: process.env.GEO_PRECISION || 10,
+  IMAGE_BUCKET: process.env.IMAGE_BUCKET || 'testing-riskmap-image-uploads',
+  IMAGES_HOST: process.env.IMAGES_HOST || 'images.petabencana.in',
+  INFRASTRUCTURE_TYPES: (process.env.INFRASTRUCTURE_TYPES || 'floodgates,pumps,waterways').split(','),
+  LANGUAGES: (process.env.LANGUAGES || 'en,id').split(','),
+  LOG_CONSOLE: process.env.LOG_CONSOLE === 'true' || false,
+  LOG_DIR: process.env.LOG_DIR || '',
+  LOG_JSON: process.env.LOG_JSON === 'true' || false,
+  LOG_LEVEL: process.env.LOG_LEVEL || 'error',
+  LOG_MAX_FILE_SIZE: process.env.LOG_MAX_FILE_SIZE || 1024 * 1024 * 100,
+  LOG_MAX_FILES: process.env.LOG_MAX_FILES || 10,
+  NODE_ENV: process.env.NODE_ENV || 'development',
+  PGHOST: process.env.PGHOST || '127.0.0.1',
+  PGDATABASE: process.env.PGDATABASE || 'cognicity',
+  PGPASSWORD: process.env.PGPASSWORD || 'p@ssw0rd',
+  PGPORT: process.env.PGPORT || 5432,
+  PGSSL: process.env.PGSSL === 'true' || false,
+  PGTIMEOUT: process.env.PGTIMEOUT || 10000,
+  PGUSER: process.env.PGUSER || 'postgres',
+  PORT: process.env.PORT || 8001,
+  REGION_CODES: (process.env.REGION_CODES || 'jbd,bdg,sby').split(','),
+  REPORT_TYPES: (process.env.REPORT_TYPES || 'drain,desilting,canalrepair,treeclearing,flood').split(','),
+  RESPONSE_TIME: process.env.RESPONSE_TIME === 'true' || false,
+  SECURE_AUTH0: process.env.SECURE_AUTH0 === 'true' || false,
+  TABLE_FLOODGAUGE_REPORTS: process.env.TABLE_FLOODGAUGE_REPORTS || 'floodgauge.reports',
+  TABLE_FEEDS_QLUE: process.env.TABLE_FEEDS_QLUE || 'qlue.reports',
+  TABLE_FEEDS_DETIK: process.env.TABLE_FEEDS_DETIK || 'detik.reports',
+  TABLE_GRASP_CARDS: process.env.TABLE_GRASP_CARDS || 'grasp.cards',
+  TABLE_GRASP_LOG: process.env.TABLE_GRASP_LOG || 'grasp.log',
+  TABLE_GRASP_REPORTS: process.env.TABLE_GRASP_REPORTS || 'grasp.reports',
+  TABLE_INSTANCE_REGIONS: process.env.TABLE_INSTANCE_REGIONS || 'cognicity.instance_regions',
+  TABLE_LOCAL_AREAS: process.env.TABLE_LOCAL_AREAS || 'cognicity.local_areas',
+  TABLE_REM_STATUS: process.env.TABLE_REM_STATUS || 'cognicity.rem_status',
+  TABLE_REM_STATUS_LOG: process.env.TABLE_REM_STATUS_LOG || 'cognicity.rem_status_log',
+  TABLE_REPORTS: process.env.TABLE_REPORTS || 'cognicity.all_reports',
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/db%0ADatabase%20initializermodule_.html b/jsdoc/db%0ADatabase%20initializermodule_.html new file mode 100644 index 0000000..94e068d --- /dev/null +++ b/jsdoc/db%0ADatabase%20initializermodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: db +Database initializer + + + + + + + + + + +
+ +

Module: db +Database initializer

+ + + + + + +
+ +
+ +
+ +
+
+ + +
CogniCity Server Database
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/db.js.html b/jsdoc/db.js.html new file mode 100644 index 0000000..c6915e9 --- /dev/null +++ b/jsdoc/db.js.html @@ -0,0 +1,100 @@ + + + + + JSDoc: Source: db.js + + + + + + + + + + +
+ +

Source: db.js

+ + + + + + +
+
+
/**
+ * CogniCity Server Database
+ * @module db
+ * Database initializer
+ **/
+import Promise from 'bluebird';
+
+// Import DB library
+const pgp = require('pg-promise')({
+  // Initialization Options
+  promiseLib: Promise, // Use bluebird for enhanced Promises
+});
+
+/**
+ * Database interaction for Cards objects
+ * @alias module:db
+ * @param {Object} config - configuration
+ * @param {Object} logger - logger
+ * @return {Object} db - PG Promise database
+ **/
+export default (config, logger) => new Promise((resolve, reject) => {
+	// Build the connection string
+	const cn = `postgres://${config.PGUSER}:${config.PGPASSWORD}@${config.PGHOST}:${config.PGPORT}/${config.PGDATABASE}?ssl=${config.PGSSL}`;
+  logger.debug(cn);
+
+	// Setup the connection
+	let db = pgp(cn);
+
+	// Make sure we can connect, if so resolve, if not reject
+	db.proc('version').timeout(config.PGTIMEOUT)
+		.then(() => resolve(db))
+		.catch((err) => {
+			logger.error(err);
+			reject(err);
+		});
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/fonts/OpenSans-Bold-webfont.eot b/jsdoc/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 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 literal 0 HcmV?d00001 diff --git a/jsdoc/fonts/OpenSans-Bold-webfont.svg b/jsdoc/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/jsdoc/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-Bold-webfont.woff b/jsdoc/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 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 ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-BoldItalic-webfont.woff b/jsdoc/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 zcmZsC18^o?(C!;28{4*R+s4MWZQHh;Y;4=c#x^##ar4z*x9Z-izo(w+)6aCD(=$_Z zX6j6jo4lA900{6SnvekG|8#os|JeVv|9=q^Q;`J#fXaVZod00t3i={0A}aR74gJ`7 zKOg|Y0f34t$SePFhX4R*5dZ*{OY4X(B(AI~1OR}C|M&#_pgi9&JXc8RP9o zCqzMe3Yr->{lvnt{P_Im`yUX@tUXMBI355%Xb=E!j7Ku=7Be?7Fa`h=e|7`@^JN2q zNM$nrA%D34Y{DOqz)gX6ncFzK|8VL*d58l5AYC78bV=5BMn8Va`9JwB|6sTJe)7h~ z!2M@j)gNB~!G8cD1g^0)urc}J(tmu`e{wXneoxZ2w{vm^0Dk`f==G;RK#AwolD(tJ zPprld0P+9fUWDkv&BX90XU!iI0RA7$qZDg@G|+#<6mQ||e|p?V^1t&9m|nvC<-TsD zZ>+Ds3t|Wbj-YR-4?5r`Fa>K0Vs)C0=rl@wBnb6$3m7g`Wx>q@OwcRc|qNB1RiTqRPjk40m`>okPgoi z7dS*Y4q2`g!l>hOy06fc+9v6Eoc^Bant68A?-*ANQPSjW&McCZwRfceo&USTE3TsF zV!K(Z*^BSfvX+f9H15vBW5@3vXRW)^s}|{t5QwH~yqMk*{YrFU zo<>IWq;M^9Y2JAp2qWSXsT02we>!!h_J!7wsndeI5Sm`s_viR)r`-V&s`T zaj5gTFFZ8_Oq$<%2v&_t&yiq=QvIEAXe6SdA zWvRE^^lP+cKI-}%@;a~<;qcC7G;VZG^acTJ_Yfy!7y(Gw9^?bE9bkufhzI(F06NGX zkM716l5T($BNVX>xX2!LL?5Rn;e>0`Kg&L=U2+TRD|Ek8iX0sHwP&%i&9L8uvvQ!+#oM76!r_a=e)O7m(xw&MRA z3C&UC|JhItHxRrsT^etqCp0vGQV7>U=W*t}$JGv>uMT!NT2}bGWJBnUA27}AGDFZ8NTF9aqncC&d0JZP%Y@>QrB?5Q z_K@$PWQY2GpsQpGl+dZ1{Y|3!K5$bNAoV&((NGvxC@K&WjtRwrWyPA_Wrvt9s9X}< z5i)y^JU8iyz?tr{3Q#i-q7_;HMVY&S$&JB{*@{R#-ImjgKOjB_#yxi5MsL{u1>x=& z`eC+*V{CvhGYGZ~+b`M%I>-S0TOXxn03&*k)v^PQeV1%gb8~N_t8tMHEM!Y7f(cEP zCej@jSCzZMRpqjLU9p*870u2S!7iv(W04^&6b=>_i;Kni)NFpXFi(^}$`|ev=Z*8B z@$_WwhY;ou^X0ROt>SDr9?K;DuhHaael#~xkRnVSrUqAyqp8uFFZN-VzM$+%KCc-ZuK_eIE<7>q+f4dbi+fD&ZB( zj+r@^&>CjvoYyd9!_)P-<^n6>mCzbk9qbM^XPf_pK-nsRE*qrDiBuJR@7UCJpEleC zj@9bBE#c}>$xSnj?1e|4G44-lHrE1QV1V{54a>kY^-TXazYv#A<(J46i1%&N`Z-fW z=o-2Drm_T0+G2kC+-QFEZqkUBT6(ZH zJ7sg>s6ruvN~2TA?o`&bQVsh7<#~l{o5f+HJ72B4DD9E1MJ%hndA-oJyHKu5317d~ zva_x6kx{Kk*Qavj5m&9uh^xjE^KpQSy9mSZ+NcPl&2sj)9bhJjFCq@8KG>oTy zCYX66LJ&$2@SqmBDY!hiUnsl&de|N-2y*=MFNrsRDif1CFrW|-3-xC%{VxYo2gCKj zzKOm8uBfH-fB;22A!a>e2_r*&ef|AoeIrv714BcPzP^X;06{`5igKVKn9$h%8JI|z zu3nARzh5Pc4E7I9tP~6kGZ5qTL-n>GO21&H0R9VbSpU<%zP_oyJ|?&rIKm6aA!Fbx z4Gg@06I2jzJSnj8Ez=_7hZ&18jA@lV*NAh}zgXb3!0^E2!0f=pz|6p&z?8r!p)R3_ z0W8rH2$)`tuWyK~QRu~9KshyJO_ZRZfS`~dc*P`=C_1qM`oVYYH~u&OgWvx5z<19# z##hhh`*Hs`gg73KxBYJaHbf_$wP)R3e;|Ynd?cRw4u9!Q;v?ze5ebMG8+eK2H}Fug z5wcR#W3*JYWwsXAC%9O-8M+$VE4*CYZN47gFQ5Rye!>ESJ;VgXdB%E&Tc`*ao6DT7 zB(o{4F7xq*lF8pSy3MASZ!Xwuw%Z*h8?l#OuGd?m3dxC?9=(PJf=^KmG@-E?FvBn~ z|Bm!mjusiJR+rMVAq-EJ`6MhYb9`UM9_IBsVXYqM`A2SQ?o_Ir3bC0)c zzMzobOXZBxnar*(gh%C2m>6(sfh|D+hfpbd|6O|lu;@1!J;8JrY!HwvNNF69L4L&8 z?Oxa_v+rJ@yQuHpfE!G0bub{NWOyC-^&C|Tw*@hjlrECkq&ZS(Fc(Z_hy3}mU|I|Y z3#wsPLLD5)YEYeG8s{T!{CADsW6GwJ2V(x}=h(F1)Z7I&a`Ee#tjbpHZpRY|vw2$f}2 zv&^KAg4qK_ZNJIa3DzaLStOCve68I~}-g8XzRAkS}a_qwDwT-xMnZsKiQ% zzgHxPe7D4z{#1c6nV?Wpxxf!yUX^XMg#Rm8xOGviWKmw4b`hJm zj*At?74aBjlOsPWooNZ9Uy)I)b{(E>0m)#rrzB;b_dx=3PM653giv3q|5a?eh>vQP z7Y9O;xJIGs@#|92j-b)hjGnG^>(W^CIPT$I;CO1rw(H*h^a1OJUj4g^GQ0g$QG04y zR03aWOMWP#co8NFlkdzuyb}g-Vp>qUO#wWQXsUqv?@Sddi!Qd2UEAz$DcN($IWhd< zXXR5jB8@!`Xsl}SeQUhV8ml9|AkB)c?$rcN+zJ#2zq~xR91U`q`=<2Tx4Wrly8Ksm z0iFYhyHZN+^;Q|hLZ1y3lXWm<6?60gs>?*mQu8!fMp>_A6xMY&8Af5R8HwrdwDwuz zXU?tzLiWqfG1+%K$AzA_%_e*T_G%&9b#TW8T>)Fon9U|?F_#NS7TCWtWmJLr7RHZ* zZPit*z#6Q7A4(#|JHrXjE0J+smY1pgP`;NU=yAqMB66=9w6&4lEVf#1_Wrr*ZD}%} zg;tNS$0mo}GWfM?gfG`u0)SIkK_I0sugMWquUza;;`=*b z?sHDcE-CrsGP3y4&%SrWB_UsX@oaHS(yr)eiln*(ZKm^nXhq7nd=_<;q?{dwyBry7 zHHR`54@4E7Q%icpwzwXkld7t1NBy;Y^+vigUa=Q8pIqjJaSf)F^#~7JQK6KAZ%!_{ zKnQC^F~PH+2!hrO9cqJffw#08`d8qIfelR)>sVWZn<`^P{kY9w@xI-t)c;bCju9#Re_#nObA9moX}WoqcxA-!1}z;W9`uP zc{qW%j*xt$VY|$Zwm{x;aQ*0q2ry%WtE4AzeISmIc!|Pw;&A=Mj%+|ZBw@SMj*y0q zkVuZUAUtGYyHK2! zp2ml7!EedX(x2NzN`7_Wi}*2{=?Z@P14@1^;fs1SM2{J_C9Wh#Dg92{^Zj{O2G!<2 z4@w{a(Dye0-hI8q2g+M{c==^&lU8fN+NPt`BC)ijX|B|ULK?e6fRdZG1X~@Y01c>~ zhUiBEi5iHn%1?zK2n`+jQ9)5rJ^1kM2(Q|@%1(ukUh~^O^D?}WN}*4mzh4xw61mNe zvpL_hnFT>p2t`VvkP*X3l0Rw0KEbaOUV`zR@=!zM!LRoqyF_LkA8Z18y2X)@Hz2P2 zAAD-p3|zUVVwn<&I&ak4HPYSp{xE&{fD$NLk770`nS-kclU+>*Q8VOSp1y>5; zpbw|CXPYA1O%KUcf}EhbI~5gK7c#TL)_y#Lv~kt>9xpaPHJ*#f^qI98q3izXbyayS zwh~uby|(9WOT(~+;{2opRo(?2bpqh0-0}!@4M`UQ;O$N4lOs6OfqcWg&inU_Pf`a{ zgtT_e3=8>Dbisv$`1+#6$Ia7w7xRfTC6qzQ31d|3P@s@F0-*+6Jgb(lq&#FKK!G|) z$w|rj(qGzEF}P{AEa5&Q#)lGx3zfP4#m(*o;a8^J|HYTQdCTr9z(KC`Hryt^-?8Rp ze69i$hqY?eA00@#ho9wUye5|x@UHwIU_b7JKQxun?0O8kj@_fZV|_STb=v{rZoOHc+!qCfjV;Zkb_qA=-_6S zKAQpGcT^$5h1sRecx*c>mk+PqMA~`HO}P2a;d;@;Q9w&EnRiSgRKg@^v=neAAyAEL zHrzabSS;$g3IabN4k30G3x@MfPz@9%Ld^!uB{EPf2qEF5>KS04U5z4%q*v0OT^18D-B&>}xj)vtyT4!)G9l!j6#^TK$yv>mia47tLAiRPM2xD% zU~ryzJ=g8NooRN`)$FoF=JdI(&hzjqC?ncPQ=GqUwR)!SFw>c=WUpQy(u?P2V>P(V zE!E&YoL%8}xYo1Z=Y`+#01_$e{_F@+E}P-wX|`BLzWWmczj;sNYU>Snsj51FFlfBt zn_CNcD?;mCswU3fl?sn*fZ{Ph$)#2dzXrGxsuJuA0L2QcVo)FnMilgj2y`FT%tni! z5x4z%5Jmyly)Pa$F3$8{VX6}sZ0r;NF2EWfQID#d1yU(n41YR);}~(AQ9=BoHXh%g z{(5_?pT*-~IMWOJzANq86WBrYvEMfNZGFY zs1H4Eht{uE_sedtLE~-@{f6Uuic#1KJfS@(69V0nJZ{XkxFhNeXWx{Id<1{E3A0~j zi$U^mD!b4$JyNj=+VFtt=u;akdVx5KUkQ;RSYJIkC7rpN48a4JEvrgS=@onI&+6^Q zho9|0eOn}oQTNAeU*jG1o!4EOIz%0p>G-=Obl+b_b$~V5QhD2yn1KQE9?qEceiz!` zJFhTrpl_z@cUkT3F6Nue550W?>UwnY$=<;_o#J3U%8mrYh*?b0Y&dE+Y1_);(OjAf z6H+#Y75GDXv?h5*zy>(Jjz6??sPb z%`S2C_ya~8noV}eC85{gypkb*!JUSPLAb&1-OWrlzTqf|@i87Akkf1XJLvb`7;2Ya zVMi;pFQoixdJ55~T+Pq0gw>$vc)|s|ddKTwR3;OV0dkZr>p`4OHsr_1+hGb~qzG0E z6JzmTu;N*HBTE*GM?z(*f1yOj3Yj2+XAL7@Bc98lo{kVhjD?Ty-<3lCAu>=>1W=L0 z)FymW`MIBdk~>ULyH{&7U(Jy1)ZMzt;SGFJJwtiloYQlF_U zE?`ct>qnSj`U+bqs~ z|1p!Xb*J;8G^tYWGhNT|dk6WoO&qQIW#gk>J?~tH%WdUfmT8)roR{6l+zBOoLabeY z>%l6Yx+1@yo`?=kfL*G{fb#iNk!OBR038c(+P_E7%55x@7XN4q{Svtu1DBV&pnERw ze8!wY&|@pJdhZI3x-xzWo1K6h#~Fb^K+$P775>QQp;6loe>=o_?W@o3PR=m&VJFI3 zEW|qNAQqCspB;RBSq_vEh=G6p_Sz8=uy}$vk4P`K0$j)2V4`5eXP9d=VnJdeP#l85 z?<2+F=Hgpna+v{c$GgAAvVHvYsPlY`z7hy$FV>!9&a3`8WyU4yc{g;o1a3U_L(6Nc zXIu^;{@&_#pFkPKaMbJ}$crrg(xR<$z#NmIkrF2TGK6B23&Ko7lsgPxg~_7+mA#6v zsigG>6g;ao5LG-tFwTi&v}Cxf9T%-k+Gw)rc-SC~9i0bj!cSLpF{2xG5tVsC+3Ubz z^Z7K9x_gOv=i^VX9q&t@vfKB=?hgM5y-ss+llM(kqQlEer#okCFZq}E#VG%kyVJAY z;p|mv$)_899>+(h1?+TmkCA@d4&W_Pr`wqB)L04CjP3qdhCcK&`3B=obaw`5b3WQX zVkhX8ogNEefr2l;-#I@3ms1gK;`zjMNSy>vq*|m;#lfEqylK#N^m1S<G3?Aw%$&3zL*kWi-?brROGT&FMbs;JioU-C7UJyB{c;t>*teO^7=z5UzcS zp~2=c8neIhdga#m`2A}&i8{~guD{5JyUu6HL&<0MMbd>hRabEfDbmC7MQv`&wI%E9 z?}d&bUK%y3N;d0MpuItD+)RcNo3EOWsH)anm3=3cSu9;`yQ_%6j)gvCbBr||qJ}~j ze<R2=eQnzxh7*Pp_9EwiMQLJOh;M~#tw@s4Dt>zE(4$|$i+7b)~a1;%8I!@ z{LN7Eu)jSP_@o10^_5_BnoH)99~2f=08KKPEa1%~AhaMkv^;u=sCn1Y3{0E=j&GOK zX0RkoDE_1sjs{0lTb-?rX8OprtX-K_4kWlC^6H)gHK&hcY{q4TC?DR#o(tg=LJx)K zAJHPZLven5vWAbvzE-PubE#{M9f0#gZ*1OKh)DvsdMWQ0?-}W&@2v8daUh)ww$t8M$X4Bj<7G z=n;NC5PM}b_zq$E8(c=yJMS`hd8Z^welnP?*WV)+$R{BN^2t}X2`mGxMRy}&u8)V? zTo9`8fh;&}>S(AP%{yTTJd6`TENrTL%ku&gT`hwiw1M|w!+k%C`z)tL;YW}Mojv;c z&PJ=*6p>`Ny<28MT_QtD- zasNV79|0HKtUMS#%1qUbHnQ){Iu(*P{XrdvdM;koh117$)f-Zv4}LnPMS3k=%Vk5n zwQ9ZV>v8aU?2a9Oe}q1*i_=VS((-G}^|ksWZEa+JKM@fnA@QJaR3OqyB|!51w|-9HFGAl{3p zzK~6lbs>Ty3nstVI|YtM_me=3;lVnX=GxsF^{YkKn#o2*DK@YSUW2;+h~@)_$w z#8=Q-Cofe38R8AhB0CJ6d$S92nz+U|_qTlCGqeuHXG`x$YJA{a(|F8`_;B=ov7I&ZYbk=|c;`t0=1pFG$|K za&BUxEP|uv7ysIIM)BNw`(?UDm8N~!=UEH7IKvWx9P@-ZbzKOQQVL3o?% z7o;eYt;BX%Ism(ZY#ModCy)<8SVyHoFVIbWUfwf!!!F)ovjm4ClP*RvCs$;^SFTln zvS$y~mDs<&-ZA6TW|Zi6J_>r%_mJJdV6xKy3XJj(eLk)QGJvy+x+u%}h@4)>gXQoQ z1%&3rLHk}&)FH-{0_I%n8$iIGg&Tlis3&gCf@lJWNR%4Er7Jg8|cUkWE#{QR4-_nKH|J_ z?xS~6K2jIltSd|HY3yHD!)U%j6QkT92#h*BOut4GiWXaxFxP%DAqDKyhk~SOUAltA~h@O`$T*nTXn(z%?#p z0A~U!v2^PQ!;%sS*fUSTH$P7Ur1sPDQoj|8Zf1g=dY$&qJiOdKwZ0eunqM4QR*b8p zk)2Sa^Ezgn8Az$@g~?ZPy+2VGsDINM4`tjQtl>Tz32u8OPj>iz1w#dh1{4Wxc>TOUrO?*}98%mR z^xx5mn?D?0BZG9XsDUC=%#pZDrW0L8vt|3_EGCS$=tl!lkB{JGB9>7CNIgLv*OC}o z#lJZ0J&&;C^xT}huT(2*JO53UCV81{`Dv+2OP&{E-&`5>E*ecXBU3Yn!IgKNO`oUY zW_T?>f~yc8CwMKV;lDVTc|8n! z=}sSG3aJM_)W`0tQ}mHZYMD@ksZgsc5M*p|rPe+8Vfvn*&NKvtOCv?Fyr;FLm<=!uciogELSZrm%?FfNUpXNE^- zNN3b>>DhQ`=Co{z*a!Na0j}&UT0eqC84SX&4Ek3g5nSnZqC(=DW%JsU+MHFoL)73e z?E^4B{H9FU0Us0CTpoNkwodJBdj6!4B+(cOu@&+C_En4$RAws&(iwP~L^l!S+|IhM zZ2`Ed)5$KU*RN}2PP_NiM|S%6U}*rD`^C(dDLDSXl=lxK{<3m*7@VSPDx zAQ?EWnk9be`0RD!$vAh!H_g*dl-d4zpBV|~4VVQvJs2GVV>}d#JCr^;GiIQKg2-Y+ zO7Oy}A)^x-=@w+rD;zj(lGd1 zHM61_qgG%9S89sAz19Zv0*B3Rl=szm^pjKZ8}5~O^tMf_qI=olr#9Sy9@ZbnMFn}7 zc0Q7^zT}HUWUpJ@wV<@!Bn|Sz1@gns{g61i3nk+R7K&(gx;*8Q8qlwOr`OgbOR*x+NcSvi=3kf3{M-HV5QEUY-AlL#7bC0#nRDbx!7w_1sl7DU)=@UWWd=P^gzzjmT1^w0nIs7xG!xVhWnTFDgSwu02 z;N5US5YR2BM9d)yLL*m?9-L*fl%9cvq|msx$FP3wCwXqNItTM8zHU#^3BBD-AE}H* zQIlwK6wSDPp9s0PYL9Kr=&iM0A88x2RoHy5x%kIR%T%t*viGS(r!0p8tzq^dyhuZ) zo~Go8Ft!kOFj}=ad&;ti5Jni+vrt~SN#@7-qxbriDS~J7Dg1O?zlw%lC?L`)m=gIuG*}f+t_3S=fkJ?I?zH@uC?%*!y-Qb?mh8;EMf?aX(5Ec(ve8!3jb&;dS+`U|%|yMWMwmY4^!5hfk7>zg2U3iu7V z5AqBxrY(VHjI7aPiaHx{)7c=#x);KI_Nv4=?JoIOWYp7Z2@73NW)e62 zKSOs;C^VQX4;6O#H~6IRlw65^l}3fGaM79&cqMZxozHQC!dcXb4GvgGykc;) ziTBBL4N``*gm)=;`N=H%$WQiuTy~B+Z04H5k9!@ubsLK<6nEBc58HUPxmYftULyB= z>{8^uY!Ztt~E@3*HqNkT3%(Yk0acX-^?ICTIk@MtMRTL0jeLH5{>!z zo0leHM)!UrXEuGthl8Tq^Cn+4&Ngu;mH+eRUG<#$ycC|cYGtA5Ex$N-(W`W+Xe{YS{2AoZA*RK{9*x%LxUj| zJ;t7-HlsW7N|_Zl+nFwUh2_tSCtO?E@F zrO|wp<-QLtW0=_(Y-v>Cfo!kFjH8i3rK-h}Vbb3+Sd0}d4pEX{r{dY9GFd9WS?o7e z(JwzxL=JaMuz_44eN|boc4y(EE`)KQ`&4yN1G}(nm@x$z?UYIJJfW*4kmLxW}-0fuq?70&{BH%2f5T;75!P~6r?4+%8kV+n9?f&&kI8L zJgY!*8JTeTO8qv&%?*g;6P?dn3V#q>i^!+~PRhnI``A9zLq5{Yp;b(ym1Zm`Wv|0H zIZIjq*g=Q^j(pH?OQ2woJVku;cn}$q!nBc8a?8M~`U(1!jMejV2)N>xnIcvu1ixaQ zx%Z%8YYP~;%nOu`7z>H_$0<-sg$Ze?X$X7HP^=TYua=)I4JLsO&I^Cl6g8{SKRmPc|2c(cD2P_!cm`Dy|{-z z^d00=qpl1InE@ZwfTS0ahKE&&j_n?mNr|Jy%Q=!e^4Zpo4XJ$2rzL44~~m zH_$)lL8F6k){%h}a;?wIK^(4F%g%>AovQ0t(1s&}m{Ayy+Yp;=2+YiLs>N-$KRixg zPu};nI=p{}^X^5%&f|Y!_1LS%_EW#x-&daGOVsnc(u0USn1Aah;>_`~1C zWE_tAO*XZ@J_ysmYiwRro}9@!jBrnck5$wmSb-XQ!I&QFi>?0=o-K*b$7uX`0>i@+`naTD%f&K7w6037<<-<9QDEj;`ME#HzREV;^pb z5Lgpr2A+w}-sR0dcqClOX$@#Hm*dgU-TB zw6o9HDy{dOmhabp!<0q7?dJ;{8Tb7-`eY!Ra(%o=)4v&30;B?Wv-~Zi%f9y(zZXM9 zL{!yO6di@)(FJIqiHIVpVEGhI*bRy~I`fr?9Z0yPTbwNR?sPcEbP|uUo`1VV5s_fO zsC9q*vDi^=5KPdHzS!;MgRzn;;l$tuUqS71b_Lzc2*?|)E)0q2fU)`qpz4I*Rb z0b@Sw&71Kq{|LA|DE%#`vFQBv>DHp>vJyC8@U=eNc)R&|O~UC{i_b;SNKjaQer=ZWC7yHO7VvmsHFX(?QK zmek=hW{5o(x|9!F6l~8M&b=T6ht^DKHB2<4^hhvMsMU34SGh8JqYPXvgS=ma-irTu zcKc4gBd`LF7Oe+uwV+4DkFu75|CiWj_5*?M!s!4;8_QkB*M#-SSd!y>+rW5W_>w_y zBa#~POS*5nxgRHO99GnI5_YXhaarFsyofnKm5#{2Y>n(se_+t$y+gC8a8KH^mjlhL zbeDO>Ue7Qp7o&m51LXy5cFKkb?n;}P>@IcP<}rD0gNg58QhJ}8+YbBHp!UbY@TG{; zPLvegu5bRJQ8e867ijeuA=Y}Dz8DZ|zg@lhRPrRJI8VMjG7enV3p7vD<8SYh?8nNF zzeqQMElGq!gxCE>z~UhJWJfuGPSl4Tu9j~Cd9oV`BEj$!K=8VE%2Z$XQe=y3XyQ*wmGKaRLph%}V{R-jNOWPfAGiP(Ub&CjSAI`jmEYsvK#u&^5bV6WnoNm(IwX(U z$CL2V%9Jk4QN}spFauZ}N6Cb=3DQ?{x`>ZC-x0~kBQ<)?EKGOw>kaAcm#<3!)S&0i zuDmR=CPMgXraH}J9>~%o@N%FzBzFTP1yzhTCUHll!ZjPVsHXjae?>T2!4L*e-Wqbe z@-agyqV7c)@aPADZm}j?ZDgJj>(aAoCyQ}$G~;ishN{KVRJiHiLknW^By>IJGD|Ai zZTBUhnr0AQkON`}$!o#)6ARpU)5* z6vT2E=19pho$_bUc{$`15g(*fP_Z4zX2N_*NSj`Nbu6B}2n?!$*rME*6FpDPn#$J1 z&_r}w%_Jq*It+!w6kI+7nb4=3h6D@O)|$sawMWL zVTP8tv_jc|kjzy>sjg)I=<}6|^_~2+jU6`C<~G;#$E9d&khI6njI?bZITYs0HI&i}WM}>hg!CLjLJkIPUnEigK41yjH%zvgDU@?#hL_@+$jRJfs`-()Vl4T| zS4iVvN^y{ErlObu4-}A(LZVkVMON@8N=G3a??~tWdct+nPjoq5}$hg!pS45LCtF) zv(pMojCI4~V1~w>gLEGGn5LeW<4ph8e63k`ZjytXd+%{)Lw(Y$w~~*3@uqLj_vm!q z$4Pb36u+$~)AgZSL*|!|A5fcIewiTc$nbi#DY7hI@~MF6n-LADax5?n8JPSXQ9ILb z&m9&u-J|=Li$#c=H4Dxx<1};9cJaHHzuqkhM+GmI{SC0v*qSvK>Kz^$zF&!t(zR_J z&7R{OC1B!aG1&ZOSF4OpW8w?7>Kz6aJ$7sBCN7O;Y;+o}L+3hOw&RD#^G>F5nC$Od zs|q)5ptxg{Q38mQunToi3o$im+grR*=#isn(`c-=X@2@)b*r%z14F5uM$hDbgCCj{vJ&>Gc`%xw{}B4 z)zf9Kw9Im++;*JiwyCSRcgf?iPh1!0^_6w-7jMa02)2W-wXk6S(8VG3+pM7jvhLvb z41CciCIYAEdo_!aKLCT-vORl7p(l`bZYzVk&x$Nom(g@Us;kFyYObOF;PkKweCa~LLG*mauLL%P$?};u>>-OqG8_dgB2}y=SW!wZ6j8KN zF-64b$xG;1d!g(KQNq7-Ote@^*n*efBEvL+hqQ_``Ob)W(*s^kI;kH#`-LIen?_EV zCoE=k_)Xrg{qo;RY4#YHg48@+4{hP=WHp~(V1%f#q9e_fD3lr{o1Dml9^ag!W(IOiQ|2wR z#l&CU!+5I>6FoE`*>Ohz8D5x55Cz$&ANT5=r2U!sc)D}WJ(yV*51E;zc#p2UUHXg= zx!ebDBQ^`R7&M+Oylt|=BS*$Df)e(dFmfhFz^wI9l&2for{FzkH8g-ELdmKP&H^-Lmk5e~1Ir`yjaA@$OFcI}G&6CE#je3kV{2939#MSegRv>2Vb* zlb@U&H1Ie-4>|#FwFjy~JUpRC_%GaV`k@OI0jxgp(ot% z!9=pYP#g;Ef|Ik&VrHMZEX(Any{=viW52OgYlLD;9K|Zbih>}$70bKV+22enhc#>S ze*WTeBc?oT2zHCdMtz0g?DH=J^%6@Csmn!FbLOS2GAUl@cJ9ET`|Vk0B0`G+hgm0s zv&<-D1D?j(?XtoD6s?`qX}nfWeIJ=xy8K&yda@#eZ||ziwmXfV-@+H^TD|k*>u`02 zIuyp)3m;D*Jy*A(-2o1Dy!Iuji_)EKiu&ZcUya$5&AI?bW!FhWaP?qFFGeS7)YMPg zDVqPc*8tCM3=x{u+{bR^F8!!MR^p08!P4Jdd=}~S(D7s-GDx0)@MJ9fMhTZXyj&;6 zd68@cZ@5kDCwtb))qmd0H{=FlpY-}8Oi=}VQRc%48QV}D=L`BYo<8xsz|lIg(EUqc z=co9+GuF*>+2R!=aGe-itUH2}1u0#;z71`DpB*%r_Z&uuCw6zSEfJY7j<3SnL5*se z_6NHKqj3iZ=&jd$r;-#J^t}{n;Arqg*^Pp>C(m`vLC(F{oAy}S4paM$s~?&AiWn}e zN+}ZxGAlOa(Lkf4NfN0XA^e1o(G z9XPsKq;)N{#nBd66~-eKM>ml0Zk&=rWJe)5YoVedaZ=j8VU)l;+(hL*80k%Oic1#@ zOpuxV!H|SI(H*9IkXm(ZM$)p94)YI%^|JJy%i8H~jh~Y5!HYDPEs;3smY9D?^1$9F z2`Y9`LRGsIG~)|`2eTJ6cY_cHg=NI`xb$$7tncXa=$e}ChOA6=Ff&-c94eApg5VQ? z_=16~W0f?Z{m5NXUlW*&Kwm`XN6gWwuavp9?vmN!cNuZg7$3*aZF>&}%hIY7dvD~i zerr!(cO9*=W?j3VufQIkn9h2fiFt;GD1cob%(ykrYhLtc&r(tJy65qnuv$Y9(~eFw z>J7VE7GFBf__)L5G6_Fva_JGZ@GB!CQHQW8Q*m*lX7HR^-JuDUvNXLofqFf{reUmx zk-dzHVLfICBQuis(+Nlfkk)9_l43#9#)p>q=<6rCRIN%Xz_aZ$#>z*?7x1bp(hQd; zhy-L$wURQ;1CMr^i3jQOo> z@gtZPnDwU29-FtDj1|W2Op2FHR z^Z#uIegliC+GeadJ!dZ&Q6FrR?b}Jx@l-5fZ{#C~7 z$|spyp7Oph3CBn=CiEjHh7b{1^MrkMKi8ghk+{?IU2vi%WysV2kt9FK^R;1$4n*-I$1~r38X-l0?G~NP2G|am^2P~N~s>muuWkb^+ z7z<+k_1(Z)xa!qceVdeOI7xf^Yz{`j-f5IZkx;_5xa79SI_wu?p*KY=LFAdb8`WFp zztAG@4I`bficVsJD|R|R>RrRzj7~FR@uE1GxB8(-z#s|B!?^Jflof|$mDI_jDH1I+ zTk~z9l5|}a(&h3*)UCgY#Lqw20^g0>l#-AwE>qM797yDlA>NA~@+rEqYjf}Td1g!tP_GoXd+zFY?SK%EG`yPdAmTZLeC+Ij!Ywh7K60tA!+sXNYJK**Gznb|@)s*T7(w6b{07+ZW-B{79Ihsl59`en&e6Hd{KLlamAnw_xId{v{ zH*xno|0~!?M-QjK_(-!uD2f4~6F3*>HT+ou(It#a4AA{4qpK7Ic}h=B^EV20cX1Iy zz^isqULkj_v6IGtMRljeJpj_h?+q)v!nKL9*7qMGAjotufsqoFw05Y94SO`3_l@-S zs|kmCna@u;3nc6+P#KIAK^YLoTD#<^>IC+-C|j<0veL-mt8JE^MXQE_ezKv}IOufp zSXr)4;D4Ke`@PXB(JWKy;%Yy>VeF9>SZ1#5%sR*{zO>W}lAH3ix78v0ke^DT2%TND zfDu0SZ)l_jmLip8BiwxQp6LGpWu@mChO+#$R~@J^(Zt%&|Lp#R*8Nyu(+<}F2H)ebZno`MP} zuDWr@@h+ueFM~^s6H=tDNJq(de`k-b z58VegjfB3Hv)~nwos5Bv4F1Yw4_`2f0_Q+F;(BnWyUV3Cuw3=8<2VzqPHQd+z`e3V zAN}qLv`(Ib_1U%?*c_3Zr*R$Hv7Lr7)n8$v3&ZgK#vIKx;MC*{G(Uw7zZ@j)E$!|F z0qTYp6`zfHMz1yYhG0W6eXVj|8YAIwf|V==$2KL|Sp0`Zxa28Sa$7%<1^FKOsO&J# zDl&O_Nc*IH2V}w9jn5%J@&1G8TZ@mhDTkBJOO0kTs%{gG@8^$nF_3wCKMj;24z_UA zZh>%Z0x&%!OD8thZGOZnL<5!hw1rxEPno8rXz=}j9N5_jOnLe;{-!!MXJMF2BUm(h zw6-=z{M=s0weX9c5N7eO6MXvFo}=Z;vP1cFrYc|G@zZ+bEZguDW`6Gu-_`g)RNHoZ zw#acWc0E5ole`a5um2MZ8T96UX4T57oo^5Mc}z)u`mmykd1ci%mbk|h7LAy3!^I(o zo{v2jwTIvL`Fo5PSTBX>pn9mD?phi1rAuE!XnR|qG>BM(OfEI>!0D~ zG`b)nc|DJoG#cG_2=%+5VNlS}2hkYZefiIup@o3{}WrFodHLsi0yEqEgXgCoTb^7qk>u#vodK z=;18E1^M2b?7o?O($i9XPG4^bn!D^1-wi+N3U62N%kPdKy~;uZ+|Z59A{3+yL8OLs zN2<%XUNBJr7=oB6c;xlZrfxxR7#PFkWly*DAN~!Yoyz(Pd+ra?>9x8Ba49rcuW7gp z4nuoxOt-Or5|04|x&3K&>JoT>H2^%s!+a~m00SX{epp$%DF#e;A16qCCP!c`CGjJ7 zr>O6X!T0HfPw}C*biudk>PGIiGCd*idS1|jxNDJ?=C~q|MjN4NG#Q9q&sWh~t9al^ z9noqL(80(l$SW%t3Zo6YVCXp-8w{br=<-Alu}~B5p_U}%!OLF*f}SNqmk8rhc|I)l_oB| zj^K=Rmoq5=Vn>rMRi7&Iz(QKxW#(Lvg;1Tp#^WTC7(S;Ya^T}Mhs}N2X*2tzxqF#5 zsDnrMnD@|+2-W*1<@8D8L`^TqN}y*nbgy-@0`+?pVO~zA5RZ#4MCeq`(sKKeBE^3H`N@^1Mo3DQC4$2 zYE2X?&WtSW%%AZ|op88uJ>V?p@WaRHes?gx!}K9_cSu)IRt5^-xB!kye^)1*L-LOb zoM2vu3)YHv1w)qvUcR~>pF+>D^|Z+Uh9^_~$;#ypG_>pjz{OHvVu}(cRKT9B5Iqp3 z_NBSSq{IYziUHbRhpDFlqj|=19PEd3gPan^q$GRX$$eA$THM+6j)*jmFPa6UYB5Ep zjsm^qv35~Nq$Ra}!R=T6IO_HB{yXJgU-|gUW#4V8T9qx@rhZ#HyJYUr(ZfbuUpz)g zOwE32$e86@TV{5kE&r9*9scBl$FXT^QStGq%Qv(;=Daj*bVJMDnd2MOz2SE$eiNg` zc*So5B<~7#xdeL`BuQIEodXab185js75H#080ygyl>bL#dhZnS$Hd0;&CKw)QXMJ4 zlv%M^tYkivGh)3zVe&UY(KSyXTA%JrR^n*2_LB8-^=u8YS=?!^RJw^OyyhP87Stk? z=g&!wSK?;~|9C;|UG5#EEeJ9Qb7Bvehkj!)Gg6aS>P2R~!cBv>eZJ?z;X# zd7D0myg=K{@>gEFapor4ayFoL_BAsLmi*&p1AZ$eFb?ZpG|6R}NX84SCq?0}Idq?D zLo#q}TS@{u;85h&6>LZ8G`78Ut)yS_vF`mVew{5!kw=zUSc=f~Z3!{#Ktx%K z2aGThCGbi+C+mGVnU{OAmlfGVE4t)*4%rd9ZeLn*JUc{D7UT|s4>QiaEhppB&-GZ0 z-WH^f))`J8zT0|Qj0nvP*50V#!!34i>*#Zt2YW0eqHiCk)1xefp4PB)QP#_%(1vBn z8kN0*wG8za!Dfkq8H|>Rrub=Uj|O4Q!A2LRPJ48_*rI8_ig& zdDQR)BT6gEZx}g}Z#{nCu)J~qqqNmggXH&@Z`%3mtv`YLed~|QYHK@b#CM}n%U=*Z zX%CX8v;T+gf>1?uV=vSJjhM#h!5of_8NWFJUS}eQ| z^mO3t=VNKRx!RJSN@*(zVx1QBF{z^7j;&OuA(GU2NxZ^deY-x%ZeY@Oo+0-bLkmQF ze`btw=RA8IYSdH0$Nb=Mh}t?Y$oj*hJEagb+r9Bp@etMksN2Fy^M)P|zdVHewu< zV0wV*4n^C~%zGib_{qgDpI(i{J;$22{l+fhIN~MK=|voqUko%4zpi}5h*@`4k~?be zi_N-kmu+-e+30`1{V^V~_u+@bZsy2N=hiLy?&gLoam2e#S0_HOK#i}JGlQBQX9g{> z_zAS1k{uVYo1bZY7{@n+9~aO#z+$m5y@#=nKgl zhuwwj@F#_}Jt1zade+6E;p%nB;WbTC@XH*4oV@O?>u0ZCHD~rc5BU1@Dd^w7k54!} zbH&m*vu?R{W|r5Rm6eyrdgbsSm~WYAge}ejYZLV8L9vOj@5y@b0mXQY3SBRR+T?4VC`MwbjsPVFDPtAs!4@Hhr|alXTo z;`PZ#x_!R@>iQJ||EJIPa?g-$f9^XAa=7Xoy!V@LlyTCEKRr&$432B%-XQht4s!Kg ztzaQ$=Qk`^JwOXEiGmuIc{AFE> z&<2A)z@Go_?|6VE)V7?pf7O1J0U>n#d@Nf-1pPiB<(q(%@*+S2Gy#$#qzJu^fui3B zq#)x^evv}DuBlfB++oOlC7)GM1o(g>Z({I`y?oyggKw0KVepluI_R$=973F&q7&Hr zEeTQp{>`6I` zXN1$Zkop_3v}V=J>N(9ssk<=qv=NGMLJRIu1sTU`aMkD4`dc!tw{ly?V}T!l^X-51T^vr#*)Jaai7yUb97j+; zQpsfr`;iWr(AeiAz<;Ga3^i_c<%^U=q02WhaB71mp4sCA@M`sXy-9Ck-_Jm=u5?QD zd!g9(GZbUmkE~gka@HZ=nT$_ie$hht{(;dEgP$i~Y}xV*$qKyxZKZA0G4-Cx)8JR7 zp~?PwCq{Y~Y@Z3-D>D`azC?$?+EYzir@@@0^c~V80#?n+`fOO+Oq2+^(2<--i(6RM zIWmH^HVHgOJBK5bCS344*gwJBom0$CpSOT^CKjOJ9nZ_BJ~#k3dgQHoBhGZo-_^}n zvH9lrfNd1_uR0!SeA?NZ+lAn?{3HO*@d6w zBq}~*3ppdSvwQkt&=Qsme%^#>gLgdr4Gv_T+D4$|IeO90cu6GmJX^2R2t2h|%Kxc@ z;L+0F6rg{za$n}9o~-j*H5yHf2B-i#W1&TeCVJ<&)9i!*9(clOr;U*DtRK?nYj_?u zn`75=#j`i1u5Z>Uk9*loND{M#5C8^WD))HlFuTZ0tBp|Z)zB+9B+-jcI`2kbG z&S51co_@tjL_g4cZ1wDe$Q~c47!0IGM_g5;NEo?IrqFAHme3^{HH0lPB7z>0(^cxs zL`BM{3>L9EHnIvuM*fMBb^dgWhL;a59z1AZp>mGfCnMd%N>n=UaT|aKST1vq8~tjT zZnwHQLU(D=vZpTJJaNej-|(Hvf5(;&Ei8{PoXRLk7h(H0NZq%?-F8jrZP$!FK2UcpOCh|m%T8%< zcXCIPkVF}c#?tWJ`lB&*eh5?kXnRcmm+irh|J$D65wI!$tIc3nktsS+{UhxWuu$Gq z242Je1EyXT^8k3-V_;-pU|^J-l@}a%J)Ym@D}y`-0|=bGD#-<-|GxPr!ePx`%)rdR z!N3F(1prZ<3$%FJV_;-p;OPC^03;dyzWMu-!J5oks=Z-l#&KQ4xxAmp@@VY#FG~hky1hs z5sx7)QYaoIr_w_S(uPt(@ghBxQY6?+-|QL);^E`%{xkpV&wD%S0<%K^WE4=Ad5q~d zXu1s}&#Cvw z6S6?2$fDh^(q_k=(MKPm#&0dVo~g)Rgz^(5H%DD0DTHo??>h+jy-?M9ALN|%0HHsO z&?9aOC8=KPcdjKle+v8VYivpb4SyUBIWrrwj`uQePE^f&)fu#@t1^vIJ!$5o;9SW^ zEXfH1-KN^-msnC)CXmNwQ@$WjE0*4+Y{bug5`nGDk?k|bwuk2ix{13wjSSZcGKS~g z0?LvyyE1Nyx@tbFmbsLyb4uNfyo|gz^bS?}_J>-GeREEA2cw*A)7wW`3%2DI(oqk+ zw>5$3>b&ivk3*Ot%iQ0QALiIiVvBySJ5}?L^)>YyZ`lw34xV09(TChe-*3ZDFb`%C z1+Pm#+i?zq#5qLVw<>$|q@Tl0>_2vd zi71Ofm_?KsHOewX$sgf}cdP6t`<0AsdSZ6i(K;NOKkn^`^J+zGdboU8zD+60y%#Lyf3 z2g0oWod9^+V_;y=fx;+;CWd>AF-$^CQClgI(W z84_P4JtP-NzL1iTnjp1L+D`h2^cxv288w+hGIwOfWc_4&WFN_~$nBH+AkQUlC7&Qa zP5yxVKLrzoRfsr+ z3vj@7#(RuU89y^&GEp#bFiA3*WOBshm#Lho0}w`-7Mb<|;SDo4vrT3v%q`64SX5Zr zSb6{e;z*U&000010002*07w7@06YK%00IDd0EYl>0003y0iXZ`00DT~om0t5!%!4G zX&i9^7sX|8AtE-WtwM2E2Sh2luv8E?X*yW#AZdyyF8vDEZu|ikeu4gsAK=RK?t87) z)`b%8%X#EIU4IagUwP5fVmMqWU zaXeZDgD0?TeHc82Ol;BMX`IDQ4W1!>Hh30!d*0wz#O;c~Z}99p?4X7!C8FG-j1nA* z&$~|)poJ^kum|OJPOXC{N(vs5l!QS^tWvv2?-u>)jN@RNI3!!0zQk{#2^UAym5Cf2 zQ{O}zTeQ?A^SFktmOwm9JVRO<H%h3t#CwMB1XN_5Q#vNY1vYTJc?p(T&jM zCwlzv>|uFoa;m9DG7;5PgYOWR)U{9#?;m$YB#aQ=UN_@_I`F?xUQfEJ^#y#*z1*aRhIcz>8p3) zO3VhQlap@B(uwZB^R17Feri%##_{Q=Z~Ywgz5d*BiW$6L>;8)6O3hVT>wPiX)a3Xb zY-1OP-2ATmA1dYvtwnBF<%!JKq_wK{1F7EOvmv$=bEmP+Gl@*^Z%cmyEa0)H004N} zZO~P0({T{M@$YS2+qt{rPXGV5>xQ?i#oe93R)MjNjsn98u7Qy72Ekr{;2QJ+2yVei z;2DR9!7Ft1#~YViKDl3Vm-`)2@VhyjUcCG-zJo+bG|?D{!H5YnvBVKi0*NG%ObV%_ zkxmAgWRXn{x#W>g0fiJ%ObMm5qBU)3OFP=rfsS;dGhOIPH@ag%L&u5@J7qX1r-B~z zq!+#ELtpyg#6^E9apPeC0~y3%hA@<23}*x*8O3PEFqUzQX95$M#AK#0m1#_81~aJ= z0|!~lI-d}1+6XksbLS;j^7vyv68Vl`j*#wA{Hl2csfHSc&MaS|^Hk|;@%EGd#IX_77( zk||k|&1ueXo(tUMEa$kz298P&*SO9V$(20GXR8!Qp%h86lt`)3SKHL!*G!?hfW=~| zjOer|RqfK1R;688(V`x1RBB3HX;s>kc4e8;p)6Pao9B$EskxdK=MDHm!J6u-Mt|f< z_e8WS9X5kI6s&J4+-e_>E3!{mU1?R?%zwYF>-rx~rl?c^002w40LW5Uu>k>&S-A)R z2moUsumK}PumdA-uop!jAWOIa4pB?622)yCurwR6C|O`;Ac|F3umUAvumMG5BVw=u zBSf+b0R}3v3>5!4z)b(~ z|6^a^095~jQsFgz|AYVAZ~$4#;V(s&5ljxnc*2xDtwc4s6GDa;XMPT3|!!;Uj-vEAnuW1cvvLO z$7e!_1a-StfkUTdp!c$}k zLY}scD3DW7SdC}jKIma3c^NHw5i-v1s0)e5ubx3#?$GUzsu+QR)zw>{+TE_c`G7y) zc(eBl+=n(*hCTWB@^f^ja(+9M3Z zaQfWK!YL_=AB8@r0ehkiuv+$P#z)&OIAg|wY_8_1<^$0=KIr{1fVlv_Pg|nyj&ElH zDvcm-guj^pN+X(wMVYKLxY8A4bSLTCebS653qv0e0-{iZYw9nFX!SpU8oE1HC>t-nm;{_v%YU!F%sw8xqR1=oWZv4p6fYyi>6{;S z_FW2+4zSp4J!-s|-_GIi_;#5mDoc=@l~W>($BZ^eD&Q0Z$2E}DTB`D;8W>IpWc?c^ zg@R+ErejGHB@Zn=gD!u1?ZkU;yb6b4`}pcvO3=47<~{a1GwT_#Ken=C#WXXFr(AzB z#cbCKXO4Q_iRv&*desLodh{)%E<@^xh@)>uTEY-I23E=($bS3|-FWpDS=*3UAGz48 z`(?^%P@8J31g?X3BXOJ=I)%%%3Z3jmNr9}B&emgx`o=O!ud|#vDXUv9=oWl?d{&It zj}afoT!M|U)^cBFIavom-Q zODu)eTrhnX2Yib9;K>F~V8Sg4yESi)zSHl_Z=>T|Cc0)&(jMc*lbrsyx5?5zWB$iq z)r?-78|T_$0mIBLvkY=SH-q(pfLZZy3rLr~5Jhhv3p#g(Lv1Hx>q~t05Re6buyW=s z(%&FeWdf_B9wKs1gSJa1CXLP6% zgA{Ne-g7l?C12Lma_36ASOvs;Z+*iaeZd@;iuE?7nmWw;mkeYhy* z)}GaYLBwa&00Sh8R{3|XY=D56XirYtX^DnI0D(fo{|z3;a*>?&j5wT{T%8R*Z$hh5 zQ;y{EAg)1)7($tQqV|p0Tz3n8GdSiWDb?U_TYE5Tv!}M2@#x=mw%=jkuAHk5be%Bx zt$pOD7VPzF0S(67y~#>`|57&uv|%5WNiZYkY>LyB&XTa@QfVIrnxIMrk3Y6vOBgd+ z=!z8bRhsTY4jz~;H+9gr&z60PhR=CGqZz6MxI}_c!qs7ZmeB0MAzU=6@sm^q@b=Jt zh;;o1KT8ZX=r`vBX*_*tUwcY=op78;LACGFxf(xA z7Foo}TJ3%4I@Py`LmVs<2|46o?G>(`wY+GtsOL+Y?gGxI6bAjyu|pur7)S_DeQMO1fcpRsn)cl1kkWmkc6s$RLU~tZX@M5 zxUmKapwT(fbfOLNjFJ3^k*Ua5xkk#(e z(Ya`X4)$T=2y+@Nv}!sV{(zJLkmg7J@*(?vt}vR9A9h;T3Ul3&-$P~DwhYYTt!#r=BnBs*L4Ja7G#I-MjllIG3*kG7qU z##;!>C+M!?X^mB64Q{o>5q!mmnmWh|E!d2GI;lY5@Gpe3bSU5Pf<=uA9#p+ce0I2% zlZrvo#hdw6UmilCifx{{30h^-2@hPd^&@OAEoK-)0|QQ|x;h;+gt;V4LSaqPVLW*4 zi<3_K*;+kOj|MgK(B=g=sM~592ELY0>wvqSu1g3uLv&g!Zt@V(u0+`LL3y2Nk3Y_6 z>OoIGgK}=I=XaSBe&%GhoPy-4mN8~h59`(;{RCr5nr|w(&nn}2NLANYDY417Lmm|S z@pBY=v7M}g1UY)|3d5n1Ppl7A(E7=kVdrv7{4WH9yeq?POg2c;c^`zSsXr4TNK+Q1 zQ6vvZm(zaOO1Mo-zs1A)v%%_9tX$KZ55PmG0UnWq*Tf@71cgA$*zUPg(ff1;-|1as z*_RT$YvebO-gf+x@OfLZb!%HD2To)SLfEn`=y-vQm^mQzErF2a!(ujCI~hj6PEr<^ z-BAsD94hIM88!w@?s^V4!fBNzpT>tn zu82asn9`Q{Ln=g-9KrU`qCVErTnxt&-%fMq)VE#ZB@_E8CjB4`v2m674{;cq+;6U;{yBb! zM#l_5X$tAE{-e8;WLcIh&<97Fln2DX-hAmNLh?yrCJHy%mJQ)Ep>!paur%A`x1rqz zIu1A*D(ZdNorkn0+x&yO1A_01IcXSk8jLg^N2f7|bW9^6V1zV>Z<7956=-&4aL?|j zoszFwh|x`0rPFe4UB8sX5at%JG`|Vb*brqL(WuOR1`$b*Gwfh2t153*FGNpSFV0jj zd2t-N|BN*=PKP1FiHaL2&PCPB)7Gp{Oe_iDR*JYnmzaeVjzU{W%vlw3p{2#f#9Q3x z$$#9vas1O1HNJtjft+-!bg5cmalG?L&C#K{A5Yl2;8-o`Q>V%Si%Z>SWS$V!- z(b==6rmD))e`6%(1e~&?3=JIkvS|$3AmuIS(Cud-3{(IspMdtckE_1%wUYfP@|y&L zXj!WOWKAXLC`%?hO+R(HPA~zhyQZcBEBvkIszVN_JSJvI#G@)H` zruJbO%myhwF@KpNl*DYfxdk}-<0heIX<7L-blH-V>k8Ry0u~4MFL*Q0*k%fNYRDjx zJ#~5L?o9L6qLnuj^}lI+WftXVlSz?etp?H&nMM!J3R&|nnFQzV3qQchDM>Aibm6*= zAhoJ-wH7LrCNh)2s_-Pt^>jo($2Azp(qD>HUbm?s#+9V=Su`_D zo(d)ENtMTWpia(=kkD>~OG(3~yM)yz0U5=N^EH(*hroJ*IqyvCs`yAw+Idxp|O%w-g#VA{T?V>wl-;m&@AIo^O#cc zzel#UBw-f;ABNO(NR@}+5RlmG?h+s6zUVoTaeAzm4tbi8sS`aH=j8O^{K=g~w5%2D zt$nndke4s7-FCocaAsJoK$t;z-p2kbxLH}sWu?tcO;;n;{`1xaO%wA=DVmC%wFGPm z;#W~u2KF9~D!`Mjm3zjNMVzn?QM`=whLVD{&o=^h{OphTaFEAu_OHzMon7#IAfrUX zJeNPy48RZf#mE+(q_$C!I-{8Ur?ho@V@G5k+Vqe1apdedlP0cz zM7`sQ-s}4}+1Rj`;n*-6{B?%WE4lRerghnh#7@^3ZRs6JR|C5{{B>CGH9yN0yqCLT z*MH&lz}-V4sv-kn7)T%Uw z$hsDs#Up1ugbDUiRy}3GO_)Q~hulo^{LDIyQ6aWGhTMX(&Y`E3%IG#G2yDx4w1yQw zfk#(PU0g|rqj=cXqa2$(A_SPUm>-A zh)6h|XQ$mzd8>{WTnVZf=U2D=J{|5hGo=t)IUA@xfnJ-A=t@ZOP3qM!1o=lq%BU zqEIfo>0i*SgAfCdu}2~;VnYAWQc?%7@#OwqjH1@=6(^oXPMnfv=ngJ8o z!~;rmY!a`q!*50b#W#wGye27jN>8R5>5Q*7k_zUex53cI?RG_V)nz(|9$vg~uCzkj z)k{0PlG*(}+uLz!DDpTSB6(?7hCVq^*!g$_eMG9XZ^tE;kB4{75iP2X_@&-3x21GV zY_b<^bs3X;++D+n9)}H%OI5TfTitr#*7L=L)PRU|eD-F5LWaKzmwJQv^_6?BrQeRZ zXxOUUCn9=T(k`Z!+aElL7W5R35%G8V!Jm)%kpeAN{PQxbXn?QYwi#9Sd(ep^am3e7 zr1vR9u=R;${u+4iUIb>~m%h1lZVjQ#156>13$OTcV;6!@na_+ZaGI2v)9{w+Gq(q#D9XDO+x4lc;F>Li#W+Pveh!sZi!DR+}YTd zCz=hIC3TX94~S|RR_x~cwSHv03%xjl+b>0leVUq_X~yF;Qw*qaRg{V?KGo#3=!w_P zuMn255zV8A5BKuycyE_2J#)Dpntr=~`|+hXQ(A_{Zke_u;J3zwT5&3Yy5o3WftV2Q zzp#n2WGZ;sn@w}4TEW9aaAsqIV}tXl7lj%Yya}$-MuQW-K;D4=bFEsUI!V2@Um1q- z=$rxC1m^TRQ2?bcJ$%G!_m>G3otm5Ybmm2}>hA1vU~5Xt6e^bOiQD4RWkPHP5APp> znBZWS&IW5?>YWl$wU}J=` zK6)?*!ROt!y3X{c+VBQ}*5Q^B>J(&|X0v|NFnKQG=C7FsJZXc9VeRvhwbdOFmIe60 zc%H87CoMhb^1&R^2<*ZT4rk!+c5fuip6y@RC`}aI+V9?P6z#24>zFiHh;21M(DqOq z-5(Kf({ypr7pBv#qOrX5(C}1v6SuU}L!c$8(?M)ohaBRzeRV&8!Qnks!9pWpAqG%2 zkj|DWYo{d1{~P9B4Pc=wlmi_eq8I?MmPxj^2>Iqp7djc(h0-|ahn_J6_M)$1%&(Cl zRIrg$8Ci%m_U7#Arh4-TVOlJKG6QkHC9oJY&#wZtGoHE}ggC@?|BzE#G`IB$M(2}zZu_) zF?u+2$1(@96*ztK9Ko@P99Tn$t`<=ofgugmx32`!qHs!B14&L?mAS&!Lho{D#<}(HJ*sTOP zZRg*dF^Rlr=^llZA6sG^@!(hQNMUlQ36Fy!QdF0hs-)sT{G_6DVt{5%^_kcqqmyz8 zRP3n;_fyUgGww>NWlM!94QEBnS2}j@{su4nCi$hjj7!OMSwUsGybAEoZD}qK;i7Nw zprPb(oNA!39X-NejeK53kwInICbx?I_NnTx|#KXh*;YKru zBn5%Q-`!c=S9URy*~lsk@DqzC{xNmECXdEz&$^>WETmq~1o#=|tRR&Ia=I=fRQZVT zP>?760rF5$fQmxDd!g)Uz{j3O#mL`5oATL3a zI%*foukAIU* zKnY(`iRbPOz91a{R$>L6Xax(RcW#9eQjo4T1?Eitx?XZzcI+1P;@@}WsVoNlW zDK@f%1n>v=j^g2Hl^`ss;6ECCHq7~9DlkL0FM1CoIFxXdJX6zznIjJ73GH{z>7h7F zy#bGm+2owsk1J-E_R`M;i~~0u7ZKQlNf#y2j?XLCHh9?#e7#|BX7H{5T&A4E1Ox;8 zUGmSIOQpyT!;k+OxkFIJD?czU?LFA^%|iL)fCp)Lyt!N|9E>M^g7-mUB!_4^c zT1yzNybJQV-G`6(YH$Fkv03|5w~WWQoiC3WNz=X)HoqR>?wSde*Y}%abz8iU(jp23 zeb3bTsJgY2l_zOKw)p$kf%H>=L!!O>l=Ii!U3+ZwU%@DrrmPu`sqxEL%t?_)4D&aM z*wjspiKZkLL2XzuVavkCdx~Ob`;)0AzG@5`M~TRqXW7D5T^FI za+>CBKBYp?$=SScVy80a23Ajgz;!2)ZD(Jno=Q7GeYwj|G(65z($9oGY0=f9b~jm( z+AWf(Rzj$#)-Y$bkoSc!IT2sg5Bxl|g4kA`Cef{qlmabyEN2Vsic`;Bx?Ue6puZEegVD!FBW>hm>kuE%` z>d1w6Ti3*|UjEw62SBBf^l!FC-;|}j{2e)|L_ABb-USWGb8%l|Thsi?RT(|bq3!xzgyA%vZnz`t)o3SD`@Cjh-#F|p$DGCrCv9>CX1eyE|p#% z=wy1do6BtaU?dE?waTX;k+@N+I-*X{TJL49OTEQWuC})#4#Vd{4p7>vDm;NN%s(>X z3Gly%SPFklFs{BO@=U4)Ya#re)uAfl(@WY)?d2}KnfHj2Z#j_}43Cr)0#uRA`y(@V zY9X*c-#leRS6}9Y3hYpfkF(G~fKk-Tsj7`93yJ-i>T`K0 z`rpVEWYZjtSN#5UlDUt$0qi&&!f#So)c9m;$&Tsvx(tUzW}nx@5F0%Kk=hvKW5{o4 zq_uYB43o2jKZOhVv|!4ce6bP;_n$A z^-be7ZIt{Um0?fWs(0=FN2YtCo$52FCG9q0jwGD%)hS5o2VuNUZz0`<4Nc3n+)Je8 z1RvE9rnJ@zq)LlIHcy5gHN;|S8qM%Bk^+k@i+Lx3Qt3U4XJbf& zr96M*FLQbHP7Vr#je-cHX8WUd?icvuS5!$5L6c|T3smmv$qRnr=~h3~IS6a`U0^pg ze)EcG4Gv$Lz*sVZ!aC*ec7;cU?2hV@5`7vo}tuoGNT1=w4{9_w_ z$hX*wBE^sJt^4O>V#=(x6KIy3Oz{$L`E8+#*5pqo3u~aO=vzIEW^D)D+JQG*v2Y|c zJNDO1j-%`!4AxQ;#k8&Gd9p2Gjn3jKtcc|CSGBMu$<6%koVo=69#bJB+J*=3GbCkT zwv@bY1sr5?5I>tyZ{BB1Bz_cNi$+u!2sAG#TU|571>k8`71O<+PlP@4GvZ&zg9o#GTAa zKbn4U@DfZhybO_C92JPt1$5!}7+kn1;nHq-Mz`casPa@{&C6}E9E8&hPTeRj*w z9$?8(h9R@W&5j3Gc=c|dJR#?I;zfomA+8|HY?6rBc2y!aNrL<*M$CQQL@#{!MzY!c z!ZN*%vL0J8-llLe$iOSNBH>`WYLmDvmVn8h&-W6I#4`N+as{o6yIHuN#+S2NP5+jS ziuJ(S^|qW2E!Ju-ItzsB2j9KDnEC3~xVxD;f|n+SVS)8SZUvF@6BM_w_NLGxH58sK ziXt)(_Q)A%+3H0Ze|zesxE>en5payQ(L039u-~U!p_)Ekggu-@yQKE{p;Q#cj`!;iIoZPL{-EU#D>AEp05$Z= zEG1o~b$=4*AT&k-mg@9|*iRZk=4C0yY_t-5yJM4FMu3J&(-qauPc*0Hs)g}N^YT;M zsshq2Q;I7qJ6#of5~@CQTppTK#Xm!98GVWP`wmM6?`hgD^HRBx%kAXFB*`#f(iUj< zbeb>OO{tQ3S@5IBr0OMb7QUt%Lfqt$A_{(n*{V>yf&#xGEx%9K=JRF#iA%^H;c{B9 z(wgU2MY&f}ZwCU5S=-&8gnPAnw$Ywi5p8LM9>#4!g)1uLo}U0W<~DP$DYz#p@>` zjM67%;c!Vi>6y_-W)`6PxW53!xUgmLFY`w3rlv|h=>c>w;S?C*gQ!zUkd&w6F_9r0 zfxn|^e-+D{9-`j7Ag&?Ok*wU@%kG#=O{iU%f|WM~<=n3gLtoY;T{tFaqMh5|Pl=4C zP2Wp+G6;O5p*(;5iHSS5&eUR_qe$Zxa^K?m{KGP45mk38y<;(%iZCmyDI<9` zszvPqcAAw?Bw*f6olhnfaW+2O;rF!+xdRecB=WU(QAZKBtSLstbwkKdUGf4wS}O2B zr7tA{7v6eQH}^z!l#-Q`8=FyFU%AAxCU$&Y5-!WSn0RU(n2IdqQAC5Q>>3-k2_a|8 z1bEvL?4$a9B%~Vgm&OO7vkN0-Bo?!gLIfUjXe6Z-=tEUHgme+4eyYd*%&v9iIh$lK zh5XDqtzvT8RIc&nL}hh0>HB?7&>=M}MqS*jY*clYK^w`ZtYrB0p!44BK!I3f=JQ`X z^#4w5HAJDAYHPAL_+O7V`L70rq+@AQ|zIP8DMP*^^roWJ-Ki^foM8TbJ8AKr}bu6>*Aw)%PGy4hW(_ zpArQasCn6#7^a8SneH7^QY~9BMHEEi*lx98g(rPM!#+!Wavau|(&2Yl8I2;84S^#H z&`Y|(t@3#cYDE|8imE~tq!{V_i9l(Fow|x|utaRyJ7x7lk7E10%c8u524zR^w8crV zOoa^7VTg5q=#{}Fd^fd_b}Wv9vY%6*K(gkLQnO+hG&9$WR8gBF;m}e`_7jUYod zrQ{AP9*D7!$0>hgUi&$cq+ou(A-tG3%|={t)fY)Dphap05mSph>$D~=6ZB$t>DJmj zz{IuC4p)H`I>-~gY+uu!rQy{B7lAYJ%P;Pk;qif>Oe;#E{+!00Uh<(q`q49_fbXR6 zJCG`Dhz~7ZQIuMn-}q<(ZLf+R{;$!_*uZf4O?_fi4y$5#Tdbs@)euA>6u{%;k}xH$ z7Q4WDmbu(Wv}-~816}<{@RQ81uWD68Sk88l;ll`-fq6E*4kFXE=)bg~-NN5%ebz95 zZ(TxDuvPS)LA6|$ia^cppRvqt59AT++?jf}km?D%z|!afgKohrwCAzKnxa=o zBpy=d`8XrRJ)ZPumGL1Avufak)a?R?2Ab0ruUwipU4Pv&`Q9aNhZ#89oo`tbAUAPz zbQPLue<@(-&))z_F&+;BzAw2kSN|A;bfSewJjA827|WQew`0MS<}ZlfC3ikP<$L4D z-TUQlZ&Q5;AT5&0d4P549oM4He&_Bpa$Q3!vx1~ zBmI%K*5_p5U$7vHbokh_v9`X>LoB_;o)_|nKDYsqx}p?7e@XO_#9~j@q;l?bzEL{x z;K$uK)AVlg@b1Vmf!Ok?Z$Zw|4TjG@rX+exHHd<3pSd1n+@;@KUYB^OYz|%U@bypR z`uh+V=PZp5E9PdA9S2Ajsl3fxF(dC{QJRS zzr7vSER4L0M~F*e1HCjCf5{|GG;dm1XPFwS$(A>cRg~TSO(0Us5?pqJKb$)|Z0SYX&RLZV*>EvM0)9%>oR zgOo^eK^&Q{ESf1q0U^*F>{;u^w9_qn1R6f;WQ-8Vfw$36Vx1vi%kr{JH00Jx37n=sIeg=L(Dvcx^s^EmH%S1pz80+4 zpL2Cz>Z?&=5t=;HhV{FdG;4h_Wfg^=5hYRjE+Izh9m$!c%;<$Aj+;W&jJ%D^^D*v? zzY3%84Lda3?QY?f5EV|KnyPP{ znI=b#~7+Y`wvU%uZm{10ZHFJy!1TLPpLdI&>P*NH-*ZQ zx99h^tjY%}cG^vd5!BTy<#rdG>cqwJ^3~k@Q9XN~?UnqvJFP9hymox{RkMY$1|!pj zHcDeQPG;v0fvbC}7>8M%a34PhuDN!E>7ZzlOCy%wr>Knf7LEPETwI-qr=B&v8L6ul zm#W|16`!}vFweo)^^EUp^El;pYMs{JF0EK!U3k<@N%$Z%HtTR0Y=od7tnL28_OmKs zZa?*?*^(<5Fpqrks82W{_^SeKLna2F>yKE}fa0HS3n^UeS{S=RjM75EYy@BB=hxyL zv)2(xO#U+tabc(WyRsk#nV%WW`*u7Dt%(7TM+#}!Eb1xGYqB_e5)bHI9C+s(cg4xI zJD;=Bqsb+aQp-F`_9mBJXZif1m}cpEc5|CDcIOT#A zq0&vG=usRvO}s^I6Wazc_|cVpUsf@`SW81|V~UOZ=wUzo#i#iV2m6bq2B!=ae5qQ| z_2?~w8~jX?Uo68kmpQ`sw(05iQ{_++A^whSr5|cN;~OmWYvlt0UHC}48#YSa=b-iu zv~b}ulbFnBlGh4hC-n^QeZD7)3!b2=$3OzHZe{_PMfqhs1$tkh{sk0Ns$zt(Rdgz6 zd_|-Y7wdrYfLY#OA^PDAJ`L{FSrO5n4)R;k%^Lf6CUGUIvfwn1+>peVP20xQaoNZI zQ6tDlzLRXEO#=?;|a@lfh*AooX5~K z#VqLumOwgc=G!o{-YhmrTL(!|n&jYQ)VplnK}SmNDiM;Xi9{xJBzo#}F>Z9zn=17k zJPMf`s(fW=?ALmgXVldUKam%%m2DC`34EfxCjU>tF-S#bg>q#*FSmiGF*NO%rQOlM)z?l{$GEdb_HN05*{#8Tj?+CI(#o^qHVv zIf8gocJwUOzLP{k%}K(FfU@lGD00t4^1UDEjTk6Hhh9K`k1g1ZnKDBs=oy)iM|7eQ zK$@EO__b174bMji+Huu}dL90D!QuP*kFT}KqlN1;EB{?q(2-fGC61)^`C{+ zY(i^IG?O$*t6D`S;zf0N(lE@E5@X6RoL#KZ{XLE4U!*-imY`aW2HZQzCUJTej?I(4 z)?1yR(h`ZT%gbv|&BiECi_#iF^eMGJlS&f5U&e8$r0y{c=w%MVM9^m~<(=k%Zk5ta&s@PhKqhBdXUqC@igP9x2O4JEaSm@`Fpwq! zWPrwS2E6T@L*S}qPutLSs}uG^(@8!qEt<5|N|_%f503w|z?}3g2|Iy0;oAR*l3D$d zuFkOrz2u1j5E5aTO_(`i_et#G$+AE^TX zyA)Jh*YNa<#)e5AhRVT)+UKzNXvn58lbn95^to-IT6Mo`bshxyJ1B zahd$2-w)mzusZ3E19CX47Mi^G$(HG(!UvwsVREWFl0^13?C^c;h|&g?wBAp}yv{lo z_hXtk9Ls=l%$1vn7<$g zzv+>3Y%BaQKo|-5_z8PR3ML}7eCK=>EpE3{m&Csu7dQKJ#y?*(m#%R;K<&qF!v>uZ zqv$IHX{#8z7;S!EHI$2oDQ9BiW!!w%DD@z=Une<1G=}lD(QkUfb9OF@yRssLC+z+b zG!xg-MVj*4pyttDAM_xjm|)d&w^hP7q55|-yHes_4mU0>K;xf_g~d>QC9gwIe&UEX z>E;m!FahCy-MJ4XdDAh-Mxy=wtpfF|s_IrWN3P(0Z?Skwio%a(_*U9l;T4?l-Z9(>tvjNJc#}qV(TcX}ej=b1hqM-xq);CW5%1 z!olCTcyj?NBJWz!qWmc$9H4V}mNN8D09jf9pn!bVb(kBQK{Nk~rN4%sAt`>)8a0Hca3Utc|$}o!Jg$PGdCYreR&@q|DB*~`iXHD5kP@Vk-;8vr3R3> zL(+nHV-Ea-6n?U&I&%E7=xg3cr9}&bD4Rw_l5k!>E3aYi!()<1Jh(?$qH&@c2!Usj zA%edP#|5J?FceAkT}u%ygah)1BC!bNyl_51j0*O3xD9=Kos*AN6;pw|=*2kV1oSHn zv55g6dl6{S*9Ys=xcaqTqy<{O2N#i-dC=Qr3SEN zzfP>K_yMeDSvoUc1CU{(2ts)30^m>#c#sxr`~Vh_TE@#iSc6e#i65Hr?7kdh^Hwr? zBu>k7tdXp1NK4kotk)Lhe>Xd;1Y7NxXTC)p?pza=*9!tGwJK4i{b<|$iHQeWK}5`4X&iJ zt3#AVQOep#C2r}kG?Ru#x|}DN(ukC!Xy)pbmrwM+J!oxFSq|&tNGcWyvvvVEm@~SL z%Zr?Na#p+qjECcGmMmFZ?O3H`qSr-}BE4F0JG*`y=v}Eh`nk?r@aNP)UXfj8L(sb2 z#C7$?Z>t*Qptzqj`IWHpdXF=U<#Z27;xckJQud9WslqmJn)L&yFvsOGpUwT8t z$Q1Qo8yBFz7dUQa+PT0vSp!t~FG7Kcn5U@7Js*HK^bqfuI`~gqL^dwBP--(kHh`qE z*D4?*y@G{SNE?9fW7}0WK-$W67aXCe1dj)t2vGCUUaVU#>Ne_A9=;!VzmD<3|sk%HR56y|q92FlM{5UL+ zm)P^+{&9L2rtz9m)dZ9YRH?A?gJa`K?O@RGKIEV|>XC(e1f2-!-fh<+DYr}|w=Tu0 zgq%ru1{YJL=hbAM!}CZR{XiKN-B!njxw4OUhS;y(W>(OcBdJYSatsyzm@g@{T^{Q? zqqeAbmpGfv|X z!(6A#gL@r3JpKom#7`l#5(IB+V8ol1}~b-^7#MhXqh^u;wuJ zmt^TecM|YdY&g1%X|uasq~wD7Xty z>!{U;hUeuH>!buTY-Q7nkZU)+3Wf96ZWuz!^!0ZL_T9iFcM&q+Y0ei66P8if#XoXZ zS~UA(`AtFk)G6G1IWEk`#=*KcEa7dPrm0YW2+lqkPN7IpNzwUVAwfD&Lj6P-Wfwg* zb1gAEXv>zl$H8!%@M&Cr9*RWR-CGPZo|j~H0z|p^ zBM%J#lYCYJLx+Lzv`dLc)J?H)g>%Y$(Nx>QWrAsgCHqxK*ehft0g9{C(FW z?MjpSQL0QvSaLzrr%YCUm;(LT>VvUoMV#{9*E&^|4C$JHN6}gybr|x8>&o#`kCIId z^qv)Y(klPni1cEj0sFbajF1CeVD-on$6KjsSG{H!n4=F>PXtqWGVTkCRO8I>Vn+wv z@YUri;s5YjTqgb2RZZlAhL-j-q9w!A+#qh7x~*T$&}h?i=?FhUi4Q>{Iy(8_;jOa@ zm5?Qflnq|^1ZI0nYSB*TD2pUc1KbWFl!uVV*vMFGz8{cuT{q8|Ze1 zOC0l4VHPhz-rZk`0`7&j?bJ5_KQ{-L*FCmz_62H&^nI!tOiMjJ4Ic-8-J*ft#z8nS z5P6}OgfocBw)Zz!Bw;IT=OSxLvPEVGhW`j~*8F@qWwWKBV7l(b$HW{%_IHf*wFd8| z)i$O>{~Kf7uR~t_hOXc}9kfF5%sCD~JxZCVUkBVVTr_oM>a=>4z@tFGN9Gq}i9L0Q zMEl=d&=Bzz{aiUIwS*2w*DjDwLSqMvroTsGj^dWqP`H${`%jt?+rBd|cvG2axoY>!*`8FTx(#EwwGL!HhPkJ=b0)OR26LVgtC#l7Li5vrI~=_dOM~=4 z-frm@`{VYMI*t$L_Si$psRR0&65(|6_{JT!b@XgV-s>0ayV2@A^4 z{To=cPneX^hf+-~u5Etmx76jcCG9hfWBD5bIexZ?z|MNzsU!7IDE+f>P9N0b7&Y3L zD(Bhd--mAU^hPzZ2l=88WxQUQQ%H}1ajBbOZ&rxzB;{Mj7_`KY*fgUsv71H;c(O{y zRcW$e{@55oWr~Z{#f&@t=o@a3=`4V438Un_%<7n0cfHmOiez{b_x_?pO?tNJk>jQ7 zIS^i=1580|HuW>Wbe~tCrD>*#D@Qa?CGSdTv5zVTzHltuB(?2l3KP4poL=dJn-6ld ze{Vl+ma0DXp6PBs?iPB zQ3cRUwIx%rpl8CN`B?1 z`T{Z*dvEjox<5l4-S4FZheLZGc|U!2IsEGAC(L#0Yttedfcs2iQcYyQcWanx>nHt$j|m>Rjv$DfTrGNCQ}24ujr!M!TNo7wiLE$x?6o3#UikdvvyPbY~FDb`|+ zDLc|~ai(pCgKL!aYk&xVtBo9ACN15;-Hiy%@Ny-D+ucg8e&g70DGE@eqM)6CEMS;J+c>Lp`zk6Pk-hVEZ=`q;>%c+s(aM3zrTEw7m%P@eWWERH%K46@<|RN9Vw!CIc|wX7i=!l1ZHf z%`JppOt+8?hql`5UpXPnZ~@yi=hIFR(Qsd+%WvyWxSd$ch>k;LqTTvLD;1$r8tI%^mRoky-L@ zHZ=3qfn$MRT$mfOMPoF*PziB!t4O{^dPTI1LK7`cY=_fl|Ut8mgkuk`(NK3Kf|zXU;F zm9&OD#Vi=$=-8rzj5H)Ts``fa*v@I9Ax^5+!=U~U+*D1NrwV{z=M0h!{8AvXpyCEXT#);grV;X@ zyNgb$#pmf!NeWiuQa-ep3Li-+Yon=RZj5)31cQ8x`Fp0w)Xgf&#!c1#BQ6yfj0+I3{Vbh#}iR(9El;LO>FE z)ShM?9)bee(Xo&`sIU|xglL0JAh#9+WaKQ5Ab#Q*ef@~)MI9qJhr&!ILokR>7Fdo2 zxa{p_RBcGCzAs9;{rUWwX38q5RhEgA=#^bFQaL_RDpj})%MkMXapo4@OeWZRm@>Nk zA{=Qu52W~NI3}TzQ^j!U=EPXz&5J$_Q*)-54WCug;FQtR@JvYXvOZk~YDA-- zE*h)EaL!IySRcV^4ypZQWpn9?a)E14KouZn9oeuyHN}E&$|prDz3WXi=7(EG8sQd_ zS#W3aat82uui%Qnl?iLFL@*`T=L|*vNkwX{PL+*x2~*YsZ(O7l<}p%5(1=U9pojvb zA?PLAm@e1|yRh`55%9ae!!cexhFq}M#7A?#OAhT46cd}OGXkYO2Z<*J4Kuw8=j8^I zQiwt)0xcscH^<~KYxHmeB?2tD+0+vZ4!w?32^1mN@}G|2#&-xp`Z2~BI3${Z_%?%o zqTesLLKe6~^KD?rOVxJ^K$=#2&f;dJ;;S|f#}mpp5lT0uIkCgPwKiP<$fr|`Y04*v z(Ao~$05Bl>M1%%ng+Z;0uEA|-i-r{HOw3Q>gxv$*I6X%fD|3YsXTAYiE6_HGf`Wx~ z2m~wo5sQdW4 z@CX3mlrkoBtPD{xSR&}g_uM8uMVaNDCuP-XJoJR;co^TO5ES{4L<*W4R-%lnDbFgB zq37Y?1AwdG^&RKY&3%JbS>e4)J(CqNb+jPig#Z~Qcoy$^G5YmSf>s>u3r%_In3JG- zS$q7>ECo|bkD)GEW0VBQxRDU$V|NRm3*~i-HWgxuaQth-;ih@d02E-yDD1J z4y8uc?3F*P0}zz1@HW8uu@v~I^)G7F#yl^d;3dEwan+m!lj4B%2pPd0kpW*OPStB4 zYb}B_Q$U~SEL_U8k$EHVB$YgmK_>_h(@I`A(wCb=foTS7CBTJv<_Ihsrz@}l27RPi&#by#n8F6IX98x1G` z3KlIh?wb~j;f3AJ)^Iq?f}u=k2(0}P9T`Lss)%tQBZTY%79=J_`loHNJKPzJ+R3Ut zD2|sR!;>T5w_OnpxSH*o)^MCK*`ZaG*sX-pwH?m9Tdy|l%6N$tj@aqlx=EB`3~P-Q zYYO0-s)xgv$8_yk&XgGz8pX*`kw{imP34RFMHOl7uLzN*$jKzRqF~mbF$qEPxp`5< zXF5PHWWY3Yjh>bLA9CIO^mffo9Y>wU4TkWu7krUNWN`so<}K7Xd2NY3Tj1D|%r|%7 ztHKJM4EW~hj%K~9e%leyeLX|x-C#ThKB4TiSV$QbA-yEbgYWKT zbz>@J6&hd-s}l^oCzqb@vvDw*cu$IiI)NNdL>F%fShy3Xfs#60MSveLDUv)Q1hMi+ zR(8RHV+c?_9#MX?a*-`E$%s%*E+mWy3~{F}N--dP&;pyIP#>W?sdjkDr6VCy9S~=k zKECdBGu&Dfb5C_(ML2}#R5&dKc^x%u4hkf{4_V~hk8i7+r4!rJHg&jU8J;p|B1>GEhu0A0dV@l~q$zWA zG#@`VFT!889tn6%>dg5Xn|j6>r|zm{nM3zPj2~ql2LrfVOsr{=lvP-NO2AODBPSI! zgVo$bm=g)!HOm&-dS*wJ8oqvBr_rlztm1H0vL*^Os&PQwMF?^_56apEQ;l0N3n`ja zLzUnPPMc>sAg=<5$5!H|JDIK|QbKfquxD~b4gkRb3Ewn{5%Cs8l)l0jxSd1>P`?2m zZPSXD(7;GoMBKD@E$x_msh&<4_lW8gdCYW0Yfig*I zub1hP25d|CL{)&$eM`sMrdn{o9-OvhNg~`1dqw(lEs8G8CC=;RuwVR?i#y+SE7g!F zfs`Pk+Je=uTx1`SlbntW*DMz9;wM^&V*)WUO)hZCIw>h)wx`Un+*^PiH>_$kp2P?S z+9i7=AAK{i6cb;-ML7*lwGqb(IF;=+ffDb1u_0FUSZl_K^-NYwTwQrD+qTNXFfvW% zssXgH4SA(<4HSq$BHkd5XsLg02fqV9L-!ddu*0K@l1e-040xa_FCyDIodPrx61eEt z6qr(pP|QDrpZhT2nFg2!Eu4NY^d`zR9fKjD8)vdv8+qRe#LEdjoJ{?HOzYz)>JO-m~$|RyfK*(8& z8M;XWQ5PVk(SsEVMJkdmYBgbWV@DW}HP&Qc^iiFW43W@-#@TWMstz8t-FDe-LwJrV zi>@(|ig-ru(POv=QIoyk3u3Sj?V1VVCLx!A{JWA6f${oIDN3{w8+i7FH;2 zwpCcT1#1VWTnY!v3N}ys%{JhtuH0p9Va8*ct4YsV-l5VV66Mp;w&_LTZ|{O(6ATJ= zopS{ud;B=}=H@taMsHi9j-xQhs^)L12+MkW(5W53_G~9QaVm|o)PkO#@cGn`Rl=)? zWjyAr*d18;gJY`QywtwUS+t5Nvh2Z+J{m}#V4)4;pSm)@s}0#=7RHxri)?4%T+ory zh(JhEqt8^$Bp!s3G4r#@FuF3V2@OI>j8-eUgZi|?_2~>%Q(9o0nSe>5b0R|bKxR!o z*n+Z8o~eY9`5?WgKIp$Vn54>jYF+0iA$D=txuXYKW))Mr=Q6WcHZLoxl~V)83gDSz zYYgF%{*pSmvjy!}0sv=7VREtHp&u#doOr?!n_P$1-#PP0* z*C=Nt)|G#Tx13g+devX~lQXu}Fy32mOL&6~tz$=%CbY z;IA!IiRt#ZMNBho0x?G)PHa;vXG>TT$m4_b# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-Italic-webfont.woff b/jsdoc/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 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 literal 0 HcmV?d00001 diff --git a/jsdoc/fonts/OpenSans-Light-webfont.svg b/jsdoc/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/jsdoc/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-Light-webfont.woff b/jsdoc/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/jsdoc/fonts/OpenSans-LightItalic-webfont.eot b/jsdoc/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/jsdoc/fonts/OpenSans-LightItalic-webfont.svg b/jsdoc/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/jsdoc/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-LightItalic-webfont.woff b/jsdoc/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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
+ +

Global

+ + + + + + +
+ +
+ +

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + + + + +

all(start, end, city) → {Object}

+ + + + + +
+ Return all reports within a defined time period, and optionally city +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
start + + +integer + + + + Timestamp as ISO 8601 string for start of window
end + + +string + + + + Timestamp as ISO 8601 string for end of window
city + + +string + + + + Optional, instance region code (e.g. 'jbd')\
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ Query result +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:db(config, logger) → {Object}

+ + + + + +
+ Database interaction for Cards objects +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + configuration
logger + + +Object + + + + logger
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ db - PG Promise database +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:server(config, initializeDb, routes, logger) → {Object}

+ + + + + +
+ Core server object +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + configuration
initializeDb + + +Object + + + + database initialization
routes + + +Object + + + + routes
logger + + +Object + + + + logger
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ - Express server application +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:src/api/cards/index(config, db, logger) → {Object}

+ + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for cards route +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:src/api/cards/model(config, db, logger) → {Object}

+ + + + + +
+ Database interaction for Cards objects +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ data Query results +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:src/api/index(config, db, logger) → {Object}

+ + + + + +
+ Build CogniCity Server Data API +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for API routes +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:src/api/reports/index(config, db, logger) → {Object}

+ + + + + +
+ Methods to get current flood reports from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for reports route +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:src/api/reports/model(config, db, logger) → {Object}

+ + + + + +
+ Methods to get current flood reports from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ Query results +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + +

module:test/testCAP(logger)

+ + + + + +
+ Test CAP data format utility +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
logger + + +Object + + + + CogniCity Server logger object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testCards(app)

+ + + + + +
+ Test cards endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testCities(app)

+ + + + + +
+ Test cities endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testDB()

+ + + + + +
+ Test db utility module +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testFeeds(app)

+ + + + + +
+ Test feeds endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testFloods(app, jwt)

+ + + + + +
+ Test infrastructure endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
jwt + + +Object + + + + Sample JSON Web Token for testing endpoint auth
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testFloodsgauges(app)

+ + + + + +
+ Test floodgauges endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testInfrastructure(app)

+ + + + + +
+ Test infrastructure endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testReports(app, reportid)

+ + + + + +
+ Test reports endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
reportid + + +Number + + + + CogniCity report ID to test against
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testReportsArchive(app)

+ + + + + +
+ Test reports archive endpoint +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

module:test/testServer(app)

+ + + + + +
+ Test top-level server routes +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
app + + +Object + + + + CogniCity server app object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/index.html b/jsdoc/index.html new file mode 100644 index 0000000..508bad2 --- /dev/null +++ b/jsdoc/index.html @@ -0,0 +1,466 @@ + + + + + JSDoc: Home + + + + + + + + + + +
+ +

Home

+ + + + + + + + +

+ + + + + + + + + + + + + + + +
+

cognicity-server

API Server for CogniCity

+

Build Status

+

Coverage Status

+

DOI for current stable release v3.0.0

+

DOI

+

Summary

This is the NodeJS server which runs the CogniCity Data API used by Urban Risk Map instances, such as PetaBencana.id site.

+

Run

ES6 Support is provided by Babel.

+
    +
  1. Install requirements from the provided package.json by doing npm install.

    +
  2. +
  3. Copy the sample.env file to a local .env and fill-in the required parameters. This local file will be ignored by Git and so should be secret safe. Further details on configuration are described below.

    +
  4. +
  5. To run a local development instance of the server do npm run dev

    +
  6. +
+

Configuration

Server configuration parameters are stored in a configuration file which is parsed by index.js on startup. Local configuration parameters are imported from the .env into src/config.js. See config.js for full details example configuration. Any variable not defined in .env will pickup the default value below (also see config.js)—note that local environment variables will override both .env and config.js. The following environment variables are currently supported by the configurtion:

+
    +
  • APP_NAME: Name of the application (default: cognicity-server)
  • +
  • API_FEEDS_QLUE_CITIES: Names of cities used by the Qlue data feed
  • +
  • API_FEEDS_QLUE_DISASTER_TYPES: Names of disaster types used by the Qlue data feed
  • +
  • API_FEEDS_DETIK_DISASTER_TYPES: Names of disaster types used by the Detik data feed
  • +
  • API_REPORTS_TIME_WINDOW: Time window for report data queries (default 1 hour)
  • +
  • API_REPORTS_TIME_WINDOW_MAX: Maximum limit for time window (default 1 week)
  • +
  • API_REPORTS_LIMIT: Total maximum number of reports to return in a single request
  • +
  • API_FLOODGAUGE_REPORTS_TIME_WINDOW: Time window for flood data (normally 12 hours)
  • +
  • API_FLOODGAUGE_REPORTS_TIME_WINDOW: Total maximum number of flood gauge records to return in a single request
  • +
  • AUTH0_AUDIENCE: Data API to be authenticated
  • +
  • AUTH0_CLIENT_ID: Auth0 client ID (NOTE: this is mandatory and no default value)
  • +
  • AUTH0_ISSUER: Web address of Auth0 instance
  • +
  • AWS_REGION: Region for AWS Infrastructure
  • +
  • AWS_S3_ACCESS_KEY_ID: Access key ID for AWS S3 bucket
  • +
  • AWS_S3_SECRET_ACCESS_KEY: Access key secret for AWS S3 bucket
  • +
  • AWS_S3_SIGNATURE_VERSION: Version of AWS S3 signature to use
  • +
  • AUTH0_SECRET: Auth0 secret (NOTE: this is mandatory and no default value)
  • +
  • BODY_LIMIT: Maximum body size POST/PUT/PATCH (default: 100kb)
  • +
  • CACHE: Should caching be enabled? (default: false)
  • +
  • CACHE_DURATION_CARDS: How long should cards be cached for? (default: '1 minute')
  • +
  • CACHE_DURATION_FLOODS: How long should floods be cached for? (default: '1 hour')
  • +
  • CACHE_DURATION_FLOODS_STATES: How long should flood states be cached for? (default: '1 hour')
  • +
  • CACHE_DURATION_INFRASTRUCTURE: How long should infrastructure be cached for? (default: '1 hour')
  • +
  • COMPRESS: Should the server gzip compress results? Only works if CACHE is disabled. (default: false)
  • +
  • CORS: Should Cross Object Resource Sharing (CORS) be enabled (default: false)
  • +
  • CORS_HEADERS: CORS headers to use (default: [Link])
  • +
  • DISASTER_TYPES: Disaster type keywords for report classification (default: flood,prep)
  • +
  • FORMAT_DEFAULT: Which format to return results in by default (default: json)
  • +
  • FORMATS: Formats supported by the system (as comma separated list) (default: json,xml)
  • +
  • GEO_FORMAT_DEFAULT: Which format to return geographic results in by default (default: topojson)
  • +
  • GEO_FORMATS: Geographic formats supported by the system (as comma separated list) (default: topojson,geojson,cap)
  • +
  • GEO_PRECISION: Precision to use when rounding geographic coordinates (default: 10)
  • +
  • IMAGE_BUCKET: AWS S3 bucket for image uploads (default: testing-riskmap-image-uploads)
  • +
  • IMAGES_HOST: Endpoint for image hosting (default: images.petabencana.id),
  • +
  • INFRASTRUCTURE_TYPES: Infrastructure types supported (as comma separated list) (default: floodgates,pumps,waterways)
  • +
  • LANGUAGES: Supported languages
  • +
  • LOG_CONSOLE: In development mode we log to the console by default, in other environments this must be enabled if required by setting this parameter to true (default: false)
  • +
  • LOG_DIR: Which directory should logs be written to. If blank, not supplied or the directory is not writable by the application this will default to the current directory
  • +
  • LOG_JSON: Should json format be used for logging (default: false)
  • +
  • LOG_LEVEL: What level to log at. Levels are: silly, debug, verbose, info, warn, error. debug level is recommended for development. (default: error)
  • +
  • LOG_MAX_FILE_SIZE: Maximum size of log file in bytes before rotating (default: 1024 * 1024 * 100 i.e. 100mb)
  • +
  • LOG_MAX_FILES: Maximum number of log files before rotation (default: 10)
  • +
  • NODE_ENV: Which environment are we in. Environments are: development, test, staging, production (default: development)
  • +
  • PGHOST: Postgres DB hostname (default: 127.0.0.1)
  • +
  • PGDATABASE: Postgres DB database name (default: cognicity)
  • +
  • PGPASSWORD: Postgres DB password (default: p@ssw0rd)
  • +
  • PGPORT: Postgres DB port (default: 5432)
  • +
  • PGSSL: SSL enabled on Postgres DB connection? (default: false)
  • +
  • PGTIMEOUT: Max duration on DB calls before timeout (in milliseconds) (default: 5000 i.e. 5 seconds)
  • +
  • PGUSER: Postgres DB username (default: postgres)
  • +
  • PORT: Which port should the application run on (default: 8001)
  • +
  • REGION_CODES: Which region codes are supported (as comma separated list) (default: jbd,bdg,sby)
  • +
  • REPORT_TYPES: Classifiers for report types (default: drain,desilting,canalrepair,treeclearing,flood)
  • +
  • RESPONSE_TIME: Should the server return an X-Response-Time header detailing the time taken to process the request. This is useful for both development to identify latency impact on testing and production for performance / health monitoring (default: false)
  • +
  • SECURE_AUTH0: Whether Auth0 JWT token security should be applied to secure routes (default: false)
  • +
  • TABLE_FLOODGAUGE_REPORTS: Postgres table name for flood-gauge reports
  • +
  • TABLE_FEEDS_QLUE: Postgres table name for Qlue feed
  • +
  • TABLE_FEEDS_DETIK: Postgres table name for Detik feed
  • +
  • TABLE_GRASP_CARDS: Postgres table name for Grasp Cards
  • +
  • TABLE_GRASP_LOG: Postgres table name for Grasp activity
  • +
  • TABLE_GRASP_REPORTS: Postgres table name for Grasp reports
  • +
  • TABLE_INSTANCE_REGIONS: Postgres table for operating regions
  • +
  • TABLE_LOCAL_AREAS: Postgres table for local areas data for each operating region
  • +
  • TABLE_REM_STATUS: Postgres table for current flood states from REM
  • +
  • TABLE_REM_STATUS_LOG: Postgres table for REM log
  • +
  • TABLE_REPORTS: Postgres table for reports
  • +
+

A few points to note on config:

+
    +
  • AWS Beanstalk: If you're deploying to AWS Elastic Beanstalk you may need to configure environment variables for each of the above under Configuration -> Software configuration.
  • +
+

Building

Run npm run -s build to build.

+

Testing

Testing is run by Travis. 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. 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.

+

To run tests locally a new database "cognicity_server_testing" is required on localhost.

+

Code follows the Google JavaScript style.ESLint is run with tests to enfoce style.

+

Issue Tracking

Issues are tracked using GitHub

+

Release

The release procedure is as follows:

+
    +
  • Update the CHANGELOG.md file with the newly released version, date, and a high-level overview of changes. Commit the change.
  • +
  • Create a tag in git from the current head of master. The tag version should be the same as the version specified in the package.json file - this is the release version.
  • +
  • Update the version in the package.json file and commit the change.
  • +
  • Further development is now on the updated version number until the release process begins again.
  • +
+

API Notes

Full API documentation at https://docs.petabencana.id. This documentation is stored in the petabencana-docs repository.

+
    +
  • The dbgeo library expects timestamps from database to be in UTC (i.e. not a local timezone)
  • +
+

License

See LICENSE.md

+
+ + + + + + + + + +
+ +
+ +

config.js

+ + +
+ +
+
+ + +
config
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

index.js

+ + +
+ +
+
+ + +
Run CogniCity Data Server
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
Author:
+
+
    +
  • Urban Risk Lab, 2017
  • +
+
+ + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

lib/util.js

+ + +
+ +
+
+ + +
CogniCity Server Utility file
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/index.js.html b/jsdoc/index.js.html new file mode 100644 index 0000000..6b1f5ce --- /dev/null +++ b/jsdoc/index.js.html @@ -0,0 +1,148 @@ + + + + + JSDoc: Source: index.js + + + + + + + + + + +
+ +

Source: index.js

+ + + + + + +
+
+
/**
+ * CogniCity Data Server
+ * @file Run CogniCity Data Server
+ * @author Urban Risk Lab, 2017
+ **/
+// Import express, fs and http
+import fs from 'fs';
+import path from 'path';
+
+// Import config
+import config from './config';
+
+// Import DB initializer
+import initializeDb from './db';
+
+// Import the routes
+import routes from './api';
+
+// Import server
+import {init} from './server.js';
+
+// Import logging libraries
+import logger from 'winston'; // Application logging
+
+// Set the default logging level
+logger.level = config.LOG_LEVEL;
+
+// Check that log file directory can be written to
+try {
+	if (config.LOG_DIR !== '') {
+		fs.accessSync(config.LOG_DIR, fs.W_OK);
+	}
+	logger.info(`Logging to ${config.LOG_DIR !== '' ? config.LOG_DIR :
+							'current working directory' }`);
+} catch (e) {
+	// If we cannot write to the desired directory then log tocurrent directory
+	logger.info(`Cannot log to '${config.LOG_DIR}',
+							logging to current working directory instead`);
+	config.LOG_DIR = '';
+}
+
+// Configure the logger
+logger.add(logger.transports.File, {
+	filename: path.join(config.LOG_DIR, `${config.APP_NAME}.log`),
+	json: config.LOG_JSON, // Log in json or plain text
+	maxsize: config.LOG_MAX_FILE_SIZE, // Max size of each file
+	maxFiles: config.LOG_MAX_FILES, // Max number of files
+	level: config.LOG_LEVEL, // Level of log messages
+});
+
+// If we are not in development and console logging not requested then remove it
+if (config.NODE_ENV !== 'development' && !config.LOG_CONSOLE) {
+	logger.remove(logger.transports.Console);
+}
+
+// If we exit immediately winston does not get chance to write last log message
+const exitWithStatus = (status) => {
+	logger.info(`Exiting with status ${status}`);
+	setTimeout(() => process.exit(status), 500);
+};
+
+// Catch kill and interrupt signals and log a clean exit status
+process
+	.on('SIGTERM', () => {
+		logger.info('SIGTERM: Application shutting down');
+		exitWithStatus(0);
+	})
+	.on('SIGINT', () => {
+		logger.info('SIGINT: Application shutting down');
+		exitWithStatus(0);
+	});
+
+// Try and start the server
+init(config, initializeDb, routes, logger).then((app) => {
+	// All good to go, start listening for requests
+	app.server.listen(config.PORT);
+	logger.info(`Application started,`
+		+ `listening on port ${app.server.address().port}`);
+}).catch((err) => {
+	// Error has occurred, log and shutdown
+	logger.error('Error starting server: ' + err.message + ', ' + err.stack);
+	logger.error('Fatal error: Application shutting down');
+	exitWithStatus(1);
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/lib_cap.js.html b/jsdoc/lib_cap.js.html new file mode 100644 index 0000000..0387eb8 --- /dev/null +++ b/jsdoc/lib_cap.js.html @@ -0,0 +1,307 @@ + + + + + JSDoc: Source: lib/cap.js + + + + + + + + + + +
+ +

Source: lib/cap.js

+ + + + + + +
+
+
'use strict';
+/**
+ * CogniCity CAP data format utility
+ * @module lib/cap
+ * @param {Object} logger Configured Winston logger instance
+ **/
+
+// XML builder used to create XML output
+import builder from 'xmlbuilder';
+// moment module, JS date/time manipulation library
+import moment from 'moment-timezone';
+// Cap class
+module.exports = class Cap {
+  /**
+   * Setup the CAP object to user specified logger
+   * @alias module:lib/cap
+   * @param {Object} logger Configured Winston logger instance
+   */
+  constructor(logger) {
+    this.logger = logger;
+  }
+  /**
+   * Transform GeoJSON data to ATOM feed of CAP format XML data.
+   * See {@link https://tools.ietf.org/html/rfc4287|ATOM syndication format}
+   * @param {Object} features Peta Jakarta GeoJSON features object
+   * @return {String} XML CAP data describing all areas
+   **/
+  geoJsonToAtomCap(features) {
+    let self = this;
+    let feed = {
+      '@xmlns': 'http://www.w3.org/2005/Atom',
+      'id': 'https://data.petabencana.id/floods',
+      'title': 'petabencana.id Flood Affected Areas',
+      'updated': moment().tz('Asia/Jakarta').format(),
+      'author': {
+        name: 'petabencana.id',
+        uri: 'https://petabencana.id/',
+      },
+    };
+
+    for (let feature of features) {
+      let alert = self.createAlert( feature );
+      // If alert creation failed, don't create the entry
+      if (!alert) {
+        continue;
+      }
+
+      if (!feed.entry) feed.entry = [];
+
+      feed.entry.push({
+        // Note, this ID does not resolve to a real resource
+        // - but enough information is contained in the URL
+        // that we could resolve the flooded report at the same point in time
+        id: 'https://data.petabencana.id/floods?parent_name='
+        +encodeURI(feature.properties.parent_name)
+        +'&area_name='
+        +encodeURI(feature.properties.area_name)
+        +'&time='
+        +encodeURI(moment.tz(feature.properties.last_updated, 'Asia/Jakarta'
+                    ).format('YYYY-MM-DDTHH:mm:ssZ')),
+        title: alert.identifier + ' Flood Affected Area',
+        updated: moment.tz(feature.properties.last_updated, 'Asia/Jakarta'
+                    ).format('YYYY-MM-DDTHH:mm:ssZ'),
+        content: {
+          '@type': 'text/xml',
+          'alert': alert,
+        },
+      });
+    }
+
+    return builder.create( {feed: feed} ).end();
+  }
+
+  /**
+   * Create CAP ALERT object.
+   * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/`
+                  + `CAP-v1.2-os.html#_Toc97699527|`
+                  + `CAP specification 3.2.1 "alert" Element and Sub-elements`}
+   * @param {Object} feature petabencana.id GeoJSON feature
+   * @return {Object} Object representing ALERT element for xmlbuilder
+   */
+  createAlert(feature) {
+    let self = this;
+
+    let alert = {};
+
+    alert['@xmlns'] = 'urn:oasis:names:tc:emergency:cap:1.2';
+
+    let identifier = feature.properties.parent_name + '.'
+      + feature.properties.area_name + '.'
+      + moment.tz(feature.properties.last_updated, 'Asia/Jakarta'
+        ).format('YYYY-MM-DDTHH:mm:ssZ');
+    identifier = identifier.replace(/ /g, '_');
+    alert.identifier = encodeURI(identifier);
+
+    alert.sender = 'BPBD.JAKARTA.GOV.ID';
+    alert.sent = moment.tz(feature.properties.last_updated, 'Asia/Jakarta'
+                  ).format('YYYY-MM-DDTHH:mm:ssZ');
+    alert.status = 'Actual';
+    alert.msgType = 'Alert';
+    alert.scope = 'Public';
+
+    alert.info = self.createInfo( feature );
+    // If info creation failed, don't create the alert
+    if (!alert.info) {
+      return;
+    }
+
+    return alert;
+  }
+
+  /**
+   * Create a CAP INFO object.
+   * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/`
+                  + `CAP-v1.2-os.html#_Toc97699542|`
+                  + `CAP specification 3.2.2 "info" Element and Sub-elements`}
+   * @param {Object} feature petabencana.id GeoJSON feature
+   * @return {Object} Object representing INFO element suitable for xmlbuilder
+   */
+  createInfo(feature) {
+    let self = this;
+
+    let info = {};
+
+    info.category = 'Met';
+    info.event = 'FLOODING';
+    info.urgency = 'Immediate';
+
+    let severity = '';
+    let levelDescription = '';
+    if ( feature.properties.state === 1 ) {
+      severity = 'Unknown';
+      levelDescription = 'AN UNKNOWN LEVEL OF FLOODING - USE CAUTION -';
+    } else if ( feature.properties.state === 2 ) {
+      severity = 'Minor';
+      levelDescription = 'FLOODING OF BETWEEN 10 and 70 CENTIMETERS';
+    } else if ( feature.properties.state === 3 ) {
+      severity = 'Moderate';
+      levelDescription = 'FLOODING OF BETWEEN 71 and 150 CENTIMETERS';
+    } else if ( feature.properties.state === 4 ) {
+      severity = 'Severe';
+      levelDescription = 'FLOODING OF OVER 150 CENTIMETERS';
+    } else {
+      self.logger.silly('Cap: createInfo(): State '
+        + feature.properties.state
+        + ' cannot be resolved to a severity');
+      return;
+    }
+    info.severity = severity;
+
+    info.certainty = 'Observed';
+    info.senderName = 'JAKARTA EMERGENCY MANAGEMENT AGENCY';
+    info.headline = 'FLOOD WARNING';
+
+    let descriptionTime = moment(feature.properties.last_updated
+                            ).tz('Asia/Jakarta').format('HH:mm z');
+    let descriptionArea = feature.properties.parent_name
+                          + ', ' + feature.properties.area_name;
+    info.description = 'AT '
+                        + descriptionTime
+                        + ' THE JAKARTA EMERGENCY MANAGEMENT AGENCY OBSERVED '
+                        + levelDescription + ' IN ' + descriptionArea + '.';
+
+    info.web = 'https://petabencana.id/';
+
+    info.area = self.createArea( feature );
+    // If area creation failed, don't create the info
+    if (!info.area) {
+      return;
+    }
+
+    return info;
+  }
+
+  /**
+   * Create a CAP AREA object.
+   * See {@link `http://docs.oasis-open.org/emergency/cap/v1.2/`
+                + `CAP-v1.2-os.html#_Toc97699550|`
+                + `CAP specification 3.2.4 "area" Element and Sub-elements`}
+   * @param {Object} feature petabencana.id GeoJSON feature
+   * @return {Object} Object representing AREA element for XML xmlbuilder
+   */
+  createArea(feature) {
+    let self = this;
+
+    let area = {};
+
+    area.areaDesc = feature.properties.area_name
+                    + ', ' + feature.properties.parent_name;
+
+    // Collate array of polygon-describing strings from different geometry types
+    area.polygon = [];
+    let featurePolygons;
+    if ( feature.geometry.type === 'Polygon' ) {
+      featurePolygons = [feature.geometry.coordinates];
+    } else if ( feature.geometry.type === 'MultiPolygon' ) {
+      featurePolygons = feature.geometry.coordinates;
+    } else {
+      /* istanbul ignore next */
+      self.logger.error( 'Cap: createInfo(): Geometry type \''
+                          + feature.geometry.type + '\' not supported' );
+      /* istanbul ignore next */
+      return;
+    }
+
+    // Construct CAP suitable polygon strings
+    // (whitespace-delimited WGS84 coordinate pairs - e.g. "lat,lon lat,lon")
+    // See: `http://docs.oasis-open.org/emergency/cap/v1.2/`
+    //          + `CAP-v1.2-os.html#_Toc97699550 - polygon`
+    // See: `http://docs.oasis-open.org/emergency/cap/v1.2/`
+    //          + `CAP-v1.2-os.html#_Toc520973440`
+    self.logger.debug( 'Cap: createInfo(): '
+                        + featurePolygons.length
+                        + ' polygons detected for '
+                        + area.areaDesc );
+    for (let polygonIndex=0; polygonIndex < featurePolygons.length;
+      polygonIndex++) {
+      // Assume all geometries to be simple Polygons of single LineString
+      if ( featurePolygons[polygonIndex].length > 1 ) {
+        /* istanbul ignore next */
+        self.logger.error( `Cap: createInfo(): Polygon with interior rings is
+                            not supported` );
+        /* istanbul ignore next */
+        return;
+      }
+
+      let polygon = '';
+      self.logger.debug( 'Cap: createInfo(): '
+                        + featurePolygons[polygonIndex][0].length
+                        + ' points detected in polygon '
+                        + polygonIndex );
+      for (let pointIndex=0; pointIndex <
+        featurePolygons[polygonIndex][0].length; pointIndex++) {
+          let point = featurePolygons[polygonIndex][0][pointIndex];
+          polygon += point[1] + ',' + point[0] + ' ';
+        }
+      area.polygon.push( polygon );
+    }
+
+    return area;
+  }
+
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/lib_util.js.html b/jsdoc/lib_util.js.html new file mode 100644 index 0000000..b7831ed --- /dev/null +++ b/jsdoc/lib_util.js.html @@ -0,0 +1,151 @@ + + + + + JSDoc: Source: lib/util.js + + + + + + + + + + +
+ +

Source: lib/util.js

+ + + + + + +
+
+
/**
+ * Server utility functions
+ * @file CogniCity Server Utility file
+ **/
+
+// Import dependencies
+import Promise from 'bluebird';
+import jwt from 'express-jwt';
+// import jwks from 'jwks-rsa'; // See TODO below regarding Auth0 mechanism
+import dbgeo from 'dbgeo';
+
+// Import config
+import config from '../config';
+
+// Caching
+import apicache from 'apicache';
+apicache.options({debug: config.LOG_LEVEL === 'debug',
+                  statusCodes: {include: [200]}});
+let cache = apicache.middleware;
+
+// Cache response if enabled
+const cacheResponse = (duration) => cache(duration, config.CACHE);
+
+// Configure our JWT checker
+const jwtCheck = jwt({
+  secret: new Buffer(config.AUTH0_SECRET),
+  audience: config.AUTH0_CLIENT_ID,
+});
+// TODO: Move to single auth0 mechanism once they support SPA auth using API
+/* const jwtCheck = jwt({
+  credentialsRequired: config.SECURE_AUTH0,
+  secret: jwks.expressJwtSecret({
+      cache: true,
+      rateLimit: true,
+      jwksRequestsPerMinute: 5,
+      jwksUri: `${config.AUTH0_ISSUER}/.well-known/jwks.json`
+  }),
+  audience: config.AUTH0_AUDIENCE,
+  issuer: config.AUTH0_ISSUER,
+  algorithms: ['RS256']
+});*/
+
+// Setup dbgeo
+dbgeo.defaults = {
+  outputFormat: config.GEO_FORMAT_DEFAULT,
+  geometryColumn: 'the_geom',
+  geometryType: 'wkb',
+  precision: config.GEO_PRECISION,
+};
+
+// Format the geographic response with the required geo format
+const formatGeo = (body, outputFormat) => new Promise((resolve, reject) => {
+  // Check that body is an array, required by dbgeo.parse
+  if (Object.prototype.toString.call( body ) !== '[object Array]') {
+    body = [body]; // Force to array
+  }
+	dbgeo.parse(body, {outputFormat}, (err, formatted) => {
+		if (err) reject(err);
+		resolve(formatted);
+	});
+});
+
+// Handle a geo response, send back a correctly formatted json object with
+// status 200 or not found 404, catch and forward any errors in the process
+const handleGeoResponse = (data, req, res, next) => {
+  return !data ?
+    res.status(404).json({statusCode: 404, found: false, result: null}) :
+      formatGeo(data, req.query.geoformat)
+        .then((formatted) => res.status(200).json({statusCode: 200,
+          result: formatted}))
+        /* istanbul ignore next */
+        .catch((err) => {
+          /* istanbul ignore next */
+          next(err);
+        });
+};
+
+// Handle a regular response, send back result or 404
+const handleResponse = (data, req, res) => {
+  return !data ?
+    res.status(404).json({statusCode: 404, found: false, result: null}) :
+    res.status(200).json({statusCode: 200, result: data});
+};
+
+module.exports = {
+  cacheResponse, formatGeo, handleResponse, handleGeoResponse, jwtCheck,
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/module-lib_cap.html b/jsdoc/module-lib_cap.html new file mode 100644 index 0000000..40f026d --- /dev/null +++ b/jsdoc/module-lib_cap.html @@ -0,0 +1,224 @@ + + + + + JSDoc: Class: module:lib/cap + + + + + + + + + + +
+ +

Class: module:lib/cap

+ + + + + + +
+ +
+ +

module:lib/cap

+ + +
+ +
+
+ + + + + +

new module:lib/cap(logger)

+ + + + + +
+ Setup the CAP object to user specified logger +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_cards_index.html b/jsdoc/module-src_api_cards_index.html new file mode 100644 index 0000000..764b114 --- /dev/null +++ b/jsdoc/module-src_api_cards_index.html @@ -0,0 +1,172 @@ + + + + + JSDoc: Module: src/api/cards/index + + + + + + + + + + +
+ +

Module: src/api/cards/index

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +
CogniCity Server /cards endpoint
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_cards_model.html b/jsdoc/module-src_api_cards_model.html new file mode 100644 index 0000000..1faf6e7 --- /dev/null +++ b/jsdoc/module-src_api_cards_model.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/cards/model + + + + + + + + + + +
+ +

Module: src/api/cards/model

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server /cards data model
+ + + + + + + +

(require("src/api/cards/model"))(config, db, logger) → {Object}

+ + + + + +
+ Database interaction for Cards objects +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ data Query results +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_index.html b/jsdoc/module-src_api_index.html new file mode 100644 index 0000000..91706bf --- /dev/null +++ b/jsdoc/module-src_api_index.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/index + + + + + + + + + + +
+ +

Module: src/api/index

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server Data API
+ + + + + + + +

(require("src/api/index"))(config, db, logger) → {Object}

+ + + + + +
+ Build CogniCity Server Data API +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for API routes +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_reports_index.html b/jsdoc/module-src_api_reports_index.html new file mode 100644 index 0000000..6a07109 --- /dev/null +++ b/jsdoc/module-src_api_reports_index.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/reports/index + + + + + + + + + + +
+ +

Module: src/api/reports/index

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server /reports endpoint
+ + + + + + + +

(require("src/api/reports/index"))(config, db, logger) → {Object}

+ + + + + +
+ Methods to get current flood reports from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for reports route +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_reports_model.html b/jsdoc/module-src_api_reports_model.html new file mode 100644 index 0000000..3ffe2c1 --- /dev/null +++ b/jsdoc/module-src_api_reports_model.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/reports/model + + + + + + + + + + +
+ +

Module: src/api/reports/model

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server /reports data model
+ + + + + + + +

(require("src/api/reports/model"))(config, db, logger) → {Object}

+ + + + + +
+ Methods to get current flood reports from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ Query results +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/scripts/linenumber.js b/jsdoc/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/jsdoc/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/jsdoc/scripts/prettify/Apache-License-2.0.txt b/jsdoc/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/jsdoc/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/jsdoc/scripts/prettify/lang-css.js b/jsdoc/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/jsdoc/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/jsdoc/scripts/prettify/prettify.js b/jsdoc/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/jsdoc/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + JSDoc: Module: server +Core server module + + + + + + + + + + +
+ +

Module: server +Core server module

+ + + + + + +
+ +
+ +
+ +
+
+ + +
CogniCity Data Server Module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/server.js.html b/jsdoc/server.js.html new file mode 100644 index 0000000..6c0833b --- /dev/null +++ b/jsdoc/server.js.html @@ -0,0 +1,153 @@ + + + + + JSDoc: Source: server.js + + + + + + + + + + +
+ +

Source: server.js

+ + + + + + +
+
+
/**
+ * CogniCity Data Server Module
+ * @module server
+ * Core server module
+ **/
+// Import
+import Promise from 'bluebird';
+
+// Express middleware and http
+import express from 'express';
+import http from 'http';
+
+// Import express middlewares
+import bodyParser from 'body-parser';
+import cors from 'cors';
+import compression from 'compression';
+import responseTime from 'response-time';
+import morgan from 'morgan'; // Express logging
+
+/**
+ * Core server object
+ * @alias module:server
+ * @param {Object} config - configuration
+ * @param {Object} initializeDb - database initialization
+ * @param {Object} routes - routes
+ * @param {Object} logger - logger
+ * @return {Object} - Express server application
+ **/
+// Function to initialize the api server
+const init = (config, initializeDb, routes, logger) =>
+	new Promise((resolve, reject) => {
+	// Create the server
+	let app = express();
+	app.server = http.createServer(app);
+
+	// Winston stream function for express so we can capture logs
+	const winstonStream = {
+		write: function(message) {
+			logger.info(message.slice(0, -1));
+		},
+	};
+
+	// Setup express logger
+	app.use(morgan('combined', {stream: winstonStream}));
+
+	// Compress responses if required but only if caching is disabled
+	if (config.COMPRESS && !config.CACHE) {
+		app.use(compression());
+	}
+
+	// Provide CORS support (not required if behind API gateway)
+	if (config.CORS) {
+		app.use(cors({exposedHeaders: config.CORS_HEADERS}));
+	}
+
+	// Provide response time header in response
+	if (config.RESPONSE_TIME) {
+		app.use(responseTime());
+	}
+
+	// Parse body messages into json
+	app.use(bodyParser.json({limit: config.BODY_LIMIT}));
+
+	// Try and connect to the db
+	initializeDb(config, logger)
+		.then((db) => {
+			// Log debug message
+			logger.debug('Successfully connected to DB');
+
+			// Mount the routes
+			app.use('/', routes({config, db, logger}));
+
+			// App is ready to go, resolve the promise
+			resolve(app);
+		})
+		.catch((err) => {
+			/* istanbul ignore next */
+			logger.error('DB Connection error: ' + err);
+			/* istanbul ignore next */
+			logger.error('Fatal error: Application shutting down');
+			/* istanbul ignore next */
+			// We cannot continue without a DB, reject
+			reject(err);
+		});
+});
+
+// Export the init function for use externally
+
+module.exports = {init};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/styles/jsdoc-default.css b/jsdoc/styles/jsdoc-default.css new file mode 100644 index 0000000..ede1919 --- /dev/null +++ b/jsdoc/styles/jsdoc-default.css @@ -0,0 +1,354 @@ +@font-face { + font-family: 'Open Sans'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Regular-webfont.eot'); + src: + local('Open Sans'), + local('OpenSans'), + url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), + url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); +} + +@font-face { + font-family: 'Open Sans Light'; + font-weight: normal; + font-style: normal; + src: url('../fonts/OpenSans-Light-webfont.eot'); + src: + local('Open Sans Light'), + local('OpenSans Light'), + url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Light-webfont.woff') format('woff'), + url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); +} + +html +{ + overflow: auto; + background-color: #fff; + font-size: 14px; +} + +body +{ + font-family: 'Open Sans', sans-serif; + line-height: 1.5; + color: #4d4e53; + background-color: white; +} + +a, a:visited, a:active { + color: #0095dd; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +header +{ + display: block; + padding: 0px 4px; +} + +tt, code, kbd, samp { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.class-description { + font-size: 130%; + line-height: 140%; + margin-bottom: 1em; + margin-top: 1em; +} + +.class-description:empty { + margin: 0; +} + +#main { + float: left; + width: 70%; +} + +article dl { + margin-bottom: 40px; +} + +section +{ + display: block; + background-color: #fff; + padding: 12px 24px; + border-bottom: 1px solid #ccc; + margin-right: 30px; +} + +.variation { + display: none; +} + +.signature-attributes { + font-size: 60%; + color: #aaa; + font-style: italic; + font-weight: lighter; +} + +nav +{ + display: block; + float: right; + margin-top: 28px; + width: 30%; + box-sizing: border-box; + border-left: 1px solid #ccc; + padding-left: 16px; +} + +nav ul { + font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; + font-size: 100%; + line-height: 17px; + padding: 0; + margin: 0; + list-style-type: none; +} + +nav ul a, nav ul a:visited, nav ul a:active { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + line-height: 18px; + color: #4D4E53; +} + +nav h3 { + margin-top: 12px; +} + +nav li { + margin-top: 6px; +} + +footer { + display: block; + padding: 6px; + margin-top: 12px; + font-style: italic; + font-size: 90%; +} + +h1, h2, h3, h4 { + font-weight: 200; + margin: 0; +} + +h1 +{ + font-family: 'Open Sans Light', sans-serif; + font-size: 48px; + letter-spacing: -2px; + margin: 12px 24px 20px; +} + +h2, h3.subsection-title +{ + font-size: 30px; + font-weight: 700; + letter-spacing: -1px; + margin-bottom: 12px; +} + +h3 +{ + font-size: 24px; + letter-spacing: -0.5px; + margin-bottom: 12px; +} + +h4 +{ + font-size: 18px; + letter-spacing: -0.33px; + margin-bottom: 12px; + color: #4d4e53; +} + +h5, .container-overview .subsection-title +{ + font-size: 120%; + font-weight: bold; + letter-spacing: -0.01em; + margin: 8px 0 3px 0; +} + +h6 +{ + font-size: 100%; + letter-spacing: -0.01em; + margin: 6px 0 3px 0; + font-style: italic; +} + +table +{ + border-spacing: 0; + border: 0; + border-collapse: collapse; +} + +td, th +{ + border: 1px solid #ddd; + margin: 0px; + text-align: left; + vertical-align: top; + padding: 4px 6px; + display: table-cell; +} + +thead tr +{ + background-color: #ddd; + font-weight: bold; +} + +th { border-right: 1px solid #aaa; } +tr > th:last-child { border-right: 1px solid #ddd; } + +.ancestors { color: #999; } +.ancestors a +{ + color: #999 !important; + text-decoration: none; +} + +.clear +{ + clear: both; +} + +.important +{ + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px; +} + +.type-signature { + color: #aaa; +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace; +} + +.details { margin-top: 14px; border-left: 2px solid #DDD; } +.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } +.details dd { margin-left: 70px; } +.details ul { margin: 0; } +.details ul { list-style-type: none; } +.details li { margin-left: 30px; padding-top: 6px; } +.details pre.prettyprint { margin: 0 } +.details .object-value { padding-top: 0; } + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption +{ + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint +{ + border: 1px solid #ddd; + width: 80%; + overflow: auto; +} + +.prettyprint.source { + width: inherit; +} + +.prettyprint code +{ + font-size: 100%; + line-height: 18px; + display: block; + padding: 4px 12px; + margin: 0; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code span.line +{ + display: inline-block; +} + +.prettyprint.linenums +{ + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol +{ + padding-left: 0; +} + +.prettyprint.linenums li +{ + border-left: 3px #ddd solid; +} + +.prettyprint.linenums li.selected, +.prettyprint.linenums li.selected * +{ + background-color: lightyellow; +} + +.prettyprint.linenums li * +{ + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td.description > p:first-child, +.props td.description > p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, +.props td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/jsdoc/styles/prettify-jsdoc.css b/jsdoc/styles/prettify-jsdoc.css new file mode 100644 index 0000000..5a2526e --- /dev/null +++ b/jsdoc/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/jsdoc/styles/prettify-tomorrow.css b/jsdoc/styles/prettify-tomorrow.css new file mode 100644 index 0000000..b6f92a7 --- /dev/null +++ b/jsdoc/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html b/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html new file mode 100644 index 0000000..d623ec6 --- /dev/null +++ b/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testCAP +A module to test the CAP data format utility + + + + + + + + + + +
+ +

Module: test/testCAP +A module to test the CAP data format utility

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testCAP module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testCAP.js.html b/jsdoc/test_testCAP.js.html new file mode 100644 index 0000000..e0d3b86 --- /dev/null +++ b/jsdoc/test_testCAP.js.html @@ -0,0 +1,261 @@ + + + + + JSDoc: Source: test/testCAP.js + + + + + + + + + + +
+ +

Source: test/testCAP.js

+ + + + + + +
+
+
/**
+ * testCAP module
+ * @module test/testCAP
+ * A module to test the CAP data format utility
+ */
+
+import * as test from 'unit.js'; // Unit testing module
+import Cap from '../lib/cap';    // Cap formatter helper
+
+
+/**
+ * Test CAP data format utility
+ * @alias module:test/testCAP
+ * @param {Object} logger - CogniCity Server logger object
+ */
+export default function(logger) {
+  describe('CAP Utility', function() {
+    const cap = new Cap(logger); // Setup our cap formatter
+
+    // dummy data (polygon)
+    let feature = {'type': 'Feature',
+                  'geometry':
+                    {'type': 'Polygon',
+                    'coordinates': [
+                      [[106.8367359996, -6.2305420003],
+                      [106.8367039999, -6.2307239997],
+                      [106.8367995401, -6.2301399095],
+                      [106.8367359996, -6.2305420003]]]},
+                    'properties':
+                      {'area_id': '1346',
+                      'geom_id': '3171100002004000',
+                      'area_name': 'RW 04',
+                      'parent_name': 'KUNINGAN TIMUR',
+                      'city_name': 'Jakarta',
+                      'state': 4,
+                      'last_updated': '2017-03-31T02:45:52.574Z',
+                    },
+                  };
+
+    // Test geometry string creation
+    it('Can create polygon strings from geojson features', function(done) {
+      cap.createArea(feature);
+      let result = cap.createArea(feature);
+      test.value(typeof(result.polygon[0])).is('string');
+      done();
+    });
+
+    // dummy data (multipolygon)
+    feature = {'type': 'Feature',
+              'geometry':
+                {'type': 'MultiPolygon',
+                'coordinates': [
+                  [[[106.8367359996, -6.2305420003],
+                  [106.8367039999, -6.2307239997],
+                  [106.8367995401, -6.2301399095],
+                  [106.8367359996, -6.2305420003]]]]},
+                'properties':
+                  {'area_id': '1346',
+                  'geom_id': '3171100002004000',
+                  'area_name': 'RW 04',
+                  'parent_name': 'KUNINGAN TIMUR',
+                  'city_name': 'Jakarta',
+                  'state': 4,
+                  'last_updated': '2017-03-31T02:45:52.574Z',
+                },
+              };
+
+    // Test multigeometry string creation
+    it('Will operate on multipolygon objects', function(done) {
+      let result = cap.createArea(feature);
+      test.value(typeof(result.polygon[0])).is('string');
+      done();
+    });
+
+    // Test level severe
+    it('Create the correct severity levels', function(done) {
+      // dummy data (polygon)
+      let feature = {'type': 'Feature',
+                    'geometry':
+                      {'type': 'Polygon',
+                      'coordinates': [
+                        [[106.8367359996, -6.2305420003],
+                        [106.8367039999, -6.2307239997],
+                        [106.8367995401, -6.2301399095],
+                        [106.8367359996, -6.2305420003]]]},
+                      'properties':
+                        {'area_id': '1346',
+                        'geom_id': '3171100002004000',
+                        'area_name': 'RW 04',
+                        'parent_name': 'KUNINGAN TIMUR',
+                        'city_name': 'Jakarta',
+                        'state': 4,
+                        'last_updated': '2017-03-31T02:45:52.574Z',
+                      },
+                    };
+
+      let result = cap.createInfo(feature);
+      test.value(result.severity).is('Severe');
+      done();
+    });
+
+    // Test level moderate
+    it('Create the correct severity levels', function(done) {
+      // dummy data (polygon)
+      let feature = {'type': 'Feature',
+                    'geometry':
+                      {'type': 'Polygon', 'coordinates': [
+                        [[106.8367359996, -6.2305420003],
+                        [106.8367039999, -6.2307239997],
+                        [106.8367995401, -6.2301399095],
+                        [106.8367359996, -6.2305420003]]]},
+                    'properties': {'area_id': '1346',
+                                  'geom_id': '3171100002004000',
+                                  'area_name': 'RW 04',
+                                  'parent_name': 'KUNINGAN TIMUR',
+                                  'city_name': 'Jakarta',
+                                  'state': 3,
+                                  'last_updated': '2017-03-31T02:45:52.574Z',
+                      },
+                    };
+
+      let result = cap.createInfo(feature);
+      test.value(result.severity).is('Moderate');
+      done();
+    });
+
+    // Test level minor
+    it('Create the correct severity levels', function(done) {
+      // dummy data (polygon)
+      let feature = {'type': 'Feature',
+                    'geometry': {'type': 'Polygon', 'coordinates': [
+                      [[106.8367359996, -6.2305420003],
+                      [106.8367039999, -6.2307239997],
+                      [106.8367995401, -6.2301399095],
+                      [106.8367359996, -6.2305420003]]]},
+                    'properties': {'area_id': '1346',
+                                  'geom_id': '3171100002004000',
+                                  'area_name': 'RW 04',
+                                  'parent_name': 'KUNINGAN TIMUR',
+                                  'city_name': 'Jakarta',
+                                  'state': 2,
+                                  'last_updated': '2017-03-31T02:45:52.574Z',
+                                },
+                    };
+
+      let result = cap.createInfo(feature);
+      test.value(result.severity).is('Minor');
+      done();
+    });
+
+    // Test level Unknown
+    it('Create the correct severity levels', function(done) {
+      // dummy data (polygon)
+      let feature = {'type': 'Feature',
+                    'geometry': {'type': 'Polygon', 'coordinates': [
+                      [[106.8367359996, -6.2305420003],
+                      [106.8367039999, -6.2307239997],
+                      [106.8367995401, -6.2301399095],
+                      [106.8367359996, -6.2305420003]]]},
+                    'properties': {'area_id': '1346',
+                     'geom_id': '3171100002004000',
+                     'area_name': 'RW 04',
+                     'parent_name': 'KUNINGAN TIMUR',
+                     'city_name': 'Jakarta',
+                     'state': 1,
+                     'last_updated': '2017-03-31T02:45:52.574Z',
+                   },
+                 };
+
+      let result = cap.createInfo(feature);
+      test.value(result.severity).is('Unknown');
+      done();
+    });
+
+    // Test bad geometry will bubble up internall within CAP module
+    it('Catch unsupported complex polygon geometry', function(done) {
+      // dummy data (polygon)
+      let feature = {'type': 'Feature',
+                    'geometry': {'type': 'Polygon', 'coordinates': [
+                      [[0, 0], [3, 6], [6, 1], [0, 0]],
+                      [[2, 2], [3, 3], [4, 2], [2, 2]]]},
+                    'properties': {'area_id': '1346',
+                      'geom_id': '3171100002004000',
+                      'area_name': 'RW 04',
+                      'parent_name': 'KUNINGAN TIMUR',
+                      'city_name': 'Jakarta',
+                      'state': 1,
+                      'last_updated': '2017-03-31T02:45:52.574Z',
+                    },
+                  };
+
+      let result = cap.createInfo(feature);
+      test.value(result).is(undefined);
+      done();
+    });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html b/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html new file mode 100644 index 0000000..acd2b62 --- /dev/null +++ b/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testCards +A module to test the /cards endpoint + + + + + + + + + + +
+ +

Module: test/testCards +A module to test the /cards endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testCards module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testCards.js.html b/jsdoc/test_testCards.js.html new file mode 100644 index 0000000..dd244cc --- /dev/null +++ b/jsdoc/test_testCards.js.html @@ -0,0 +1,233 @@ + + + + + JSDoc: Source: test/testCards.js + + + + + + + + + + +
+ +

Source: test/testCards.js

+ + + + + + +
+
+
/**
+ * testCards module
+ * @module test/testCards
+ * A module to test the /cards endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test cards endpoint
+ * @alias module:test/testCards
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Cards endpoint
+  describe('Cards endpoint', function() {
+    // Cards
+    it('Return 404 if card requested without ID (GET /cards)', function(done) {
+        test.httpAgent(app)
+          .get('/cards')
+          .expect(404)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Can get reports
+    it('Return 400 if card ID is invalid (GET /cards/:id)', function(done) {
+        test.httpAgent(app)
+          .get('/cards/1')
+          .expect(400)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+   });
+
+   // Cards end to end test
+   describe('End-to-end card test', function() {
+     let cardId = '0';
+
+     // Request a card, submit and get resulting card details
+     it('Get card one time link', function(done) {
+         test.httpAgent(app)
+           .post('/cards')
+           .send({
+               'username': 'testuser',
+               'network': 'test network',
+               'language': 'en',
+           })
+           .expect(200)
+           .expect('Content-Type', /json/)
+           .end(function(err, res) {
+             if (err) {
+               test.fail(err.message + ' ' + JSON.stringify(res));
+             } else {
+               cardId = res.body.cardId;
+               test.value(res.body.created).is(true);
+               done();
+             }
+          });
+       });
+
+       // Check HEAD request on cardId
+       it('Check HEAD request on cardId', function(done) {
+           test.httpAgent(app)
+             .head('/cards/'+cardId)
+             .expect(200)
+             .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)
+             .put('/cards/'+cardId)
+             .send({
+                 'disaster_type': 'flood',
+                 'card_data': {
+                   'flood_depth': 20,
+                   'report_type': 'flood',
+                 },
+                 'text': 'integration testing',
+                 'created_at': '2017-06-07T07:00:00+0700',
+                 'location': {
+                   'lat': -6.4,
+                   'lng': 106.6,
+                 },
+             })
+             .expect(200)
+             .expect('Content-Type', /json/)
+             .end(function(err, res) {
+               if (err) {
+                 test.fail(err.message + ' ' + JSON.stringify(res));
+               } else {
+                 done();
+               }
+            });
+         });
+
+         // Get signed URL for card image
+         it('Get card image link', (done) => {
+             test.httpAgent(app)
+               .get('/cards/'+cardId+'/images')
+               .expect(200)
+               .end(function(err, res) {
+                 if (err) {
+                   test.fail(err.message + ' ' + JSON.stringify(res));
+                 } else {
+                   done();
+                 }
+              });
+           }).timeout(150000);
+
+         // Request a card, submit and get resulting report
+         it('Get card data', function(done) {
+             test.httpAgent(app)
+               .get('/cards/'+cardId)
+               .expect(200)
+               .expect('Content-Type', /json/)
+               .end(function(err, res) {
+                 if (err) {
+                   test.fail(err.message + ' ' + JSON.stringify(res));
+                 } else {
+                   test.value(res.body.result.card_id).is(cardId);
+                   test.value(res.body.result.username).is('testuser');
+                   test.value(res.body.result.network).is('test network');
+                   test.value(res.body.result.language).is('en');
+                   test.value(res.body.result.report.text)
+                    .is('integration testing');
+                   done();
+                 }
+              });
+           });
+
+           // Update a card
+           it('Update card data', function(done) {
+               test.httpAgent(app)
+                 .patch('/cards/'+cardId)
+                 .send({
+                     'image_url': 'dummy image url',
+                 })
+                 .expect(200)
+                 .expect('Content-Type', /json/)
+                 .end(function(err, res) {
+                   if (err) {
+                     test.fail(err.message + ' ' + JSON.stringify(res));
+                   } else {
+                     done();
+                   }
+                });
+             });
+   });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html b/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html new file mode 100644 index 0000000..fdf0b1d --- /dev/null +++ b/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testCities +A module to test the /cities endpoint + + + + + + + + + + +
+ +

Module: test/testCities +A module to test the /cities endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testCities module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testCities.js.html b/jsdoc/test_testCities.js.html new file mode 100644 index 0000000..878fac9 --- /dev/null +++ b/jsdoc/test_testCities.js.html @@ -0,0 +1,96 @@ + + + + + JSDoc: Source: test/testCities.js + + + + + + + + + + +
+ +

Source: test/testCities.js

+ + + + + + +
+
+
/**
+ * testCities module
+ * @module test/testCities
+ * A module to test the /cities endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test cities endpoint
+ * @alias module:test/testCities
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Cities endpoint
+  describe('Cities endpoint', function() {
+    // Can get cities
+    it('Return 200 for cities list', function(done) {
+        test.httpAgent(app)
+          .get('/cities')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html b/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html new file mode 100644 index 0000000..2afa05b --- /dev/null +++ b/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testDB +A module to test the db utility module + + + + + + + + + + +
+ +

Module: test/testDB +A module to test the db utility module

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testDB module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testDB.js.html b/jsdoc/test_testDB.js.html new file mode 100644 index 0000000..20d7097 --- /dev/null +++ b/jsdoc/test_testDB.js.html @@ -0,0 +1,102 @@ + + + + + JSDoc: Source: test/testDB.js + + + + + + + + + + +
+ +

Source: test/testDB.js

+ + + + + + +
+
+
/* eslint-disable no-console */
+/**
+ * testDB module
+ * @module test/testDB
+ * A module to test the db utility module
+ */
+
+import * as test from 'unit.js';  // Unit testing module
+import initializeDb from '../db'; // Database utility
+
+/**
+ * Test db utility module
+ * @alias module:test/testDB
+ */
+export default function() {
+  describe('Test CogniCity Server Database Module', function() {
+   it('Catches errors on startup', function(done) {
+     // Try and connect to the db
+     let config = {};
+     let logger = {};
+     logger.error = function(err) {
+       console.log(err);
+     };
+     logger.debug = function(err) {
+       console.log(err);
+     };
+     initializeDb(config, logger)
+      .then((db) => {
+        test.value(db).is(null);
+        // done(); do nothing here, an error should be forced by empty config
+      })
+      .catch((err) => {
+        console.log(err);
+        done();
+      });
+   });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html b/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html new file mode 100644 index 0000000..d7bbe17 --- /dev/null +++ b/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testFeeds +A module to test the /feeds endpoint + + + + + + + + + + +
+ +

Module: test/testFeeds +A module to test the /feeds endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testFeeds module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testFeeds.js.html b/jsdoc/test_testFeeds.js.html new file mode 100644 index 0000000..f984236 --- /dev/null +++ b/jsdoc/test_testFeeds.js.html @@ -0,0 +1,185 @@ + + + + + JSDoc: Source: test/testFeeds.js + + + + + + + + + + +
+ +

Source: test/testFeeds.js

+ + + + + + +
+
+
/**
+ * testFeeds module
+ * @module test/testFeeds
+ * A module to test the /feeds endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test feeds endpoint
+ * @alias module:test/testFeeds
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Feeds endpoint
+  describe('Feeds endpoint', function() {
+    // Can post to qlue
+    it('Return 200 for post to feeds/qlue', function(done) {
+        test.httpAgent(app)
+          .post('/feeds/qlue')
+          .send({
+              'post_id': '9999',
+              'created_at': '2017-06-07T14:32+0700',
+              'qlue_city': 'surabaya',
+              'disaster_type': 'flood',
+              'text': 'test report',
+              'location': {
+                'lat': 45,
+                'lng': 45,
+              },
+          })
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Catch duplicate post to qlue
+    it('Catch duplicate entry to feeds/qlue', function(done) {
+        test.httpAgent(app)
+          .post('/feeds/qlue')
+          .send({
+              'post_id': '9999',
+              'created_at': '2017-06-07T14:32+0700',
+              'qlue_city': 'surabaya',
+              'disaster_type': 'flood',
+              'text': 'test report',
+              'location': {
+                'lat': 45,
+                'lng': 45,
+              },
+          })
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              test.value(res.body.created).is(false);
+              done();
+            }
+         });
+      });
+
+    // Can post to detik
+    it('Return 200 for post to feeds/detik', function(done) {
+        test.httpAgent(app)
+          .post('/feeds/detik')
+          .send({
+              'contribution_id': '9999',
+              'created_at': '2017-06-07T14:32+0700',
+              'disaster_type': 'flood',
+              'location': {
+                'latitude': 45,
+                'longitude': 45,
+              },
+              'text': 'test report',
+          })
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+      // Catch duplicate post to detik
+      it('Return 200 for post to feeds/detik', function(done) {
+          test.httpAgent(app)
+            .post('/feeds/detik')
+            .send({
+                'contribution_id': '9999',
+                'created_at': '2017-06-07T14:32+0700',
+                'disaster_type': 'flood',
+                'location': {
+                  'latitude': 45,
+                  'longitude': 45,
+                },
+                'text': 'test report',
+            })
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                test.value(res.body.created).is(false);
+                done();
+              }
+           });
+        });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testFloodgauges.js.html b/jsdoc/test_testFloodgauges.js.html new file mode 100644 index 0000000..5f7d25a --- /dev/null +++ b/jsdoc/test_testFloodgauges.js.html @@ -0,0 +1,126 @@ + + + + + JSDoc: Source: test/testFloodgauges.js + + + + + + + + + + +
+ +

Source: test/testFloodgauges.js

+ + + + + + +
+
+
/**
+ * testFloodsgauges module
+ * @module test/testFloodsgauges
+ * A module to test the /floodgauges endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test floodgauges endpoint
+ * @alias module:test/testFloodsgauges
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Flood gauges endpoint
+  describe('Flood gauges endpoint', function() {
+    // Can get flood gauge data
+    it('Return 200 for get /floodgauges', function(done) {
+        test.httpAgent(app)
+          .get('/floodgauges')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+      // Catch invalid city in floodgauge
+      it('Return 400 for get /floodgauges?city=xxx', function(done) {
+          test.httpAgent(app)
+            .get('/floodgauges?city=xxx')
+            .expect(400)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+
+      // Catch invalid floodgauge id
+      it('Return 404 for get /floodgauges/:id', function(done) {
+          test.httpAgent(app)
+            .get('/floodgauges/0')
+            .expect(404)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html b/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html new file mode 100644 index 0000000..7970c88 --- /dev/null +++ b/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testFloods +A module to test the /floods endpoint + + + + + + + + + + +
+ +

Module: test/testFloods +A module to test the /floods endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testFloods module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testFloods.js.html b/jsdoc/test_testFloods.js.html new file mode 100644 index 0000000..0a2118c --- /dev/null +++ b/jsdoc/test_testFloods.js.html @@ -0,0 +1,229 @@ + + + + + JSDoc: Source: test/testFloods.js + + + + + + + + + + +
+ +

Source: test/testFloods.js

+ + + + + + +
+
+
/**
+ * testFloods module
+ * @module test/testFloods
+ * A module to test the /floods endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test infrastructure endpoint
+ * @alias module:test/testFloods
+ * @param {Object} app - CogniCity server app object
+ * @param {Object} jwt - Sample JSON Web Token for testing endpoint auth
+ */
+export default function(app, jwt) {
+// Floods endpoint
+describe('Flood areas endpoint', function() {
+    // Test put flood auth
+    it('Catch bad auth for put a flood (PUT /floods/:id)', function(done) {
+      test.httpAgent(app)
+        .put('/floods/5')
+        .send({
+            'state': '2',
+        })
+        .expect(401)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+
+    // Test delete flood auth
+    it('Catch bad auth for delete a flood (PUT /floods/:id)', function(done) {
+      test.httpAgent(app)
+        .delete('/floods/5')
+        .send({
+            'username': 'testing',
+        })
+        .expect(401)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+
+    // Test put flood auth
+    it('Put a flood  with auth(PUT /floods/:id)', function(done) {
+      test.httpAgent(app)
+        .put('/floods/5?username=testing')
+        .set({'Authorization': 'Bearer ' + jwt})
+        .send({
+            'state': '2',
+        })
+        .expect(200)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+
+    // Test delete flood auth
+    it('Delete a flood (PUT /floods/:id)', function(done) {
+      test.httpAgent(app)
+        .delete('/floods/5?username=testing')
+        .set({'Authorization': 'Bearer ' + jwt})
+        .expect(200)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+
+    // Get floods
+    it('Get floods (GET /floods)', (done) => {
+        test.httpAgent(app)
+          .get('/floods')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      }).timeout(15000); // a lot of data is returned
+
+      // Get floods
+      it('Get severe floods (GET /floods?minimum_state=3)', function(done) {
+          test.httpAgent(app)
+            .get('/floods?minimum_state=3')
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        }).timeout(15000); // a lot of data is returned
+
+
+      // Just get flood states
+      it('Get flood states without geo (GET /floods/states)', function(done) {
+          test.httpAgent(app)
+            .get('/floods/states')
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+
+      // Get geographic floods
+      it('Get floods in geojson format', (done) => {
+          test.httpAgent(app)
+            .get('/floods/?format=json&geoformat=geojson')
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        }).timeout(15000); // a lot of data is returned
+
+    // Can get reports in CAP format
+    it('Get all reports in CAP format', (done) => {
+        test.httpAgent(app)
+          .get('/floods?format=xml&geoformat=cap')
+          .expect(200)
+          .expect('Content-Type', /text/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      }).timeout(15000); // a lot of data is returned;
+    });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html b/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html new file mode 100644 index 0000000..f4a8b8a --- /dev/null +++ b/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testFloodsgauges +A module to test the /floodgauges endpoint + + + + + + + + + + +
+ +

Module: test/testFloodsgauges +A module to test the /floodgauges endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testFloodsgauges module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html b/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html new file mode 100644 index 0000000..2aef490 --- /dev/null +++ b/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testInfrastructure +A module to test the /infrastructure endpoint + + + + + + + + + + +
+ +

Module: test/testInfrastructure +A module to test the /infrastructure endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testInfrastructure module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testInfrastructure.js.html b/jsdoc/test_testInfrastructure.js.html new file mode 100644 index 0000000..2241d24 --- /dev/null +++ b/jsdoc/test_testInfrastructure.js.html @@ -0,0 +1,111 @@ + + + + + JSDoc: Source: test/testInfrastructure.js + + + + + + + + + + +
+ +

Source: test/testInfrastructure.js

+ + + + + + +
+
+
/**
+ * testInfrastructure module
+ * @module test/testInfrastructure
+ * A module to test the /infrastructure endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test infrastructure endpoint
+ * @alias module:test/testInfrastructure
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Infrastructure endpoint
+  describe('Infrastructure endpoint', function() {
+    // Catch invalid top-level infrastructure endpoint
+    it('Return 404 for get /infrastructure', function(done) {
+        test.httpAgent(app)
+          .get('/infrastructure')
+          .expect(404)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+      // Return waterways infrastructure
+      it('Return 200 for get /infrastructure/waterways', function(done) {
+          test.httpAgent(app)
+            .get('/infrastructure/waterways')
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html b/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html new file mode 100644 index 0000000..dc63256 --- /dev/null +++ b/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testReports +A module to test the /reports endpoint + + + + + + + + + + +
+ +

Module: test/testReports +A module to test the /reports endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testReports module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testReports.js.html b/jsdoc/test_testReports.js.html new file mode 100644 index 0000000..407b923 --- /dev/null +++ b/jsdoc/test_testReports.js.html @@ -0,0 +1,172 @@ + + + + + JSDoc: Source: test/testReports.js + + + + + + + + + + +
+ +

Source: test/testReports.js

+ + + + + + +
+
+
/**
+ * testReports module
+ * @module test/testReports
+ * A module to test the /reports endpoint
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test reports endpoint
+ * @alias module:test/testReports
+ * @param {Object} app - CogniCity server app object
+ * @param {Number} reportid - CogniCity report ID to test against
+ */
+export default function(app, reportid) {
+  // Reports endpoint
+  describe('Reports endpoint', function() {
+    // Can get reports
+    it('Get all reports (GET /reports)', function(done) {
+        test.httpAgent(app)
+          .get('/reports')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Can get reports as geojson
+    it('Get all reports as geojson', function(done) {
+        test.httpAgent(app)
+          .get('/reports?format=json&geoformat=geojson')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Can get reports as geojson
+    it('Get all reports as topojson', function(done) {
+        test.httpAgent(app)
+          .get('/reports?format=json&geoformat=topojson')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Can get reports by city
+    it('Get reports by city /reports?city=jbd', function(done) {
+        test.httpAgent(app)
+          .get('/reports?city=jbd')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Catch report by city error
+    it('Get reports by city /reports?city=xxx', function(done) {
+        test.httpAgent(app)
+          .get('/reports?city=xxx')
+          .expect(400)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+      // Can report by id
+      it('Get reports/:id endpoint', function(done) {
+          test.httpAgent(app)
+            .get('/reports/'+reportid)
+            .expect(200)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+   });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html b/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html new file mode 100644 index 0000000..630b33d --- /dev/null +++ b/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testReportsArchive +A module to test the /reports/archive endpoint + + + + + + + + + + +
+ +

Module: test/testReportsArchive +A module to test the /reports/archive endpoint

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testReportsArchive module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testReportsArchive.js.html b/jsdoc/test_testReportsArchive.js.html new file mode 100644 index 0000000..84ecaee --- /dev/null +++ b/jsdoc/test_testReportsArchive.js.html @@ -0,0 +1,206 @@ + + + + + JSDoc: Source: test/testReportsArchive.js + + + + + + + + + + +
+ +

Source: test/testReportsArchive.js

+ + + + + + +
+
+
/* eslint-disable max-len */
+/**
+ * testReportsArchive module
+ * @module test/testReportsArchive
+ * A module to test the /reports/archive endpoint
+ */
+
+import * as test from 'unit.js';
+
+// TODO
+// - test against an actual time in the database
+// - Entered through cards.js (or add a new call to cards here)
+
+/**
+ * Test reports archive endpoint
+ * @alias module:test/testReportsArchive
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  // Reports endpoint
+  describe('Reports Archive Endpoint', function() {
+    // Can get reports between given timestamps
+    it('Can get reports between given timestamps', function(done) {
+        test.httpAgent(app)
+          .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    it('Can get reports between given timestamps as geojson', function(done) {
+        test.httpAgent(app)
+          .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=geojson')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              test.value(res.body.result.type).is('FeatureCollection');
+              done();
+            }
+         });
+      });
+
+    it('Can get reports between timestamps as topojson', function(done) {
+        test.httpAgent(app)
+          .get('/reports/archive?start=2017-06-07T00:00:00%2B0700&end=2017-06-08T23:00:00%2B0700&format=json&geoformat=topojson')
+          .expect(200)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              test.value(res.body.result.type).is('Topology');
+              done();
+            }
+         });
+      });
+
+    // Can catch no start parameter
+    it('Required start parameter by default', function(done) {
+        test.httpAgent(app)
+          .get('/reports/archive?end=2017-02-22T07:00:00%2B0700')
+          .expect(400)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+    // Can catch no end parameter
+    it('Required end parameter by default', function(done) {
+        test.httpAgent(app)
+          .get('/reports/archive?start=2017-02-22T07:00:00%2B0700')
+          .expect(400)
+          .expect('Content-Type', /json/)
+          .end(function(err, res) {
+            if (err) {
+              test.fail(err.message + ' ' + JSON.stringify(res));
+            } else {
+              done();
+            }
+         });
+      });
+
+      // Can catch no UTC offset in end parameter
+      it('Required end parameter to have a UTC offset', function(done) {
+          test.httpAgent(app)
+            .get('/reports/archive?start=2017-02-21T07:00:00%2B0700&end=2017-02-22T07:00:00')
+            .expect(400)
+            .expect('Content-Type', /json/)
+            .end(function(err, res) {
+              if (err) {
+                test.fail(err.message + ' ' + JSON.stringify(res));
+              } else {
+                done();
+              }
+           });
+        });
+
+        // Can catch no UTC offset in start parameter
+        it('Required start parameter to have a UTC offset', function(done) {
+            test.httpAgent(app)
+              .get('/reports/archive?start=2017-02-21T07:00:00')
+              .expect(400)
+              .expect('Content-Type', /json/)
+              .end(function(err, res) {
+                if (err) {
+                  test.fail(err.message + ' ' + JSON.stringify(res));
+                } else {
+                  done();
+                }
+             });
+          });
+
+        // Can catch no UTC offset in start parameter
+        it('Catches badly formed time stamp', function(done) {
+            test.httpAgent(app)
+              .get('/reports/archive?start=2017-02-21')
+              .expect(400)
+              .expect('Content-Type', /json/)
+              .end(function(err, res) {
+                if (err) {
+                  test.fail(err.message + ' ' + JSON.stringify(res));
+                } else {
+                  done();
+                }
+             });
+          });
+   });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html b/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html new file mode 100644 index 0000000..e9638aa --- /dev/null +++ b/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html @@ -0,0 +1,142 @@ + + + + + JSDoc: Module: test/testServer +A module to test top-level API routes from the server + + + + + + + + + + +
+ +

Module: test/testServer +A module to test top-level API routes from the server

+ + + + + + +
+ +
+ +
+ +
+
+ + +
testServer module
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/test_testServer.js.html b/jsdoc/test_testServer.js.html new file mode 100644 index 0000000..70a3229 --- /dev/null +++ b/jsdoc/test_testServer.js.html @@ -0,0 +1,108 @@ + + + + + JSDoc: Source: test/testServer.js + + + + + + + + + + +
+ +

Source: test/testServer.js

+ + + + + + +
+
+
/**
+ * testServer module
+ * @module test/testServer
+ * A module to test top-level API routes from the server
+ */
+
+import * as test from 'unit.js';
+
+/**
+ * Test top-level server routes
+ * @alias module:test/testServer
+ * @param {Object} app - CogniCity server app object
+ */
+export default function(app) {
+  describe('Top level API endpoint', function() {
+    it('Gets current API version', function(done) {
+      test.httpAgent(app)
+        .get('/')
+        .expect(200)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+
+    it('Can handle unknown routes', function(done) {
+      test.httpAgent(app)
+        .get('/moon')
+        .expect(404)
+        .expect('Content-Type', /json/)
+        .end(function(err, res) {
+          if (err) {
+            test.fail(err.message + ' ' + JSON.stringify(res));
+          } else {
+            done();
+          }
+        });
+    });
+  });
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) +
+ + + + + diff --git a/package.json b/package.json index 8e15d59..731b072 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "mocha": "nyc mocha --compilers js:babel-core/register src/test/", "coverage": "nyc report --reporter=text-lcov | coveralls", "lint": "eslint src", - "test": "npm run lint & npm run mocha" + "test": "npm run lint & npm run mocha", + "jsdoc": "./node_modules/jsdoc/jsdoc.js -r -R README.md -d ./jsdoc/ src/" }, "nyc": { "exclude": [ From c60ea818e892f6ba6eb9f16654e5a69e54170673 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 15:54:08 -0400 Subject: [PATCH 133/160] Removed old test file --- src/api/routes/reports/archive/test.js | 48 -------------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/api/routes/reports/archive/test.js diff --git a/src/api/routes/reports/archive/test.js b/src/api/routes/reports/archive/test.js deleted file mode 100644 index 74e43cc..0000000 --- a/src/api/routes/reports/archive/test.js +++ /dev/null @@ -1,48 +0,0 @@ -const request = require('supertest'); -const assert = require('chai').assert; -require('it-each')(); - -import {init} from '../../..'; - -// Setup an array of tests to run -const tests = [ - { - url: '/reports', - exp: { - status: 200, - }, - }, - { - url: '/reports?city=jbd', - exp: { - status: 200, - }, - }, - { - url: '/reports?city=xxx', - exp: { - status: 400, - }, - }, - { - url: '/reports/0', - exp: { - status: 404, - }, - }, -]; - -// Run the tests -describe('GET /reports', () => { - it.each(tests, 'respond with correct response for test', (test, next) => { - init().then((app) => { - request(app) - .get(test.url) - .end((err, res) => { - if (err) next(err); - assert.equal(res.status, test.exp.status); - return next(); - }); - }); - }); -}); From b3208d16733681ac7d409ac9d9cfa2b6a663eb50 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 16:02:37 -0400 Subject: [PATCH 134/160] Updated JSDoc --- jsdoc/api_index.js.html | 6 +- jsdoc/api_routes_cards_index.js.html | 6 +- jsdoc/api_routes_cards_model.js.html | 6 +- jsdoc/api_routes_infrastructure_index.js.html | 115 ++++++ jsdoc/api_routes_infrastructure_model.js.html | 101 +++++ .../api_routes_reports_archive_index.js.html | 123 ++++++ .../api_routes_reports_archive_model.js.html | 29 +- jsdoc/api_routes_reports_index.js.html | 6 +- jsdoc/api_routes_reports_model.js.html | 6 +- jsdoc/config.js.html | 6 +- jsdoc/db%0ADatabase%20initializermodule_.html | 6 +- jsdoc/db.js.html | 6 +- jsdoc/index.html | 6 +- jsdoc/index.js.html | 6 +- jsdoc/lib_cap.js.html | 6 +- jsdoc/lib_util.js.html | 6 +- jsdoc/module-lib_cap.html | 6 +- jsdoc/module-src_api_cards_index.html | 6 +- jsdoc/module-src_api_cards_model.html | 6 +- jsdoc/module-src_api_index.html | 6 +- .../module-src_api_infrastructure_index.html | 373 ++++++++++++++++++ .../module-src_api_infrastructure_model.html | 172 ++++++++ .../module-src_api_reports_archive_index.html | 373 ++++++++++++++++++ jsdoc/module-src_api_reports_index.html | 150 +++---- jsdoc/module-src_api_reports_model.html | 207 +++++++++- ...e-src_api_reports_model_archive_model.html | 172 ++++++++ ...erver%0ACore%20server%20modulemodule_.html | 6 +- jsdoc/server.js.html | 6 +- ...0CAP%20data%20format%20utilitymodule_.html | 6 +- jsdoc/test_testCAP.js.html | 6 +- ...test%20the%20_cards%20endpointmodule_.html | 6 +- jsdoc/test_testCards.js.html | 6 +- ...est%20the%20_cities%20endpointmodule_.html | 6 +- jsdoc/test_testCities.js.html | 6 +- ...%20the%20db%20utility%20modulemodule_.html | 6 +- jsdoc/test_testDB.js.html | 6 +- ...test%20the%20_feeds%20endpointmodule_.html | 6 +- jsdoc/test_testFeeds.js.html | 6 +- jsdoc/test_testFloodgauges.js.html | 6 +- ...est%20the%20_floods%20endpointmodule_.html | 6 +- jsdoc/test_testFloods.js.html | 6 +- ...0the%20_floodgauges%20endpointmodule_.html | 6 +- ...e%20_infrastructure%20endpointmodule_.html | 6 +- jsdoc/test_testInfrastructure.js.html | 6 +- ...st%20the%20_reports%20endpointmodule_.html | 6 +- jsdoc/test_testReports.js.html | 6 +- ...%20_reports_archive%20endpointmodule_.html | 6 +- jsdoc/test_testReportsArchive.js.html | 6 +- ...20routes%20from%20the%20servermodule_.html | 6 +- jsdoc/test_testServer.js.html | 6 +- src/api/routes/infrastructure/index.js | 13 +- src/api/routes/infrastructure/model.js | 13 +- src/api/routes/reports/archive/index.js | 13 +- src/api/routes/reports/archive/model.js | 19 +- 54 files changed, 1893 insertions(+), 220 deletions(-) create mode 100644 jsdoc/api_routes_infrastructure_index.js.html create mode 100644 jsdoc/api_routes_infrastructure_model.js.html create mode 100644 jsdoc/api_routes_reports_archive_index.js.html create mode 100644 jsdoc/module-src_api_infrastructure_index.html create mode 100644 jsdoc/module-src_api_infrastructure_model.html create mode 100644 jsdoc/module-src_api_reports_archive_index.html create mode 100644 jsdoc/module-src_api_reports_model_archive_model.html diff --git a/jsdoc/api_index.js.html b/jsdoc/api_index.js.html index aa7a9b0..9737ac5 100644 --- a/jsdoc/api_index.js.html +++ b/jsdoc/api_index.js.html @@ -100,7 +100,7 @@

Source: api/index.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/api_routes_cards_index.js.html b/jsdoc/api_routes_cards_index.js.html index 6579318..cedff0a 100644 --- a/jsdoc/api_routes_cards_index.js.html +++ b/jsdoc/api_routes_cards_index.js.html @@ -308,7 +308,7 @@

Source: api/routes/cards/index.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/api_routes_cards_model.js.html b/jsdoc/api_routes_cards_model.js.html index e36f92a..710d205 100644 --- a/jsdoc/api_routes_cards_model.js.html +++ b/jsdoc/api_routes_cards_model.js.html @@ -178,7 +178,7 @@

Source: api/routes/cards/model.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/api_routes_infrastructure_index.js.html b/jsdoc/api_routes_infrastructure_index.js.html new file mode 100644 index 0000000..8a792d9 --- /dev/null +++ b/jsdoc/api_routes_infrastructure_index.js.html @@ -0,0 +1,115 @@ + + + + + JSDoc: Source: api/routes/infrastructure/index.js + + + + + + + + + + +
+ +

Source: api/routes/infrastructure/index.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /infrastructure endpoint
+ * @module src/api/infrastructure/index
+ **/
+import {Router} from 'express';
+
+// Import our data model
+import infrastructure from './model';
+
+// Import any required utility functions
+import {cacheResponse, handleGeoResponse} from '../../../lib/util';
+
+// Import validation dependencies
+import Joi from 'joi';
+import validate from 'celebrate';
+
+/**
+ * Endpoint specification for infrastructrue data
+ * @alias module:src/api/infrastructure/index
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} api Express router object for reports route
+ */
+export default ({config, db, logger}) => {
+	let api = Router(); // eslint-disable-line new-cap
+
+	// Get a list of infrastructure by type for a given city
+	api.get('/:type', cacheResponse(config.CACHE_DURATION_INFRASTRUCTURE),
+		validate({
+			params: {type: Joi.any().valid(config.INFRASTRUCTURE_TYPES)},
+			query: {
+				city: Joi.any().valid(config.REGION_CODES),
+				format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT),
+				geoformat: Joi.any().valid(config.GEO_FORMATS)
+									.default(config.GEO_FORMAT_DEFAULT),
+			},
+		}),
+		(req, res, next) => infrastructure(config, db, logger)
+			.all(req.query.city, req.params.type)
+			.then((data) => handleGeoResponse(data, req, res, next))
+			.catch((err) => {
+				/* istanbul ignore next */
+				logger.error(err);
+				/* istanbul ignore next */
+				next(err);
+			})
+	);
+
+	return api;
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_infrastructure_model.js.html b/jsdoc/api_routes_infrastructure_model.js.html new file mode 100644 index 0000000..f64b63a --- /dev/null +++ b/jsdoc/api_routes_infrastructure_model.js.html @@ -0,0 +1,101 @@ + + + + + JSDoc: Source: api/routes/infrastructure/model.js + + + + + + + + + + +
+ +

Source: api/routes/infrastructure/model.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /infrastructure data model
+ * @module src/api/infrastructure/model
+ **/
+import Promise from 'bluebird';
+
+/**
+* Methods to get infrastructure layers from database
+ * @alias module:src/api/reports/model
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} Query results
+ */
+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
+			FROM infrastructure.${type}
+			WHERE ($1 IS NULL OR tags->>'instance_region_code'=$1)`;
+
+		// Setup values
+		let values = [city];
+
+		// Execute
+		logger.debug(query, values);
+		db.any(query, values).timeout(config.PGTIMEOUT)
+			.then((data) => resolve(data))
+			/* istanbul ignore next */
+			.catch((err) => {
+				/* istanbul ignore next */
+				reject(err);
+			});
+	}),
+
+});
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_reports_archive_index.js.html b/jsdoc/api_routes_reports_archive_index.js.html new file mode 100644 index 0000000..894a961 --- /dev/null +++ b/jsdoc/api_routes_reports_archive_index.js.html @@ -0,0 +1,123 @@ + + + + + JSDoc: Source: api/routes/reports/archive/index.js + + + + + + + + + + +
+ +

Source: api/routes/reports/archive/index.js

+ + + + + + +
+
+
/**
+ * CogniCity Server /reports/archive endpoint
+ * @module src/api/reports/archive/index
+ **/
+import {Router} from 'express';
+
+// Import our data model
+import archive from './model';
+
+// Import any required utility functions
+import {cacheResponse, handleGeoResponse} from '../../../../lib/util';
+
+// Import validation dependencies
+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
+ * @alias module:src/api/reports/archive/index
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} api Express router object for reports route
+ */
+export default ({config, db, logger}) => {
+	let api = Router(); // eslint-disable-line new-cap
+
+	// Get a list of all reports
+	api.get('/', cacheResponse('1 minute'),
+
+		validate({
+			query: {
+				city: Joi.any().valid(config.REGION_CODES),
+				// TODO - does it matter than end time can be "before" start time?
+				// - Can reference arguments using Joi.ref('start')
+        start: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(),
+        end: Joi.date().format('YYYY-MM-DDTHH:mm:ssZ').required(),
+				// TODO we should restrict output to geo/topojson only.
+				// - CAP format doesn't make sense for historic data.
+				format: Joi.any().valid(config.FORMATS).default(config.FORMAT_DEFAULT),
+				geoformat: Joi.any().valid(config.GEO_FORMATS)
+					.default(config.GEO_FORMAT_DEFAULT),
+			},
+		}),
+		(req, res, next) => archive(config, db, logger)
+			.all(req.query.start, req.query.end, req.query.city)
+			.then((data) => handleGeoResponse(data, req, res, next))
+			.catch((err) => {
+				/* istanbul ignore next */
+				logger.error(err);
+				/* istanbul ignore next */
+				next(err);
+			})
+	);
+
+	return api;
+};
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + diff --git a/jsdoc/api_routes_reports_archive_model.js.html b/jsdoc/api_routes_reports_archive_model.js.html index 6bb8120..ba3cf7b 100644 --- a/jsdoc/api_routes_reports_archive_model.js.html +++ b/jsdoc/api_routes_reports_archive_model.js.html @@ -26,16 +26,21 @@

Source: api/routes/reports/archive/model.js

-
import Promise from 'bluebird';
-
+            
/**
+ * CogniCity Server /reports/archive data model
+ * @module src/api/reports/model/archive/model
+ **/
+import Promise from 'bluebird';
+
+/**
+ * Interact with historic report objects
+ * @alias module:src/api/reports/archive/model
+ * @param {Object} config Server configuration
+ * @param {Object} db PG Promise database instance
+ * @param {Object} logger Configured Winston logger instance
+ * @return {Object} Query results
+ */
 export default (config, db, logger) => ({
-	/**
-	 * Return all reports within a defined time period, and optionally city
-	 * @param {integer} start Timestamp as ISO 8601 string for start of window
-	 * @param {string} end Timestamp as ISO 8601 string for end of window
-	 * @param {string} city Optional, instance region code (e.g. 'jbd')\
-	 * @return {Object} Query result
-	 */
 	all: (start, end, city) => new Promise((resolve, reject) => {
 		// Setup query
 		let query = `SELECT pkey, created_at, source,
@@ -68,7 +73,7 @@ 

Source: api/routes/reports/archive/model.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/api_routes_reports_index.js.html b/jsdoc/api_routes_reports_index.js.html index 15112c0..78d9fb0 100644 --- a/jsdoc/api_routes_reports_index.js.html +++ b/jsdoc/api_routes_reports_index.js.html @@ -117,7 +117,7 @@

Source: api/routes/reports/index.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/api_routes_reports_model.js.html b/jsdoc/api_routes_reports_model.js.html index 2a1fdf7..7267352 100644 --- a/jsdoc/api_routes_reports_model.js.html +++ b/jsdoc/api_routes_reports_model.js.html @@ -98,7 +98,7 @@

Source: api/routes/reports/model.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/config.js.html b/jsdoc/config.js.html index 7c08919..9c9d3a1 100644 --- a/jsdoc/config.js.html +++ b/jsdoc/config.js.html @@ -113,7 +113,7 @@

Source: config.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/db%0ADatabase%20initializermodule_.html b/jsdoc/db%0ADatabase%20initializermodule_.html index 94e068d..6766566 100644 --- a/jsdoc/db%0ADatabase%20initializermodule_.html +++ b/jsdoc/db%0ADatabase%20initializermodule_.html @@ -115,7 +115,7 @@

Module: db
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/db.js.html b/jsdoc/db.js.html index c6915e9..5f5507a 100644 --- a/jsdoc/db.js.html +++ b/jsdoc/db.js.html @@ -73,7 +73,7 @@

Source: db.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/index.html b/jsdoc/index.html index 508bad2..120b586 100644 --- a/jsdoc/index.html +++ b/jsdoc/index.html @@ -439,7 +439,7 @@

lib/util.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/index.js.html b/jsdoc/index.js.html index 6b1f5ce..66c0b52 100644 --- a/jsdoc/index.js.html +++ b/jsdoc/index.js.html @@ -121,7 +121,7 @@

Source: index.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/lib_cap.js.html b/jsdoc/lib_cap.js.html index 0387eb8..76849a8 100644 --- a/jsdoc/lib_cap.js.html +++ b/jsdoc/lib_cap.js.html @@ -280,7 +280,7 @@

Source: lib/cap.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/lib_util.js.html b/jsdoc/lib_util.js.html index b7831ed..158782a 100644 --- a/jsdoc/lib_util.js.html +++ b/jsdoc/lib_util.js.html @@ -124,7 +124,7 @@

Source: lib/util.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-lib_cap.html b/jsdoc/module-lib_cap.html index 40f026d..e980189 100644 --- a/jsdoc/module-lib_cap.html +++ b/jsdoc/module-lib_cap.html @@ -197,7 +197,7 @@
Parameters:

- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_cards_index.html b/jsdoc/module-src_api_cards_index.html index 764b114..8e1bd87 100644 --- a/jsdoc/module-src_api_cards_index.html +++ b/jsdoc/module-src_api_cards_index.html @@ -145,7 +145,7 @@

Module: src/api/cards/index


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_cards_model.html b/jsdoc/module-src_api_cards_model.html index 1faf6e7..65bf9ca 100644 --- a/jsdoc/module-src_api_cards_model.html +++ b/jsdoc/module-src_api_cards_model.html @@ -346,7 +346,7 @@
Returns:

- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_index.html b/jsdoc/module-src_api_index.html index 91706bf..5946ab4 100644 --- a/jsdoc/module-src_api_index.html +++ b/jsdoc/module-src_api_index.html @@ -346,7 +346,7 @@
Returns:

- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_infrastructure_index.html b/jsdoc/module-src_api_infrastructure_index.html new file mode 100644 index 0000000..e3b0312 --- /dev/null +++ b/jsdoc/module-src_api_infrastructure_index.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/infrastructure/index + + + + + + + + + + +
+ +

Module: src/api/infrastructure/index

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server /infrastructure endpoint
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

(require("src/api/infrastructure/index"))(config, db, logger) → {Object}

+ + + + + +
+ Endpoint specification for infrastructrue data +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for reports route +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_infrastructure_model.html b/jsdoc/module-src_api_infrastructure_model.html new file mode 100644 index 0000000..f2d9603 --- /dev/null +++ b/jsdoc/module-src_api_infrastructure_model.html @@ -0,0 +1,172 @@ + + + + + JSDoc: Module: src/api/infrastructure/model + + + + + + + + + + +
+ +

Module: src/api/infrastructure/model

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +
CogniCity Server /infrastructure data model
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_reports_archive_index.html b/jsdoc/module-src_api_reports_archive_index.html new file mode 100644 index 0000000..154b11d --- /dev/null +++ b/jsdoc/module-src_api_reports_archive_index.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Module: src/api/reports/archive/index + + + + + + + + + + +
+ +

Module: src/api/reports/archive/index

+ + + + + + +
+ +
+ + + + + + + +
+ +
+
+ + +
CogniCity Server /reports/archive endpoint
+ + + + + + + +

(require("src/api/reports/archive/index"))(config, db, logger) → {Object}

+ + + + + +
+ Methods to get historic flood reports from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ api Express router object for reports route +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/module-src_api_reports_index.html b/jsdoc/module-src_api_reports_index.html index 6a07109..5a9e7d0 100644 --- a/jsdoc/module-src_api_reports_index.html +++ b/jsdoc/module-src_api_reports_index.html @@ -46,6 +46,78 @@

Module: src/api/reports/index

+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +

(require("src/api/reports/index"))(config, db, logger) → {Object}

@@ -243,78 +315,6 @@
Returns:
- - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - @@ -346,7 +346,7 @@
Returns:

- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_reports_model.html b/jsdoc/module-src_api_reports_model.html index 3ffe2c1..20ba155 100644 --- a/jsdoc/module-src_api_reports_model.html +++ b/jsdoc/module-src_api_reports_model.html @@ -33,6 +33,8 @@

Module: src/api/reports/model

+ + @@ -48,6 +50,205 @@

Module: src/api/reports/model

+

(require("src/api/reports/model"))(config, db, logger) → {Object}

+ + + + + +
+ Methods to get infrastructure layers from database +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
config + + +Object + + + + Server configuration
db + + +Object + + + + PG Promise database instance
logger + + +Object + + + + Configured Winston logger instance
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ Query results +
+ + + +
+
+ Type +
+
+ +Object + + +
+
+ + + + + + + + + +

(require("src/api/reports/model"))(config, db, logger) → {Object}

@@ -346,7 +547,7 @@
Returns:

- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/module-src_api_reports_model_archive_model.html b/jsdoc/module-src_api_reports_model_archive_model.html new file mode 100644 index 0000000..e7fa714 --- /dev/null +++ b/jsdoc/module-src_api_reports_model_archive_model.html @@ -0,0 +1,172 @@ + + + + + JSDoc: Module: src/api/reports/model/archive/model + + + + + + + + + + +
+ +

Module: src/api/reports/model/archive/model

+ + + + + + +
+ +
+ + + + + +
+ +
+
+ + +
CogniCity Server /reports/archive data model
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT) +
+ + + + + \ No newline at end of file diff --git a/jsdoc/server%0ACore%20server%20modulemodule_.html b/jsdoc/server%0ACore%20server%20modulemodule_.html index 8da38b1..b50ad28 100644 --- a/jsdoc/server%0ACore%20server%20modulemodule_.html +++ b/jsdoc/server%0ACore%20server%20modulemodule_.html @@ -115,7 +115,7 @@

Module: server
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/server.js.html b/jsdoc/server.js.html index 6c0833b..0119b2d 100644 --- a/jsdoc/server.js.html +++ b/jsdoc/server.js.html @@ -126,7 +126,7 @@

Source: server.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html b/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html index d623ec6..deed061 100644 --- a/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html +++ b/jsdoc/test_testCAP%0AA%20module%20to%20test%20the%20CAP%20data%20format%20utilitymodule_.html @@ -115,7 +115,7 @@

Module: test/testCAP
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCAP.js.html b/jsdoc/test_testCAP.js.html index e0d3b86..ecf1322 100644 --- a/jsdoc/test_testCAP.js.html +++ b/jsdoc/test_testCAP.js.html @@ -234,7 +234,7 @@

Source: test/testCAP.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html b/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html index acd2b62..c19801f 100644 --- a/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html +++ b/jsdoc/test_testCards%0AA%20module%20to%20test%20the%20_cards%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testCards
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCards.js.html b/jsdoc/test_testCards.js.html index dd244cc..217e1c4 100644 --- a/jsdoc/test_testCards.js.html +++ b/jsdoc/test_testCards.js.html @@ -206,7 +206,7 @@

Source: test/testCards.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html b/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html index fdf0b1d..fcc12e9 100644 --- a/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html +++ b/jsdoc/test_testCities%0AA%20module%20to%20test%20the%20_cities%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testCities
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testCities.js.html b/jsdoc/test_testCities.js.html index 878fac9..8a817a9 100644 --- a/jsdoc/test_testCities.js.html +++ b/jsdoc/test_testCities.js.html @@ -69,7 +69,7 @@

Source: test/testCities.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html b/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html index 2afa05b..4c55cf2 100644 --- a/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html +++ b/jsdoc/test_testDB%0AA%20module%20to%20test%20the%20db%20utility%20modulemodule_.html @@ -115,7 +115,7 @@

Module: test/testDB
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testDB.js.html b/jsdoc/test_testDB.js.html index 20d7097..e63ffb6 100644 --- a/jsdoc/test_testDB.js.html +++ b/jsdoc/test_testDB.js.html @@ -75,7 +75,7 @@

Source: test/testDB.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html b/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html index d7bbe17..802e36c 100644 --- a/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html +++ b/jsdoc/test_testFeeds%0AA%20module%20to%20test%20the%20_feeds%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testFeeds
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFeeds.js.html b/jsdoc/test_testFeeds.js.html index f984236..30c732a 100644 --- a/jsdoc/test_testFeeds.js.html +++ b/jsdoc/test_testFeeds.js.html @@ -158,7 +158,7 @@

Source: test/testFeeds.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFloodgauges.js.html b/jsdoc/test_testFloodgauges.js.html index 5f7d25a..bdb5c55 100644 --- a/jsdoc/test_testFloodgauges.js.html +++ b/jsdoc/test_testFloodgauges.js.html @@ -99,7 +99,7 @@

Source: test/testFloodgauges.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html b/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html index 7970c88..386885e 100644 --- a/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html +++ b/jsdoc/test_testFloods%0AA%20module%20to%20test%20the%20_floods%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testFloods
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFloods.js.html b/jsdoc/test_testFloods.js.html index 0a2118c..7dc5081 100644 --- a/jsdoc/test_testFloods.js.html +++ b/jsdoc/test_testFloods.js.html @@ -202,7 +202,7 @@

Source: test/testFloods.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html b/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html index f4a8b8a..f870fb4 100644 --- a/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html +++ b/jsdoc/test_testFloodsgauges%0AA%20module%20to%20test%20the%20_floodgauges%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testFloodsgauges
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html b/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html index 2aef490..f6c5bc0 100644 --- a/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html +++ b/jsdoc/test_testInfrastructure%0AA%20module%20to%20test%20the%20_infrastructure%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testInfrastructure
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testInfrastructure.js.html b/jsdoc/test_testInfrastructure.js.html index 2241d24..46b65c6 100644 --- a/jsdoc/test_testInfrastructure.js.html +++ b/jsdoc/test_testInfrastructure.js.html @@ -84,7 +84,7 @@

Source: test/testInfrastructure.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html b/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html index dc63256..f9dd745 100644 --- a/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html +++ b/jsdoc/test_testReports%0AA%20module%20to%20test%20the%20_reports%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testReports
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testReports.js.html b/jsdoc/test_testReports.js.html index 407b923..f0fc088 100644 --- a/jsdoc/test_testReports.js.html +++ b/jsdoc/test_testReports.js.html @@ -145,7 +145,7 @@

Source: test/testReports.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html b/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html index 630b33d..e572ec2 100644 --- a/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html +++ b/jsdoc/test_testReportsArchive%0AA%20module%20to%20test%20the%20_reports_archive%20endpointmodule_.html @@ -115,7 +115,7 @@

Module: test/testReportsArchive
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testReportsArchive.js.html b/jsdoc/test_testReportsArchive.js.html index 84ecaee..0742360 100644 --- a/jsdoc/test_testReportsArchive.js.html +++ b/jsdoc/test_testReportsArchive.js.html @@ -179,7 +179,7 @@

Source: test/testReportsArchive.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html b/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html index e9638aa..eaba03d 100644 --- a/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html +++ b/jsdoc/test_testServer%0AA%20module%20to%20test%20top-level%20API%20routes%20from%20the%20servermodule_.html @@ -115,7 +115,7 @@

Module: test/testServer
- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/jsdoc/test_testServer.js.html b/jsdoc/test_testServer.js.html index 70a3229..cfc4062 100644 --- a/jsdoc/test_testServer.js.html +++ b/jsdoc/test_testServer.js.html @@ -81,7 +81,7 @@

Source: test/testServer.js


- Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 15:51:49 GMT-0400 (EDT) + Documentation generated by JSDoc 3.4.3 on Thu Jun 29 2017 16:01:56 GMT-0400 (EDT)
diff --git a/src/api/routes/infrastructure/index.js b/src/api/routes/infrastructure/index.js index ea1df68..deb878a 100644 --- a/src/api/routes/infrastructure/index.js +++ b/src/api/routes/infrastructure/index.js @@ -1,3 +1,7 @@ +/** + * CogniCity Server /infrastructure endpoint + * @module src/api/infrastructure/index + **/ import {Router} from 'express'; // Import our data model @@ -10,7 +14,14 @@ import {cacheResponse, handleGeoResponse} from '../../../lib/util'; import Joi from 'joi'; import validate from 'celebrate'; - +/** + * Endpoint specification for infrastructrue data + * @alias module:src/api/infrastructure/index + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} api Express router object for reports route + */ export default ({config, db, logger}) => { let api = Router(); // eslint-disable-line new-cap diff --git a/src/api/routes/infrastructure/model.js b/src/api/routes/infrastructure/model.js index 4718959..a8a988c 100644 --- a/src/api/routes/infrastructure/model.js +++ b/src/api/routes/infrastructure/model.js @@ -1,7 +1,18 @@ +/** + * CogniCity Server /infrastructure data model + * @module src/api/infrastructure/model + **/ import Promise from 'bluebird'; +/** +* Methods to get infrastructure layers from database + * @alias module:src/api/reports/model + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} Query results + */ export default (config, db, logger) => ({ - // A list of all infrastructure matching a given type all: (city, type) => new Promise((resolve, reject) => { // Setup query diff --git a/src/api/routes/reports/archive/index.js b/src/api/routes/reports/archive/index.js index c53ce89..571b8db 100644 --- a/src/api/routes/reports/archive/index.js +++ b/src/api/routes/reports/archive/index.js @@ -1,3 +1,7 @@ +/** + * CogniCity Server /reports/archive endpoint + * @module src/api/reports/archive/index + **/ import {Router} from 'express'; // Import our data model @@ -12,7 +16,14 @@ import Extension from 'joi-date-extensions'; const Joi = BaseJoi.extend(Extension); import validate from 'celebrate'; - +/** + * Methods to get historic flood reports from database + * @alias module:src/api/reports/archive/index + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} api Express router object for reports route + */ export default ({config, db, logger}) => { let api = Router(); // eslint-disable-line new-cap diff --git a/src/api/routes/reports/archive/model.js b/src/api/routes/reports/archive/model.js index c0aa34e..aed12c3 100644 --- a/src/api/routes/reports/archive/model.js +++ b/src/api/routes/reports/archive/model.js @@ -1,13 +1,18 @@ +/** + * CogniCity Server /reports/archive data model + * @module src/api/reports/model/archive/model + **/ import Promise from 'bluebird'; +/** + * Interact with historic report objects + * @alias module:src/api/reports/archive/model + * @param {Object} config Server configuration + * @param {Object} db PG Promise database instance + * @param {Object} logger Configured Winston logger instance + * @return {Object} Query results + */ export default (config, db, logger) => ({ - /** - * Return all reports within a defined time period, and optionally city - * @param {integer} start Timestamp as ISO 8601 string for start of window - * @param {string} end Timestamp as ISO 8601 string for end of window - * @param {string} city Optional, instance region code (e.g. 'jbd')\ - * @return {Object} Query result - */ all: (start, end, city) => new Promise((resolve, reject) => { // Setup query let query = `SELECT pkey, created_at, source, From 62f72adaadc6cc8abc488b660f78ba4cea334088 Mon Sep 17 00:00:00 2001 From: Tomas Date: Thu, 29 Jun 2017 16:40:07 -0400 Subject: [PATCH 135/160] updated JSDoc --- jsdoc/api_index.js.html | 4 +- jsdoc/api_routes_cards_index.js.html | 4 +- jsdoc/api_routes_cards_model.js.html | 6 +- jsdoc/api_routes_cities_index.js.html | 113 + jsdoc/api_routes_cities_model.js.html | 93 + jsdoc/api_routes_feeds_index.js.html | 148 + jsdoc/api_routes_feeds_model.js.html | 137 + jsdoc/api_routes_floodgauges_index.js.html | 133 + jsdoc/api_routes_floodgauges_model.js.html | 129 + jsdoc/api_routes_floods_index.js.html | 253 ++ jsdoc/api_routes_floods_model.js.html | 212 ++ jsdoc/api_routes_infrastructure_index.js.html | 4 +- jsdoc/api_routes_infrastructure_model.js.html | 8 +- .../api_routes_reports_archive_index.js.html | 4 +- .../api_routes_reports_archive_model.js.html | 8 +- jsdoc/api_routes_reports_index.js.html | 4 +- jsdoc/api_routes_reports_model.js.html | 6 +- jsdoc/config.js.html | 4 +- jsdoc/db%0ADatabase%20initializermodule_.html | 4 +- jsdoc/db.js.html | 4 +- jsdoc/global.html | 2851 +---------------- jsdoc/index.html | 4 +- jsdoc/index.js.html | 4 +- jsdoc/lib_cap.js.html | 4 +- jsdoc/lib_util.js.html | 4 +- jsdoc/module-lib_cap.html | 4 +- jsdoc/module-src_api_cards_index.html | 4 +- jsdoc/module-src_api_cards_model.html | 6 +- jsdoc/module-src_api_cities_index.html | 373 +++ jsdoc/module-src_api_cities_model.html | 373 +++ jsdoc/module-src_api_feeds_index.html | 373 +++ jsdoc/module-src_api_feeds_model.html | 373 +++ jsdoc/module-src_api_floodgauge_model.html | 172 + jsdoc/module-src_api_floodgauges_index.html | 373 +++ jsdoc/module-src_api_floodgauges_model.html | 373 +++ jsdoc/module-src_api_floods_index.html | 373 +++ jsdoc/module-src_api_floods_model.html | 373 +++ jsdoc/module-src_api_index.html | 4 +- .../module-src_api_infrastructure_index.html | 148 +- .../module-src_api_infrastructure_model.html | 205 +- .../module-src_api_reports_archive_index.html | 4 +- .../module-src_api_reports_archive_model.html | 373 +++ jsdoc/module-src_api_reports_index.html | 4 +- jsdoc/module-src_api_reports_model.html | 207 +- ...le%0AInitialize%20the%20servermodule_.html | 145 + ...erver%0ACore%20server%20modulemodule_.html | 230 +- jsdoc/server.js.html | 18 +- ...0CAP%20data%20format%20utilitymodule_.html | 4 +- jsdoc/test_testCAP.js.html | 4 +- ...test%20the%20_cards%20endpointmodule_.html | 4 +- jsdoc/test_testCards.js.html | 4 +- ...est%20the%20_cities%20endpointmodule_.html | 4 +- jsdoc/test_testCities.js.html | 4 +- ...%20the%20db%20utility%20modulemodule_.html | 4 +- jsdoc/test_testDB.js.html | 4 +- ...test%20the%20_feeds%20endpointmodule_.html | 4 +- jsdoc/test_testFeeds.js.html | 4 +- jsdoc/test_testFloodgauges.js.html | 4 +- ...est%20the%20_floods%20endpointmodule_.html | 4 +- jsdoc/test_testFloods.js.html | 4 +- ...0the%20_floodgauges%20endpointmodule_.html | 4 +- ...e%20_infrastructure%20endpointmodule_.html | 4 +- jsdoc/test_testInfrastructure.js.html | 4 +- ...st%20the%20_reports%20endpointmodule_.html | 4 +- jsdoc/test_testReports.js.html | 4 +- ...%20_reports_archive%20endpointmodule_.html | 4 +- jsdoc/test_testReportsArchive.js.html | 4 +- ...20routes%20from%20the%20servermodule_.html | 4 +- jsdoc/test_testServer.js.html | 4 +- src/api/routes/cards/model.js | 2 +- src/api/routes/cities/index.js | 13 +- src/api/routes/cities/model.js | 13 +- src/api/routes/feeds/index.js | 15 +- src/api/routes/feeds/model.js | 14 +- src/api/routes/floodgauges/index.js | 15 +- src/api/routes/floodgauges/model.js | 13 +- src/api/routes/floods/index.js | 12 + src/api/routes/floods/model.js | 16 +- src/api/routes/infrastructure/model.js | 4 +- src/api/routes/reports/archive/model.js | 4 +- src/api/routes/reports/model.js | 2 +- src/server.js | 14 +- 82 files changed, 5643 insertions(+), 3235 deletions(-) create mode 100644 jsdoc/api_routes_cities_index.js.html create mode 100644 jsdoc/api_routes_cities_model.js.html create mode 100644 jsdoc/api_routes_feeds_index.js.html create mode 100644 jsdoc/api_routes_feeds_model.js.html create mode 100644 jsdoc/api_routes_floodgauges_index.js.html create mode 100644 jsdoc/api_routes_floodgauges_model.js.html create mode 100644 jsdoc/api_routes_floods_index.js.html create mode 100644 jsdoc/api_routes_floods_model.js.html create mode 100644 jsdoc/module-src_api_cities_index.html create mode 100644 jsdoc/module-src_api_cities_model.html create mode 100644 jsdoc/module-src_api_feeds_index.html create mode 100644 jsdoc/module-src_api_feeds_model.html create mode 100644 jsdoc/module-src_api_floodgauge_model.html create mode 100644 jsdoc/module-src_api_floodgauges_index.html create mode 100644 jsdoc/module-src_api_floodgauges_model.html create mode 100644 jsdoc/module-src_api_floods_index.html create mode 100644 jsdoc/module-src_api_floods_model.html create mode 100644 jsdoc/module-src_api_reports_archive_model.html create mode 100644 jsdoc/server%0ACore%20server%20module%0AInitialize%20the%20servermodule_.html diff --git a/jsdoc/api_index.js.html b/jsdoc/api_index.js.html index 9737ac5..5a76423 100644 --- a/jsdoc/api_index.js.html +++ b/jsdoc/api_index.js.html @@ -100,7 +100,7 @@

Source: api/index.js

)CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/jsdoc/fonts/OpenSans-Regular-webfont.eot b/jsdoc/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/fonts/OpenSans-Regular-webfont.woff b/jsdoc/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/jsdoc/global.html b/jsdoc/global.html new file mode 100644 index 0000000..bbea8a5 --- /dev/null +++ b/jsdoc/global.html @@ -0,0 +1,3166 @@ + + + + + JSDoc: Global + + + + + + + + + + +

)CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.eot b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.woff b/jsdoc/cognicity-server/3.0.1/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/jsdoc/cognicity-server/3.0.1/index.html b/jsdoc/cognicity-server/3.0.1/index.html new file mode 100644 index 0000000..a81a5b3 --- /dev/null +++ b/jsdoc/cognicity-server/3.0.1/index.html @@ -0,0 +1,273 @@ + + + + + JSDoc: Home + + + + + + + + + + +