From e6cff862e34dce15fe67caeb85d7d999fe6ade45 Mon Sep 17 00:00:00 2001 From: m2-genz Date: Wed, 4 Nov 2015 10:27:28 -0800 Subject: [PATCH 1/2] Adding readonlymode and db filtering for the data explorer --- app.js | 53 ++++++++++++++------- config.template.js | 2 + routes/api.js | 8 +++- routes/index.js | 10 +++- views/partials/index.jade | 7 ++- views/partials/table.jade | 99 +++++++++++++++++++++------------------ 6 files changed, 110 insertions(+), 69 deletions(-) diff --git a/app.js b/app.js index 44094ea..c82b2ac 100644 --- a/app.js +++ b/app.js @@ -3,7 +3,8 @@ module.exports = function(config) { // Import var express = require('express'), routes = require('./routes'), - api = require('./routes/api')(config); + api = require('./routes/api')(config), + isReadOnlyMode = false; var app = module.exports = express(); @@ -17,6 +18,12 @@ module.exports = function(config) { } process.exit(1); }) + + if(typeof(config.isReadOnlyMode) !== "undefined") + { + isReadOnlyMode = config.isReadOnlyMode; + } + config.isReadOnlyMode = isReadOnlyMode; // Configuration app.configure(function(){ @@ -36,7 +43,8 @@ module.exports = function(config) { app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); - // Home + // Home + routes.config(config); app.get('/', routes.index); // Templates app.get('/partials/:name', routes.partials); @@ -44,28 +52,39 @@ module.exports = function(config) { // Get a list of databases and tables app.get('/api/databases/tables', api.databasesAndTables); // Add a database - app.post('/api/database/add', api.databaseAdd); + if(!isReadOnlyMode) { + app.post('/api/database/add', api.databaseAdd); + } // Add a table app.get('/api/databases', api.databases); - app.post('/api/table/add', api.tableAdd); + if(!isReadOnlyMode) { + app.post('/api/table/add', api.tableAdd); + } // Delete a database - app.post('/api/database/delete', api.databaseDelete); - // Delete a table - app.post('/api/table/delete', api.tableDelete); - app.post('/api/table/empty', api.tableEmpty); + if(!isReadOnlyMode) { + app.post('/api/database/delete', api.databaseDelete); + } + // Delete a table + if(!isReadOnlyMode) { + app.post('/api/table/delete', api.tableDelete); + app.post('/api/table/empty', api.tableEmpty); + } app.get('/api/table', api.table); app.get('/api/table/list/:order/:skip/:limit', api.table); app.get('/api/export/table', api.exportTable); - app.post('/api/import/table', api.importTable); - - app.post('/api/doc/delete', api.docDelete); - app.post('/api/doc/update', api.docUpdate); - app.post('/api/doc/insert', api.docInsert); - - app.post('/api/field/delete', api.fieldDelete); - app.post('/api/field/rename', api.fieldRename); - app.post('/api/field/add', api.fieldAdd); + + if(!isReadOnlyMode) { + app.post('/api/import/table', api.importTable); + + app.post('/api/doc/delete', api.docDelete); + app.post('/api/doc/update', api.docUpdate); + app.post('/api/doc/insert', api.docInsert); + + app.post('/api/field/delete', api.fieldDelete); + app.post('/api/field/rename', api.fieldRename); + app.post('/api/field/add', api.fieldAdd); + } // Redirect all others to the index // A 404 page is probably a better move diff --git a/config.template.js b/config.template.js index 20939f5..670e4a6 100644 --- a/config.template.js +++ b/config.template.js @@ -8,3 +8,5 @@ exports.expressPort = 3000; // Port used by express exports.debug = true; // Debug mode exports.network = '127.0.0.1' // Network the node app will run on +exports.isReadOnlyMode = false; +exports.filterDB = ".*"; diff --git a/routes/api.js b/routes/api.js index e127b2c..8e9e926 100644 --- a/routes/api.js +++ b/routes/api.js @@ -45,7 +45,9 @@ module.exports = function(config) { }) } else { - r.dbList().forEach(function(db) { + r.dbList().filter(function(dbName) { + return dbName.match(config.filterDB || '.*'); + }).forEach(function(db) { return r.expr({ x: [{ db: db, @@ -72,7 +74,9 @@ module.exports = function(config) { }) } exports.databases = function (req, res) { - r.dbList().run( connection, {timeFormat: 'raw'}, function(error, dbs) { + r.dbList().filter(function(dbName) { + return dbName.match(config.filterDB || '.*'); + }).run( connection, {timeFormat: 'raw'}, function(error, dbs) { if (error) handleError(error); res.json({ error: error, diff --git a/routes/index.js b/routes/index.js index a6eebad..407559e 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,8 +1,14 @@ +var appConfig = null; + +exports.config = function(config){ + appConfig = config; +}; + exports.index = function(req, res){ - res.render('index'); + res.render('index', {"isReadOnlyMode": appConfig.isReadOnlyMode}); }; exports.partials = function (req, res) { var name = req.params.name; - res.render('partials/' + name); + res.render('partials/' + name , {"isReadOnlyMode": appConfig.isReadOnlyMode}); }; diff --git a/views/partials/index.jade b/views/partials/index.jade index baf2c67..ae710e9 100644 --- a/views/partials/index.jade +++ b/views/partials/index.jade @@ -1,5 +1,6 @@ div.home(ng-switch="", on="status") div.actions + unless isReadOnlyMode a(href="/add/db") Add a database a(href="/add/table/{{db}}") Add a table @@ -18,13 +19,15 @@ div.home(ng-switch="", on="status") ul.database(ng-repeat="db in dbs") li.database div.database - a.meta_action(href="/delete/db/{{db.db}}") Delete + unless isReadOnlyMode + a.meta_action(href="/delete/db/{{db.db}}") Delete i.icon-th-large a.db_name.black.bold(href="/db/{{db.db}}") {{db.db}} ul.tables li.table(ng-repeat="table in db.tables") - a.meta_action(href="/delete/table/{{db.db}}/{{table}}") Delete + unless isReadOnlyMode + a.meta_action(href="/delete/table/{{db.db}}/{{table}}") Delete i.icon-th a.gray(href="/table/{{db.db}}/{{table}}") {{table}} diff --git a/views/partials/table.jade b/views/partials/table.jade index fbfc59c..1752977 100644 --- a/views/partials/table.jade +++ b/views/partials/table.jade @@ -1,11 +1,12 @@ div.table_view(ng-switch="", on="status") div.actions(ng-switch="", on="display == -1") + unless isReadOnlyMode a(href="doc/add/{{db}}/{{table}}") Add a document a(href="field/add/{{db}}/{{table}}") Add a field - a.meta_action(href="export/{{db}}/{{table}}") Export - a.meta_action(href="import/{{db}}/{{table}}") Import a.meta_action(href="empty/{{db}}/{{table}}") Empty a.meta_action(href="delete/table/{{db}}/{{table}}") Delete this table + a.meta_action(href="import/{{db}}/{{table}}") Import + a.meta_action(href="export/{{db}}/{{table}}") Export div.alert.hidden a.close(href="#", ng-click="hide($event)") × @@ -54,10 +55,11 @@ div.table_view(ng-switch="", on="status") div.field_container(ng-mousedown="stopPropagation($event)") span.field_span {{field.name}} div.center_edit_container - a(ng-click="renameFieldConfirm($index)") - i.icon-edit.center_edit - a(ng-click="deleteFieldConfirm($index)") - i.icon-trash.center_edit + unless isReadOnlyMode + a(ng-click="renameFieldConfirm($index)") + i.icon-edit.center_edit + a(ng-click="deleteFieldConfirm($index)") + i.icon-trash.center_edit a(href="table/{{db}}/{{table}}/0/{{limit}}/{{field.name}}/asc") i.icon-arrow-up.center_edit a(href="table/{{db}}/{{table}}/0/{{limit}}/{{field.name}}/desc") @@ -65,26 +67,27 @@ div.table_view(ng-switch="", on="status") - tr(ng-show="changeField == true") - th.op(colspan="{{fields.length+1}}", ng-switch="", on="operation") - div.confirm_delete.alert.no_border_top(ng-switch-when="delete") - h3.alert_title Delete a field - p Are you sure you want to delete the field - em {{fieldToChangeStr}} - | for - strong all - | documents ? - div.button_container - button.float.btn(ng-click='cancel()') Cancel - button.float.btn(ng-click='deleteField()') Delete - div.update_form_container.alert.no_border_top(ng-switch-when="rename") - h3.alert_title Rename the field {{fieldToChangeStr}} - div.doc_form_container - input(type="text", name="newFieldName", class="newFieldName", ng-model="newFieldName", ng-enter="renameField") - - div.button_container - button.float.btn(ng-click='cancel()') Cancel - button.float.btn(ng-click='renameField()') Update + unless isReadOnlyMode + tr(ng-show="changeField == true") + th.op(colspan="{{fields.length+1}}", ng-switch="", on="operation") + div.confirm_delete.alert.no_border_top(ng-switch-when="delete") + h3.alert_title Delete a field + p Are you sure you want to delete the field + em {{fieldToChangeStr}} + | for + strong all + | documents ? + div.button_container + button.float.btn(ng-click='cancel()') Cancel + button.float.btn(ng-click='deleteField()') Delete + div.update_form_container.alert.no_border_top(ng-switch-when="rename") + h3.alert_title Rename the field {{fieldToChangeStr}} + div.doc_form_container + input(type="text", name="newFieldName", class="newFieldName", ng-model="newFieldName", ng-enter="renameField") + + div.button_container + button.float.btn(ng-click='cancel()') Cancel + button.float.btn(ng-click='renameField()') Update tbody(ng-repeat="document in flattened_docs", ng-switch="", on="display == $index") @@ -92,28 +95,32 @@ div.table_view(ng-switch="", on="status") td.index_cell div.expand_vertical(ng-mousedown="resizeVertical($index, $event)") div.field_container(ng-mousedown="stopPropagation($event)") - span.to_hide {{$index+skip}} - a(ng-click="updateTrigger($index)") - i.icon-edit.float_edit - a(ng-click="deleteTrigger($index)") - i.icon-trash.float_edit + if isReadOnlyMode + span {{$index+skip}} + unless isReadOnlyMode + span.to_hide {{$index+skip}} + a(ng-click="updateTrigger($index)") + i.icon-edit.float_edit + a(ng-click="deleteTrigger($index)") + i.icon-trash.float_edit td.value_td(ng-repeat="field in document", class="col-{{$index}}") div.value_container(class="{{field.type}}") {{formatValue(field.value)}} - tr(ng-switch-when="true") - td.op(colspan="{{document.length+1}}", class="op-{{$index}}", ng-switch="", on="operation") - div.confirm_delete.alert.no_border_top(ng-switch-when="delete") - h3.alert_title Delete a document - p Are you sure you want to delete the document above? - div.button_container - button.float.btn(ng-click='cancel()') Cancel - button.float.btn(ng-click='deleteDoc($index)') Delete - div.update_form_container.alert.no_border_top(ng-switch-when="update") - h3.alert_title Update a document - div.doc_form_container(ng-model="form", ng-include='', src="'partials/doc_form_field'", onload="pushScope(documents[$index])") - - div.button_container - button.float.btn(ng-click='cancel()') Cancel - button.float.btn(ng-click='updateDoc()') Update + unless isReadOnlyMode + tr(ng-switch-when="true") + td.op(colspan="{{document.length+1}}", class="op-{{$index}}", ng-switch="", on="operation") + div.confirm_delete.alert.no_border_top(ng-switch-when="delete") + h3.alert_title Delete a document + p Are you sure you want to delete the document above? + div.button_container + button.float.btn(ng-click='cancel()') Cancel + button.float.btn(ng-click='deleteDoc($index)') Delete + div.update_form_container.alert.no_border_top(ng-switch-when="update") + h3.alert_title Update a document + div.doc_form_container(ng-model="form", ng-include='', src="'partials/doc_form_field'", onload="pushScope(documents[$index])") + + div.button_container + button.float.btn(ng-click='cancel()') Cancel + button.float.btn(ng-click='updateDoc()') Update div.link_previous_next(ng-switch="", on="skip > 0") From ced465712adb175384c98c8963569b5013688388 Mon Sep 17 00:00:00 2001 From: m2-genz Date: Wed, 4 Nov 2015 14:19:59 -0800 Subject: [PATCH 2/2] Sorted Keys are null for certain documents. --- routes/api.js | 106 ++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/routes/api.js b/routes/api.js index 8e9e926..8d1b4f0 100644 --- a/routes/api.js +++ b/routes/api.js @@ -871,41 +871,43 @@ module.exports = function(config) { function flattenKeys(keys, prefix, numDocs) { var result = []; - for(var i=0; i 0) || (hasNestedKeys === false)) { - result.push(prefix.concat(keys.sorted_keys[i])) - } - else { - var count = 0; - for(var type in keys.keys[keys.sorted_keys[i]].type) { - count += keys.keys[keys.sorted_keys[i]].type[type] - } - if (typeof keys.keys[keys.sorted_keys[i]].primitiveCount === 'number') { - count += keys.keys[keys.sorted_keys[i]].primitiveCount; - } - - if (count < numDocs) { - result.push(prefix.concat(keys.sorted_keys[i])) - } - } - - // Recursively add more fields - if ((keys.keys[keys.sorted_keys[i]].type.object != null) - && (keys.keys[keys.sorted_keys[i]].type.object > 0)) { - result.push.apply(result, flattenKeys(keys.keys[keys.sorted_keys[i]], prefix.concat([keys.sorted_keys[i]]), numDocs)) - } + if(keys.sorted_keys){ + for(var i=0; i 0) || (hasNestedKeys === false)) { + result.push(prefix.concat(keys.sorted_keys[i])) + } + else { + var count = 0; + for(var type in keys.keys[keys.sorted_keys[i]].type) { + count += keys.keys[keys.sorted_keys[i]].type[type] + } + if (typeof keys.keys[keys.sorted_keys[i]].primitiveCount === 'number') { + count += keys.keys[keys.sorted_keys[i]].primitiveCount; + } + + if (count < numDocs) { + result.push(prefix.concat(keys.sorted_keys[i])) + } + } + + // Recursively add more fields + if ((keys.keys[keys.sorted_keys[i]].type.object != null) + && (keys.keys[keys.sorted_keys[i]].type.object > 0)) { + result.push.apply(result, flattenKeys(keys.keys[keys.sorted_keys[i]], prefix.concat([keys.sorted_keys[i]]), numDocs)) + } + } } return result } @@ -913,22 +915,24 @@ module.exports = function(config) { var result = []; prefix = prefix || [] - for(var i=0; i 0)) { - - result.push({ - field: keys.sorted_keys[i], - prefix: prefix, - nested: nestedKeys( keys.keys[keys.sorted_keys[i]], prefix.concat([keys.sorted_keys[i]])) - }) - } - else { - result.push({ - field: keys.sorted_keys[i], - prefix: prefix - }) - } + if(keys.sorted_keys){ + for(var i=0; i 0)) { + + result.push({ + field: keys.sorted_keys[i], + prefix: prefix, + nested: nestedKeys( keys.keys[keys.sorted_keys[i]], prefix.concat([keys.sorted_keys[i]])) + }) + } + else { + result.push({ + field: keys.sorted_keys[i], + prefix: prefix + }) + } + } } return result }