├── .gitignore ├── .travis.yml ├── Dockerfile ├── README.md ├── app.js ├── docker-compose.yml ├── migrations ├── 1494711961629_create_chat_messages_table.js ├── 1494713525512_create_locations_table.js ├── 1494713602316_create_locations_archive_table.js ├── 1495574087854_create_gallery_table.js └── 1505762006946_add_name_and_color.js ├── package.json ├── routes ├── debug.js ├── gallery.js ├── index.js └── twitter.js ├── sql ├── retrieve_locations.sql ├── save_locations.sql └── save_messages.sql └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | services: 4 | - docker 5 | 6 | # Install non-default Docker Compose version 7 | before_install: 8 | - sudo rm /usr/local/bin/docker-compose 9 | - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose 10 | - chmod +x docker-compose 11 | - sudo mv docker-compose /usr/local/bin 12 | 13 | install: 14 | - travis_retry docker build -t $DOCKER_IMAGE_NAME -f ./Dockerfile . 15 | 16 | script: 17 | - docker-compose -f docker-compose.yml up -d 18 | - docker-compose down 19 | 20 | env: 21 | global: 22 | - DOCKER_IMAGE_NAME=criticalmapsapi_web 23 | - DOCKER_COMPOSE_VERSION=1.12.0 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6 2 | 3 | RUN apt-get update -y && \ 4 | apt-get upgrade -y && \ 5 | apt-get install -y git GraphicsMagick 6 | 7 | RUN mkdir -p /app/ 8 | WORKDIR /app/ 9 | 10 | RUN npm install -g node-pg-migrate pg --silent 11 | 12 | COPY package.json . 13 | RUN npm install 14 | 15 | COPY . . 16 | 17 | CMD npm start 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Critical Maps API 2 | 3 | [![Build Status](https://travis-ci.org/criticalmaps/criticalmaps-api.svg?branch=master)](https://travis-ci.org/criticalmaps/criticalmaps-api) 4 | [![Code Climate](https://codeclimate.com/github/criticalmaps/criticalmaps-api/badges/gpa.svg)](https://codeclimate.com/github/criticalmaps/criticalmaps-api) 5 | [![Test Coverage](https://codeclimate.com/github/criticalmaps/criticalmaps-api/badges/coverage.svg)](https://codeclimate.com/github/criticalmaps/criticalmaps-api/coverage) 6 | [![Dependency Status](https://gemnasium.com/criticalmaps/criticalmaps-api.svg)](https://gemnasium.com/criticalmaps/criticalmaps-api) 7 | 8 | ## Start development session with: 9 | 10 | ```docker-compose -f docker-compose.dev.yml up --build``` 11 | 12 | ### Api will be available under: 13 | http://localhost:3000 14 | 15 | ### Debugger will be available at: 16 | http://localhost:8080/?port=5858 17 | 18 | ### phpPgAdmin is at: 19 | http://localhost:8082/phppgadmin/ 20 | 21 | ## Migrations 22 | 23 | ``` 24 | docker build -t criticalmaps-db-migrations -f Dockerfile.migrations . && \ 25 | docker run \ 26 | -v $(pwd)/migrations/:/migrations/ \ 27 | -e DATABASE_URL=postgres://bla:bla@db/criticalmaps \ 28 | criticalmaps-db-migrations \ 29 | up 30 | ``` 31 | 32 | docker exec -ti $(docker ps | grep postgres | awk '{ print $1}') /bin/bash 33 | 34 | psql -d criticalmaps -U bla 35 | 36 | ## TODO 37 | cors header?? 38 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var pgp = require('pg-promise')(); 3 | var bodyParser = require('body-parser'); 4 | 5 | 6 | var app = express(); 7 | 8 | // connect to databases 9 | postgres_db = pgp({ 10 | host: process.env.POSTGRES_HOST, 11 | port: process.env.POSTGRES_PORT, 12 | database: process.env.POSTGRES_DB, 13 | user: process.env.POSTGRES_USER, 14 | password: process.env.POSTGRES_PASSWORD, 15 | poolSize: 8 16 | }); 17 | 18 | app.set('port', 80); 19 | 20 | app.use(bodyParser.json({ 21 | limit: '5mb' 22 | })); // Parses req.body json from html POST 23 | app.use(bodyParser.urlencoded({ 24 | limit: '5mb', 25 | extended: true 26 | })); // Parses urlencoded req.body, including extended syntax 27 | 28 | // app.configure('development', function() { 29 | // app.use(express.logger('dev')); 30 | // app.use(express.errorHandler({ 31 | // dumpExceptions: true, 32 | // showStack: true 33 | // })); 34 | // }); 35 | 36 | app.use('/postv2', require('./routes/index')); 37 | app.use('/exchange', require('./routes/index')); 38 | app.use('/', require('./routes/index')); 39 | 40 | app.use('/twitter', require('./routes/twitter')); 41 | app.use('/twitter/get.php', require('./routes/twitter')); 42 | 43 | app.use('/gallery', require('./routes/gallery')); 44 | 45 | app.use('/debug', require('./routes/debug')); 46 | 47 | 48 | if (!module.parent) { 49 | app.listen(app.get('port'), function() { 50 | console.log('Server started on port ' + app.get('port')); 51 | }) 52 | } 53 | 54 | module.exports = app; 55 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | db: 4 | image: postgres:9.6 5 | environment: 6 | POSTGRES_USER: bla 7 | POSTGRES_PASSWORD: bla 8 | POSTGRES_DB: criticalmaps 9 | ports: 10 | - "5433:5432" 11 | restart: always 12 | 13 | web: 14 | build: 15 | context: . 16 | dockerfile: Dockerfile 17 | ports: 18 | - "80:80" 19 | volumes: 20 | - .:/usr/src/app 21 | environment: 22 | NODE_ENV: production 23 | DEBUG: "*" 24 | DATABASE_URL: postgres://bla:bla@db:5433/criticalmaps 25 | links: 26 | - db 27 | restart: always 28 | -------------------------------------------------------------------------------- /migrations/1494711961629_create_chat_messages_table.js: -------------------------------------------------------------------------------- 1 | var sql = ` 2 | CREATE TABLE IF NOT EXISTS chat_messages ( 3 | message text NOT NULL, 4 | ip inet NOT NULL, 5 | created timestamp NOT NULL default CURRENT_TIMESTAMP, 6 | identifier varchar(40) NOT NULL, 7 | longitude integer DEFAULT NULL, 8 | latitude integer DEFAULT NULL 9 | ); 10 | ` 11 | 12 | exports.up = (pgm) => { 13 | pgm.sql(sql) 14 | }; 15 | -------------------------------------------------------------------------------- /migrations/1494713525512_create_locations_table.js: -------------------------------------------------------------------------------- 1 | var sql = ` 2 | CREATE TABLE IF NOT EXISTS locations ( 3 | id SERIAL, 4 | device varchar(40) UNIQUE NOT NULL, 5 | updated timestamp NOT NULL default CURRENT_TIMESTAMP, 6 | longitude integer DEFAULT NULL, 7 | latitude integer DEFAULT NULL 8 | ); 9 | ` 10 | 11 | exports.up = (pgm) => { 12 | pgm.sql(sql) 13 | }; 14 | -------------------------------------------------------------------------------- /migrations/1494713602316_create_locations_archive_table.js: -------------------------------------------------------------------------------- 1 | var sql = ` 2 | CREATE TABLE IF NOT EXISTS locations_archive ( 3 | id SERIAL, 4 | device varchar(40) NOT NULL, 5 | created timestamp NOT NULL default CURRENT_TIMESTAMP, 6 | longitude integer DEFAULT NULL, 7 | latitude integer DEFAULT NULL 8 | ); 9 | ` 10 | 11 | exports.up = (pgm) => { 12 | pgm.sql(sql) 13 | }; 14 | -------------------------------------------------------------------------------- /migrations/1495574087854_create_gallery_table.js: -------------------------------------------------------------------------------- 1 | var sql = ` 2 | CREATE TYPE review AS ENUM ('pending', 'aproved', 'rejected'); 3 | 4 | CREATE TABLE IF NOT EXISTS gallery ( 5 | id SERIAL, 6 | thumbnail bytea NOT NULL, 7 | image bytea NOT NULL, 8 | longitude integer DEFAULT NULL, 9 | latitude integer DEFAULT NULL, 10 | review_state review, 11 | ip inet NOT NULL 12 | ); 13 | ` 14 | 15 | exports.up = (pgm) => { 16 | pgm.sql(sql) 17 | }; 18 | -------------------------------------------------------------------------------- /migrations/1505762006946_add_name_and_color.js: -------------------------------------------------------------------------------- 1 | var sql = ` 2 | ALTER TABLE locations ADD COLUMN name VARCHAR DEFAULT ''; 3 | ALTER TABLE locations ADD COLUMN color varchar(6) DEFAULT NULL; 4 | ` 5 | 6 | exports.up = (pgm) => { 7 | pgm.sql(sql) 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "criticalmaps-api", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./app.js", 6 | "scripts": { 7 | "start": "pg-migrate up && node app.js", 8 | "test": "mocha test.js" 9 | }, 10 | "dependencies": { 11 | "body-parser": "^1.17.1", 12 | "express": "^4.15.2", 13 | "form-data": "^2.2.0", 14 | "gm": "^1.23.0", 15 | "image-size": "^0.5.4", 16 | "moment": "^2.18.1", 17 | "multer": "^1.3.0", 18 | "pg-large-object": "^2.0.0", 19 | "pg-promise": "^5.6.7", 20 | "twitter": "^1.7.0" 21 | }, 22 | "devDependencies": { 23 | "supertest-chai": "0.0.8", 24 | "chai-http": "^3.0.0", 25 | "chai": "^3.5.0", 26 | "mocha": "^3.3.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /routes/debug.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var os = require('os'); 4 | var counter = 0; 5 | 6 | router.get('/', function(req, res, next) { 7 | var response = ""; 8 | response += 'counter' + ' - ' + counter + '
'; 9 | response += 'os.hostname()' + ' - ' + os.hostname() + '
'; 10 | response += 'os.type()' + ' - ' + os.type() + '
'; 11 | response += 'os.platform()' + ' - ' + os.platform() + '
'; 12 | response += 'os.arch()' + ' - ' + os.arch() + '
'; 13 | response += 'os.release()' + ' - ' + os.release() + '
'; 14 | response += 'os.uptime()' + ' - ' + os.uptime() + '
'; 15 | response += 'os.loadavg()' + ' - ' + os.loadavg() + '
' 16 | response += 'os.totalmem()' + ' - ' + os.totalmem() + '
'; 17 | response += 'os.freemem()' + ' - ' + os.freemem() + '
'; 18 | response += 'os.cpus()' + ' - ' + os.cpus().length + '
'; 19 | response += 'os.networkInterfaces()' + ' - ' + os.networkInterfaces() + '
'; 20 | console.log(response); 21 | 22 | 23 | 24 | 25 | 26 | res.send(response); 27 | 28 | 29 | counter++; 30 | 31 | 32 | }); 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /routes/gallery.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var debug = require('debug')('http'); 4 | var QueryFile = require('pg-promise').QueryFile; 5 | 6 | var multer = require('multer'); 7 | var upload = multer({ 8 | dest: '/tmp/' 9 | }) 10 | var sizeOf = require('image-size'); 11 | var gm = require('gm'); 12 | var fs = require('fs'); 13 | 14 | var maxWidth = 100; 15 | 16 | var saveImage = function(req, res, next) { 17 | var pathImage = req.file.path; 18 | var pathThumbnail = req.file.path + "_thumb"; 19 | 20 | var dimensions = sizeOf(pathImage); 21 | 22 | var oldWidth = dimensions.width 23 | var oldHeight = dimensions.height 24 | 25 | var newWidth = maxWidth; 26 | var newHeight = Math.floor(oldHeight * (maxWidth / oldWidth)); 27 | 28 | gm(req.file.path) 29 | .resize(newWidth, newHeight) 30 | .noProfile() 31 | .write(pathThumbnail, function(err) { 32 | if (err) { 33 | return res.send("error " + err); 34 | } 35 | fs.readFile(pathImage, function(err, dataImage) { 36 | fs.readFile(pathThumbnail, function(err, dataThumbnail) { 37 | postgres_db.none('INSERT INTO gallery(image, thumbnail, review_state, ip, longitude, latitude) \ 38 | VALUES($1, $2, $3, $4, $5, $6)', [dataImage, dataThumbnail, 'pending', 39 | req.connection.remoteAddress.replace(/^.*:/, ''), 40 | JSON.parse(req.body.data).longitude, 41 | JSON.parse(req.body.data).latitude 42 | ]) 43 | .then(function() { 44 | res.send("success") 45 | // use http 200 46 | }) 47 | .catch(function(error) { 48 | res.send("error: " + error) 49 | console.log(error) 50 | 51 | // use http code 52 | }); 53 | }); 54 | }); 55 | }); 56 | } 57 | 58 | router.post('/', upload.single('uploaded_file'), saveImage); 59 | router.post('/post.php', upload.single('uploaded_file'), saveImage); 60 | 61 | router.get('/', function(req, res, next) { 62 | res.setHeader('Access-Control-Allow-Origin', '*'); 63 | postgres_db.any("SELECT id, longitude, latitude FROM gallery") 64 | .then(function(data) { 65 | res.send(data) 66 | }) 67 | }); 68 | 69 | router.get('/thumbnail/:id', function(req, res, next) { 70 | res.setHeader('Access-Control-Allow-Origin', '*'); 71 | postgres_db.one("SELECT thumbnail FROM gallery WHERE id=$1", [req.params.id]) 72 | .then(function(data) { 73 | console.log(data); 74 | res.writeHead(200, { 75 | 'Content-Type': 'image/jpeg' 76 | }); 77 | res.write(data.thumbnail); 78 | res.end(); 79 | }) 80 | }); 81 | 82 | router.get('/image/:id', function(req, res, next) { 83 | res.setHeader('Access-Control-Allow-Origin', '*'); 84 | postgres_db.one("SELECT image FROM gallery WHERE id=$1", [req.params.id]) 85 | .then(function(data) { 86 | console.log(data); 87 | res.writeHead(200, { 88 | 'Content-Type': 'image/jpeg' 89 | }); 90 | res.write(data.image); 91 | res.end(); 92 | }); 93 | }); 94 | 95 | 96 | module.exports = router; 97 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var debug = require('debug')('http'); 4 | var QueryFile = require('pg-promise').QueryFile; 5 | var moment = require('moment') 6 | 7 | var QUERY_FILE_SAVE_LOCATIONS = QueryFile('sql/save_locations.sql') 8 | var QUERY_FILE_RETRIEVE_LOCATIONS = QueryFile('sql/retrieve_locations.sql') 9 | var QUERY_FILE_SAVE_MESSAGES = QueryFile('sql/save_messages.sql') 10 | 11 | var mainExchange = function(req, res, next) { 12 | res.setHeader('Access-Control-Allow-Origin', '*'); 13 | postgres_db.tx(function(t1) { 14 | return t1.batch([ 15 | save_messages_batch(req, t1), 16 | save_own_location_task(req, t1) 17 | ]); 18 | }).then(function(data_dont_care) { 19 | postgres_db.tx(function(t1) { 20 | return t1.batch([ 21 | retrieve_other_locations(req, t1), 22 | retrieve_chat_messages(req, t1) 23 | ]) 24 | }).then(function(data) { 25 | var response_obj = { 26 | locations: {}, 27 | chatMessages: {} 28 | }; 29 | data[0].forEach(function(location_obj) { 30 | response_obj.locations[location_obj.device] = { 31 | "longitude": location_obj.longitude, 32 | "latitude": location_obj.latitude, 33 | "timestamp": Math.floor(moment(location_obj.updated).valueOf() / 1000), 34 | "name": location_obj.name, 35 | "color": location_obj.color 36 | } 37 | }); 38 | 39 | data[1].forEach(function(message_obj) { 40 | response_obj.chatMessages[message_obj.identifier] = { 41 | "message": message_obj.message, 42 | "timestamp": Math.floor(moment(message_obj.created).valueOf() / 1000) 43 | } 44 | }); 45 | res.json(response_obj); 46 | }).catch(function(error) { 47 | handle_error(error, res) 48 | }); 49 | }) 50 | .catch(function(error) { 51 | handle_error(error, res) 52 | }); 53 | } 54 | 55 | router.post('/', mainExchange); 56 | router.get('/', mainExchange); 57 | router.post('/postv2', mainExchange); 58 | 59 | var save_own_location_task = function(req, t) { 60 | if (req.body.hasOwnProperty("device") && req.body.hasOwnProperty("location")) { 61 | var longitude = req.body.location.longitude; 62 | var latitude = req.body.location.latitude; 63 | var device = req.body.device; 64 | var name = req.body.name; 65 | var color = req.body.color; 66 | return t.none(QUERY_FILE_SAVE_LOCATIONS, [device, longitude, latitude, name, color]); 67 | } else { 68 | return null; 69 | } 70 | } 71 | 72 | var retrieve_other_locations = function(req, t) { 73 | var device = req.body.device || "undefined"; 74 | return t.any(QUERY_FILE_RETRIEVE_LOCATIONS, [device]); 75 | } 76 | 77 | var save_messages_batch = function(req, t) { 78 | if (!req.body.hasOwnProperty("messages")) { 79 | return null; 80 | } 81 | 82 | req.body.messages.sort(function(a, b) { 83 | return a.timestamp - b.timestamp; 84 | }); 85 | 86 | var save_messages_batch = []; 87 | var delay_counter = 0; 88 | req.body.messages.forEach(function(message) { 89 | var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress.replace(/^.*:/, ''); 90 | var latitude; 91 | var longitude; 92 | 93 | if (req.body.hasOwnProperty("location")) { 94 | latitude = req.body.location.latitude 95 | longitude = req.body.location.longitude 96 | } 97 | 98 | save_messages_batch.push( 99 | t.none(QUERY_FILE_SAVE_MESSAGES, [ 100 | message.text, 101 | delay_counter, 102 | ip, 103 | message.identifier, 104 | longitude, 105 | latitude 106 | ]) 107 | ); 108 | delay_counter++; 109 | }) 110 | return save_messages_batch; 111 | } 112 | 113 | var retrieve_chat_messages = function(req, t) { 114 | return t.any("SELECT * FROM chat_messages WHERE created > (NOW() - '$1 minutes'::INTERVAL)", [30]); 115 | } 116 | 117 | var handle_error = function(error, res) { 118 | console.log("ERROR:", error.message || error); 119 | res.status(500).json({ 120 | error: error.message 121 | }); 122 | } 123 | 124 | module.exports = router; 125 | -------------------------------------------------------------------------------- /routes/twitter.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | var Twitter = require('twitter'); 5 | 6 | var twitterClient = new Twitter({ 7 | consumer_key: process.env.TWITTER_CONSUMER_KEY, 8 | consumer_secret: process.env.TWITTER_CONSUMER_SECRET, 9 | access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY, 10 | access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET 11 | }); 12 | 13 | var twitterHandler = function(req, res, next) { 14 | console.log("foo") 15 | 16 | twitterClient.get('search/tweets', { 17 | q: 'criticalmaps', 18 | count: 100 19 | }, function(error, tweets, response) { 20 | if (!error) { 21 | res.json(tweets); 22 | } else { 23 | res.send(error); 24 | } 25 | }); 26 | } 27 | 28 | router.get('/', twitterHandler); 29 | router.get('/get.php', twitterHandler); 30 | 31 | module.exports = router; 32 | -------------------------------------------------------------------------------- /sql/retrieve_locations.sql: -------------------------------------------------------------------------------- 1 | -- delete locations older than 5 minutes 2 | DELETE 3 | FROM locations 4 | WHERE updated <= (CURRENT_TIMESTAMP - '5 minutes'::INTERVAL); 5 | 6 | -- get locations within the last 5 minutes & 7 | -- select the one with the highest timestamp for each device & 8 | -- ignore own device 9 | SELECT * 10 | FROM locations 11 | WHERE updated > (CURRENT_TIMESTAMP - '5 minutes'::INTERVAL) 12 | AND device != $1; 13 | -------------------------------------------------------------------------------- /sql/save_locations.sql: -------------------------------------------------------------------------------- 1 | -- save own location 2 | -- if the device location already exists, override! 3 | INSERT INTO locations(device, longitude, latitude, name, color, updated) 4 | VALUES($1, 5 | $2, 6 | $3, 7 | $4, 8 | $5, 9 | CURRENT_TIMESTAMP) ON CONFLICT (device) DO 10 | UPDATE 11 | SET longitude = excluded.longitude, 12 | latitude = excluded.latitude, 13 | updated = CURRENT_TIMESTAMP; 14 | 15 | -- also save to locations archive 16 | -- INSERT INTO locations_archive(device, longitude, latitude) 17 | -- VALUES($1, 18 | -- $2, 19 | -- $3); 20 | -------------------------------------------------------------------------------- /sql/save_messages.sql: -------------------------------------------------------------------------------- 1 | -- insert message only if the indentifier doesnt exist yet 2 | INSERT INTO chat_messages (message, created, ip, identifier, longitude, latitude) 3 | SELECT $1, 4 | now() + '$2 seconds'::INTERVAL, 5 | $3, 6 | $4, 7 | $5, 8 | $6 9 | WHERE NOT EXISTS 10 | ( SELECT * 11 | FROM chat_messages 12 | WHERE identifier = $4 ) 13 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var chaiHttp = require('chai-http'); 3 | var expect = chai.expect; 4 | 5 | chai.use(chaiHttp); 6 | 7 | var endpoint = "http://localhost:80"; 8 | 9 | var test_user_A = { 10 | "device": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 11 | "location": { 12 | "longitude": 12345, 13 | "latitude": 54321 14 | } 15 | }; 16 | 17 | var test_user_B_location_1 = { 18 | "device": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 19 | "location": { 20 | "longitude": 1, 21 | "latitude": 1 22 | } 23 | }; 24 | 25 | var test_user_B_location_2 = { 26 | "device": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 27 | "location": { 28 | "longitude": 2, 29 | "latitude": 2 30 | } 31 | }; 32 | 33 | describe('test location logic', function() { 34 | it('should not return own location', function(done) { 35 | chai.request(endpoint) 36 | .post('/') 37 | .send(test_user_A) 38 | .end(function(err, res) { 39 | expect(err).to.be.null; 40 | expect(res).to.have.status(200); 41 | expect(res).to.be.json; 42 | console.log(res.body) 43 | expect(res.body.locations).to.not.include.keys(test_user_A.device); 44 | done(); 45 | }); 46 | }); 47 | 48 | it('should receive previous location from test user A', function(done) { 49 | chai.request(endpoint) 50 | .post('/') 51 | .send(test_user_B_location_1) 52 | .end(function(err, res) { 53 | expect(err).to.be.null; 54 | expect(res).to.have.status(200); 55 | expect(res).to.be.json; 56 | console.log(res.body) 57 | expect(res.body.locations[test_user_A.device]) 58 | .to.deep.equal(test_user_A.location) 59 | done(); 60 | }); 61 | }); 62 | 63 | it('should save new location test user B', function(done) { 64 | chai.request(endpoint) 65 | .post('/') 66 | .send(test_user_B_location_2) 67 | .end(function(err, res) { 68 | expect(err).to.be.null; 69 | expect(res).to.have.status(200); 70 | expect(res).to.be.json; 71 | done(); 72 | }); 73 | }); 74 | 75 | it('should receive new location from test user B', function(done) { 76 | chai.request(endpoint) 77 | .post('/') 78 | .send(test_user_A) 79 | .end(function(err, res) { 80 | expect(err).to.be.null; 81 | expect(res).to.have.status(200); 82 | expect(res).to.be.json; 83 | expect(res.body.locations[test_user_B_location_1.device]) 84 | .to.deep.equal(test_user_B_location_2.location) 85 | done(); 86 | }); 87 | }); 88 | 89 | 90 | // describe('test messaging logic', function() { 91 | // it('should not return own location', function(done) { 92 | // chai.request('http://' + host + ':' + port) 93 | // .post('/') 94 | // .send(test_user_A) 95 | // .end(function(err, res) { 96 | // expect(err).to.be.null; 97 | // expect(res).to.have.status(200); 98 | // expect(res).to.be.json; 99 | // console.log(res.body) 100 | // expect(res.body.locations).to.not.include.keys(test_user_A.device); 101 | // done(); 102 | // }); 103 | // }); 104 | 105 | }); 106 | --------------------------------------------------------------------------------