From e53679087b1be88fdafecce250822637e540f930 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Wed, 17 Aug 2016 17:56:42 +0300 Subject: [PATCH 01/11] Persist reports in memory --- bin/remotedev.js | 2 +- bin/server.js | 2 +- lib/{getOptions.js => options.js} | 2 ++ lib/store.js | 32 +++++++++++++++++++++++++++++++ lib/worker.js | 9 +++++++-- package.json | 2 ++ 6 files changed, 45 insertions(+), 4 deletions(-) rename lib/{getOptions.js => options.js} (82%) create mode 100644 lib/store.js diff --git a/bin/remotedev.js b/bin/remotedev.js index 1ea3d55..4dca3aa 100755 --- a/bin/remotedev.js +++ b/bin/remotedev.js @@ -4,7 +4,7 @@ var path = require('path'); var argv = require('minimist')(process.argv.slice(2)); var chalk = require('chalk'); var injectServer = require('./injectServer'); -var getOptions = require('./../lib/getOptions'); +var getOptions = require('./../lib/options'); function readFile(filePath) { return fs.readFileSync(path.resolve(process.cwd(), filePath), 'utf-8'); diff --git a/bin/server.js b/bin/server.js index 12b722e..7837158 100644 --- a/bin/server.js +++ b/bin/server.js @@ -1,6 +1,6 @@ var assign = require('object-assign'); var repeat = require('repeat-string'); -var getOptions = require('./../lib/getOptions'); +var getOptions = require('./../lib/options'); var getPort = require('getport'); var LOG_LEVEL_NONE = 0; diff --git a/lib/getOptions.js b/lib/options.js similarity index 82% rename from lib/getOptions.js rename to lib/options.js index 682f829..af9fef5 100644 --- a/lib/getOptions.js +++ b/lib/options.js @@ -8,6 +8,8 @@ module.exports = function getOptions(argv) { cert: argv.cert || process.env.npm_package_remotedev_cert || null, passphrase: argv.passphrase || process.env.npm_package_remotedev_passphrase || null }, + adapter: argv.adapter || process.env.npm_package_remotedev_adapter, + db: argv.adapter || process.env.npm_package_remotedev_db, logLevel: argv.logLevel || 3 }; } diff --git a/lib/store.js b/lib/store.js new file mode 100644 index 0000000..65bab62 --- /dev/null +++ b/lib/store.js @@ -0,0 +1,32 @@ +var uuid = require('node-uuid'); +var JSData = require('js-data'); + +var store; +var adapter; +var Report; + +function add(data) { + var obj = { + id: uuid.v4(), + type: data.type, + payload: data.payload + }; + if (!adapter) { + return new Promise(function(resolve) { + var report = Report.inject(obj); + return resolve(report); + }); + } + return Report.create(obj); +} + +function createStore(options) { + store = new JSData.DS(); + Report = store.defineResource('report'); + + return { + add: add + }; +} + +module.exports = createStore; diff --git a/lib/worker.js b/lib/worker.js index 2a9e51a..a2aea54 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -2,10 +2,12 @@ var path = require('path'); var app = require('express')(); var bodyParser = require('body-parser'); var cors = require('cors'); +var createStore = require('./store'); module.exports.run = function(worker) { var httpServer = worker.httpServer; var scServer = worker.scServer; + var store; httpServer.on('request', app); @@ -20,8 +22,11 @@ module.exports.run = function(worker) { app.use(bodyParser.json()); app.post('/', function(req, res) { if (!req.body) return res.status(404).end(); - scServer.exchange.publish('log', req.body); - res.send('OK'); + if (!store) store = createStore(worker.options); + store.add(req.body).then(function(r) { + scServer.exchange.publish('log', req.body); + res.send({ id: r.id }); + }); }); scServer.addMiddleware(scServer.MIDDLEWARE_EMIT, function (req, next) { diff --git a/package.json b/package.json index 593668d..a35364c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,9 @@ "ejs": "^2.4.1", "express": "^4.13.3", "getport": "^0.1.0", + "js-data": "^2.9.0", "minimist": "^1.2.0", + "node-uuid": "^1.4.0", "object-assign": "^4.0.0", "repeat-string": "^1.5.4", "semver": "^5.3.0", From ff4245d9652974eb7718f9a00888fc8a08688e12 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Wed, 17 Aug 2016 18:31:12 +0300 Subject: [PATCH 02/11] Return error in request --- lib/store.js | 11 +++++++++++ lib/worker.js | 12 ++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/store.js b/lib/store.js index 65bab62..49c6a9a 100644 --- a/lib/store.js +++ b/lib/store.js @@ -5,12 +5,23 @@ var store; var adapter; var Report; +function error(msg) { + return new Promise(function(resolve, reject) { + return resolve({ error: msg }); + }); +} + function add(data) { + if (!data.type || !data.payload) { + return error('Required parameters aren\'t specified.'); + } + var obj = { id: uuid.v4(), type: data.type, payload: data.payload }; + if (!adapter) { return new Promise(function(resolve) { var report = Report.inject(obj); diff --git a/lib/worker.js b/lib/worker.js index a2aea54..3eccacc 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -23,10 +23,14 @@ module.exports.run = function(worker) { app.post('/', function(req, res) { if (!req.body) return res.status(404).end(); if (!store) store = createStore(worker.options); - store.add(req.body).then(function(r) { - scServer.exchange.publish('log', req.body); - res.send({ id: r.id }); - }); + + switch(req.body.op) { + default: + store.add(req.body).then(function(r) { + scServer.exchange.publish('log', req.body); + res.send({ id: r.id, error: r.error }); + }); + } }); scServer.addMiddleware(scServer.MIDDLEWARE_EMIT, function (req, next) { From 0a8576ab867ea1e33dfe1e8dafd3191e1a8b50ea Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Wed, 17 Aug 2016 18:33:07 +0300 Subject: [PATCH 03/11] Get a report --- lib/store.js | 13 +++++++++++++ lib/worker.js | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/lib/store.js b/lib/store.js index 49c6a9a..8c46555 100644 --- a/lib/store.js +++ b/lib/store.js @@ -11,6 +11,18 @@ function error(msg) { }); } +function get(id) { + if (!id) return error('No id specified.'); + + if (!adapter) { + return new Promise(function(resolve) { + var report = Report.get(id); + return resolve(report); + }); + } + return Report.find(id); +} + function add(data) { if (!data.type || !data.payload) { return error('Required parameters aren\'t specified.'); @@ -36,6 +48,7 @@ function createStore(options) { Report = store.defineResource('report'); return { + get: get, add: add }; } diff --git a/lib/worker.js b/lib/worker.js index 3eccacc..0729265 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -25,6 +25,11 @@ module.exports.run = function(worker) { if (!store) store = createStore(worker.options); switch(req.body.op) { + case 'get': + store.get(req.body.id).then(function(r) { + res.send(r || {}); + }); + break; default: store.add(req.body).then(function(r) { scServer.exchange.publish('log', req.body); From 03ed564bce1943da20ce2d43ba13f7be197ce5a5 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Wed, 17 Aug 2016 18:37:52 +0300 Subject: [PATCH 04/11] Parse x-www-form-urlencoded data --- lib/worker.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/worker.js b/lib/worker.js index 0729265..5bfa4b4 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -20,6 +20,7 @@ module.exports.run = function(worker) { app.use(cors({ methods: 'POST' })); app.use(bodyParser.json()); + app.use(bodyParser.urlencoded({ extended: false })); app.post('/', function(req, res) { if (!req.body) return res.status(404).end(); if (!store) store = createStore(worker.options); From 426fc75610582bbcfad0a8d1cac575e400419326 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Fri, 19 Aug 2016 19:42:38 +0300 Subject: [PATCH 05/11] Add more data to the report --- lib/store.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/store.js b/lib/store.js index 8c46555..49f36ca 100644 --- a/lib/store.js +++ b/lib/store.js @@ -31,7 +31,14 @@ function add(data) { var obj = { id: uuid.v4(), type: data.type, - payload: data.payload + title: data.title, + description: data.description, + failed: data.failed, + payload: data.payload, + screen: data.screen, + user: data.user, + isLog: !!data.isLog, + added: Date.now() }; if (!adapter) { From b9de52d23fefb8734fc800f7476226b80cae486d Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Fri, 19 Aug 2016 19:43:36 +0300 Subject: [PATCH 06/11] List reports --- lib/store.js | 27 +++++++++++++++++++++++++++ lib/worker.js | 5 +++++ package.json | 1 + 3 files changed, 33 insertions(+) diff --git a/lib/store.js b/lib/store.js index 49f36ca..628225d 100644 --- a/lib/store.js +++ b/lib/store.js @@ -1,4 +1,5 @@ var uuid = require('node-uuid'); +var pick = require('lodash/pick'); var JSData = require('js-data'); var store; @@ -11,6 +12,31 @@ function error(msg) { }); } +function map(data, fields) { + if (!fields) return data; + return data.map(function(r) { + return pick(r, fields); + }); +} + +function listEvery(query) { + if (!adapter) { + return new Promise(function(resolve) { + var report = Report.filter(query); + return resolve(report); + }); + } + return Report.findAll(query); +} + +function list(query, fields) { + return new Promise(function(resolve) { + listEvery(query).then(function(data) { + return resolve(map(data, fields)); + }); + }); +} + function get(id) { if (!id) return error('No id specified.'); @@ -55,6 +81,7 @@ function createStore(options) { Report = store.defineResource('report'); return { + list: list, get: get, add: add }; diff --git a/lib/worker.js b/lib/worker.js index 5bfa4b4..3b9edcb 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -31,6 +31,11 @@ module.exports.run = function(worker) { res.send(r || {}); }); break; + case 'list': + store.list(req.body.query, req.body.fields).then(function(r) { + res.send(r); + }); + break; default: store.add(req.body).then(function(r) { scServer.exchange.publish('log', req.body); diff --git a/package.json b/package.json index a35364c..5712c41 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "express": "^4.13.3", "getport": "^0.1.0", "js-data": "^2.9.0", + "lodash": "^4.15.0", "minimist": "^1.2.0", "node-uuid": "^1.4.0", "object-assign": "^4.0.0", From 7986004ea2e34b733d2ac6a8b1ff5a24947160c3 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Sat, 3 Sep 2016 18:09:56 +0300 Subject: [PATCH 07/11] More data! --- lib/store.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/store.js b/lib/store.js index 628225d..9ec842a 100644 --- a/lib/store.js +++ b/lib/store.js @@ -53,16 +53,26 @@ function add(data) { if (!data.type || !data.payload) { return error('Required parameters aren\'t specified.'); } - + if (data.type !== 'ACTIONS' && data.type !== 'STATE') { + return error('Type ' + data.type + ' is not supported yet.'); + } + var obj = { id: uuid.v4(), type: data.type, title: data.title, description: data.description, - failed: data.failed, + action: data.action, payload: data.payload, - screen: data.screen, + preloadedState: data.preloadedState, + screenshot: data.screenshot, + version: data.version, + appId: data.appId, + userAgent: data.userAgent, user: data.user, + userId: typeof data.user === 'object' ? data.user.id : data.user, + meta: data.meta, + error: data.error, isLog: !!data.isLog, added: Date.now() }; From 9040cae21bcd1c7984c701da70c946f2421ed73b Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Sun, 11 Sep 2016 17:07:02 +0300 Subject: [PATCH 08/11] Data selector by base fields --- lib/store.js | 18 +++++++++++++----- lib/worker.js | 8 ++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/store.js b/lib/store.js index 9ec842a..ea65bd5 100644 --- a/lib/store.js +++ b/lib/store.js @@ -6,6 +6,8 @@ var store; var adapter; var Report; +var baseFields = ['id', 'title', 'added']; + function error(msg) { return new Promise(function(resolve, reject) { return resolve({ error: msg }); @@ -32,7 +34,7 @@ function listEvery(query) { function list(query, fields) { return new Promise(function(resolve) { listEvery(query).then(function(data) { - return resolve(map(data, fields)); + return resolve(map(data, fields || baseFields)); }); }); } @@ -60,7 +62,7 @@ function add(data) { var obj = { id: uuid.v4(), type: data.type, - title: data.title, + title: data.title || data.exception && data.exception.message || data.action, description: data.description, action: data.action, payload: data.payload, @@ -72,8 +74,7 @@ function add(data) { user: data.user, userId: typeof data.user === 'object' ? data.user.id : data.user, meta: data.meta, - error: data.error, - isLog: !!data.isLog, + exception: data.exception, added: Date.now() }; @@ -86,6 +87,10 @@ function add(data) { return Report.create(obj); } +function byBaseFields(data) { + return pick(data, baseFields); +} + function createStore(options) { store = new JSData.DS(); Report = store.defineResource('report'); @@ -93,7 +98,10 @@ function createStore(options) { return { list: list, get: get, - add: add + add: add, + selectors: { + byBaseFields: byBaseFields + } }; } diff --git a/lib/worker.js b/lib/worker.js index 3b9edcb..411d454 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -7,7 +7,7 @@ var createStore = require('./store'); module.exports.run = function(worker) { var httpServer = worker.httpServer; var scServer = worker.scServer; - var store; + var store = createStore(worker.options); httpServer.on('request', app); @@ -23,8 +23,6 @@ module.exports.run = function(worker) { app.use(bodyParser.urlencoded({ extended: false })); app.post('/', function(req, res) { if (!req.body) return res.status(404).end(); - if (!store) store = createStore(worker.options); - switch(req.body.op) { case 'get': store.get(req.body.id).then(function(r) { @@ -38,8 +36,10 @@ module.exports.run = function(worker) { break; default: store.add(req.body).then(function(r) { - scServer.exchange.publish('log', req.body); res.send({ id: r.id, error: r.error }); + scServer.exchange.publish('report', { + type: 'add', data: store.selectors.byBaseFields(r) + }); }); } }); From 2523b3e491bd4fb882ce83ea3bd20f3271fe8f48 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Sun, 11 Sep 2016 17:08:05 +0300 Subject: [PATCH 09/11] Send updates of reports via sockets --- lib/worker.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/worker.js b/lib/worker.js index 411d454..238d755 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -55,6 +55,15 @@ module.exports.run = function(worker) { next(); }); + scServer.addMiddleware(scServer.MIDDLEWARE_SUBSCRIBE, function (req, next) { + next(); + if (req.channel === 'report') { + store.list().then(function(data) { + req.socket.emit(req.channel, { type: 'list', data: data }); + }); + } + }); + scServer.on('connection', function(socket) { var channelToWatch, channelToEmit; socket.on('login', function (credentials, respond) { @@ -68,6 +77,11 @@ module.exports.run = function(worker) { }); respond(null, channelToWatch); }); + socket.on('getReport', function (id, respond) { + store.get(id).then(function(data) { + respond(null, data); + }); + }); socket.on('disconnect', function() { var channel = worker.exchange.channel('sc-' + socket.id); channel.unsubscribe(); channel.destroy(); From da18bb47d6950a8884af4f1c3ffec326873d6f23 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Sun, 11 Sep 2016 17:43:36 +0300 Subject: [PATCH 10/11] Support adapters --- lib/adapter.js | 5 +++++ lib/options.js | 2 +- lib/store.js | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 lib/adapter.js diff --git a/lib/adapter.js b/lib/adapter.js new file mode 100644 index 0000000..643e74d --- /dev/null +++ b/lib/adapter.js @@ -0,0 +1,5 @@ +function getAdapter(adapter) { + return require('js-data-' + adapter); +} + +module.exports = getAdapter; diff --git a/lib/options.js b/lib/options.js index af9fef5..b970e4b 100644 --- a/lib/options.js +++ b/lib/options.js @@ -9,7 +9,7 @@ module.exports = function getOptions(argv) { passphrase: argv.passphrase || process.env.npm_package_remotedev_passphrase || null }, adapter: argv.adapter || process.env.npm_package_remotedev_adapter, - db: argv.adapter || process.env.npm_package_remotedev_db, + dbOptions: argv.dbOptions || process.env.npm_package_remotedev_db, logLevel: argv.logLevel || 3 }; } diff --git a/lib/store.js b/lib/store.js index ea65bd5..b350c4d 100644 --- a/lib/store.js +++ b/lib/store.js @@ -1,6 +1,7 @@ var uuid = require('node-uuid'); var pick = require('lodash/pick'); var JSData = require('js-data'); +var getAdapter = require('./adapter'); var store; var adapter; @@ -92,7 +93,15 @@ function byBaseFields(data) { } function createStore(options) { + var adapterName = options.adapter; store = new JSData.DS(); + + if (adapterName) { + var DSAdapter = getAdapter(adapterName); + adapter = new DSAdapter(options.dbOptions); + store.registerAdapter(adapterName, adapter, { default: true }); + } + Report = store.defineResource('report'); return { From 9b1c292b1c0ce8fa42b58331185c3e84b18565b2 Mon Sep 17 00:00:00 2001 From: Zalmoxisus Date: Sun, 11 Sep 2016 18:53:37 +0300 Subject: [PATCH 11/11] Update the README --- README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a0c7c74..a9b0cfe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ RemoteDev Server ================ -Bridge for connecting [remotedev monitor app](https://github.com/zalmoxisus/remotedev-app) with [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools) or [RemoteDev](https://github.com/zalmoxisus/remotedev) using a local server. Running a local server is optional, you may use [remotedev.io](https://remotedev.io) server instead, which is by default. +Bridge for communicating with an application remotely via [Redux DevTools extension](https://github.com/zalmoxisus/redux-devtools-extension), [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools) or [RemoteDev](https://github.com/zalmoxisus/remotedev). Running your server is optional, you can use [remotedev.io](https://remotedev.io) instead. ### Installation @@ -19,7 +19,7 @@ npm install --save-dev remotedev-server } ``` -So, you can start local server by running `npm run remotedev`. +So, you can start remotedev server by running `npm run remotedev`. ##### Import in your `server.js` script you use for starting a development server: @@ -36,7 +36,11 @@ So, you can start remotedev server together with your dev server. remotedev --hostname=localhost --port=8000 ``` -Change `hostname` and `port` to the values you want. +### Connection settings + +Set `hostname` and `port` to the values you want. `hostname` by default is `localhost` and `port` is `8000`. + +To use WSS, set `protocol` argument to `https` and provide `key`, `cert` and `passphrase` arguments. ### Inject to React Native local server @@ -50,9 +54,7 @@ Change `hostname` and `port` to the values you want. The `injectserver` value can be `reactnative` or `macos` ([react-native-macos](https://github.com/ptmt/react-native-macos)), it used `reactnative` by default. -Then, we can start React Native server and RemoteDev server with one command: - -![Inject server](https://cloud.githubusercontent.com/assets/3001525/16925822/92b6b3ac-4d58-11e6-9f36-d57dac8892c4.png) +Then, we can start React Native server and RemoteDev server with one command (`npm start`). ##### Revert the injection @@ -68,6 +70,8 @@ Or just run `$(npm bin)/remotedev --revert`. ### Connect from Android device or emulator +> Note that if you're using `injectserver` argument explained above, this step is not necessary. + If you're running an Android 5.0+ device connected via USB or an Android emulator, use [adb command line tool](http://developer.android.com/tools/help/adb.html) to setup port forwarding from the device to your computer: ``` @@ -76,6 +80,24 @@ adb reverse tcp:8000 tcp:8000 If you're still use Android 4.0, you should use `10.0.2.2` (Genymotion: `10.0.3.2`) instead of `localhost` in [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools#storeconfigurestorejs) or [remotedev](https://github.com/zalmoxisus/remotedev#usage). +### Save reports and logs + +You can store reports via [`redux-remotedev`](https://github.com/zalmoxisus/redux-remotedev) and get them replicated with [Redux DevTools extension](https://github.com/zalmoxisus/redux-devtools-extension) or [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools). You can get action history right in the extension just by clicking the link from a report. + +Remotedev server is database agnostic. By default everything is stored in the memory, but you can persist data by specifying one of the jsData adapters above for `adapter` argument. Also you can add an `dbOptions` argument for database configuration. If not provided the default options will be used (for some adapters, like `sql`, it's required). You have to install the required adapter's npm package. + +| Storage | `adapter` | `dbOptions` argument example (optional) | install | +|-----------|-----------|------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------| +| Firebase | firebase | `{ basePath: 'https://my-app.firebase.io' }` | `npm install --save js-data-firebase` | +| HTTP | http | `{ basePath: 'https://my-rest-server/api' }` | `npm install --save js-data-http` | +| LevelUp | levelup | `'./db'` (the levelup "db" object will be available at "adapter.db") | `npm install --save js-data-levelup` | +| MongoDB | mongodb | `{ name: 'user', idAttribute: '_id', table: 'users' }` | `npm install --save js-data-mongodb` | +| MySQL | sql | `{ client: 'mysql', connection: { host: '123.45.67.890', user: 'ubuntu', password: 'welcome1234', database: 'db1' }` | `npm install --save js-data-sql` | +| Postgres | sql | `{ client: 'pg', connection: { host: '123.45.67.890', user: 'ubuntu', password: 'welcome1234', database: 'db1' }` | `npm install --save js-data-sql` | +| Redis | redis | See the configurable options for [`node_redis`](https://github.com/NodeRedis/node_redis) | `npm install --save js-data-redis` | +| RethinkDB | rethinkdb | `{ host: '123.456.68.987', db: 'my_db' }` | `npm install --save rethinkdbdash js-data-rethinkdb` | +| SQLite3 | sql | `{ client: 'sqlite3', connection: { host: '123.45.67.890', user: 'ubuntu', password: 'welcome1234', database: 'db1' }` | `npm install --save js-data-sql` | + ### License MIT