1
0
mirror of /repos/Prototyper.git synced 2025-12-30 06:31:32 +01:00

refactored data backend

This commit is contained in:
Aiko Mastboom 2015-07-03 17:18:53 +02:00
parent 1bf77533b5
commit c43359c69e
5 changed files with 306 additions and 516 deletions

156
lib/data.js Normal file
View File

@ -0,0 +1,156 @@
'use strict';
var _ = require('underscore');
module.exports = function (config, dataBase, updateShare) {
var ensuring = false;
function ensureContent(options, callback) {
if (ensuring) {
//noinspection JSUnresolvedFunction
setImmediate(function rescheduling() {
config.debug && config.debug('Ensuring, rescheduling', options);
return ensureContent(options, callback);
});
} else {
ensuring = true;
if (!options.query) {
options.query = {
name: options.name
};
}
var stopEnsuring = function (err, result, col) {
config.debug && config.debug('Stop ensuring', options);
ensuring = false;
if (err) {
return callback && callback(err);
}
return callback && callback(null, result, col);
};
return getRethinkContent(options, function document(err, result, col) {
if (err) {
if (/Data not found*/.test(err.message)) {
var documentId = 'json:' + options.collection + ':' + options.name;
var data = {name: options.name};
return dataBase.setRethinkContent(data, options, function (err, content_result, col) {
var keys = _.keys(data); // reset all attributes;
return updateShare.document(documentId, data, keys, function updatedShareDocument() {
stopEnsuring(err, content_result, col);
});
});
} else {
return stopEnsuring(err);
}
} else {
return stopEnsuring(null, result, col);
}
});
}
}
/* options:
* no_share (optional): prevent share from updating itself.
*/
function setRethinkAttribute(data, options, callback) {
config.debug && config.debug('setMongoAttribute options', options);
return ensureContent(options, function document(err, result, col) {
if (err) {
config.error && config.error('ERR1 setMongoAttribute', err);
return callback && callback(err);
}
var attribute_options = {
collection: options.collection,
name: result.name + '.' + options.attribute
};
if (result.hasOwnProperty(options.attribute) &&
result[options.attribute].guid) {
attribute_options.query = {
id: result[options.attribute].guid
};
} else {
attribute_options.query = {
parent: result.id,
name: result.name + '.' + options.attribute
};
}
config.debug && config.debug('getMongoAttribute parent found, get child and save', result, attribute_options);
return ensureContent(attribute_options, function attribute(err, attribute_result) {
if (err) {
config.error && config.error('ERR2 setMongoAttribute ensureContent', err);
return callback && callback(err);
}
var updateContent = true;
if (result[options.attribute]) {
if (attribute_result.id === String(result[options.attribute].guid)) {
updateContent = false;
} else {
result[options.attribute].guid = attribute_result.id;
}
} else {
result[options.attribute] = {guid: attribute_result.id};
}
attribute_result.parent = result.id;
attribute_result.name = result.name + '.' + options.attribute;
attribute_result[options.attribute] = data;
if (options.operation) {
attribute_result.version = options.operation.v;
}
return dataBase.saveData(col, attribute_result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var documentId = 'json:' + options.collection + ':' + result.name;
var type = options.type || 'text';
var attributeDocumentId = type + ':' + options.collection + ':' + result.name + ':' + options.attribute;
var keys = null;
var share_data = null;
if (type === 'json') {
keys = _.keys(attribute_result); // reset all attributes;
share_data = attribute_result;
} else {
keys = [attribute_result.name];
share_data = data;
}
if (options.no_share) {
keys = null;
}
return updateShare.document(attributeDocumentId, share_data, keys, function updatedShareAttribute() {
if (updateContent) {
dataBase.updateData(col, result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var path = [options.attribute, 'guid']; // reset just guid attribute;
return updateShare.documentPath(documentId, result, path, function updatedShareContent() {
return callback && callback(null, attribute_result, col);
});
});
} else {
return callback && callback(null, attribute_result, col);
}
});
});
});
});
}
return {
getMongoAttribute: dataBase.getAttribute,
getMongoContent: dataBase.getContent,
setMongoAttribute: setRethinkAttribute,
setMongoContent: dataBase.setContent,
getAttribute: dataBase.getAttribute,
getContent: dataBase.getContent,
setAttribute: setRethinkAttribute,
setContent: dataBase.setContent,
ensureContent: ensureContent
};
};

View File

@ -3,7 +3,7 @@ var ObjectID = require('mongodb').ObjectID;
var _ = require('underscore');
module.exports = function (config, db, shareModel) {
module.exports = function (config, db) {
/*
* options:
* collection (mandatory)
@ -178,7 +178,7 @@ module.exports = function (config, db, shareModel) {
config.error && config.error('ERR3 setMongoContent', err);
return callback && callback(err);
}
if (result) {
if (result && result._id) {
data._id = result._id;
}
return dumpData(collection, data, callback);
@ -189,251 +189,11 @@ module.exports = function (config, db, shareModel) {
});
}
function updateShareDocumentPath(documentId, data, path, callback) {
shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN setMongoAttribute updateShareDocumentPath shareModel.getSnapshot', documentId, err);
return callback && callback();
}
var sub_data = data;
var sub_snapshot_data = doc.snapshot;
var equal_path = [];
var found = false;
var x;
for (x = 0; !found && x < path.length; x += 1) {
var key = path[x];
if (sub_data && sub_data.hasOwnProperty(key) &&
sub_snapshot_data &&
sub_snapshot_data.hasOwnProperty(key)) {
sub_data = sub_data[key];
sub_snapshot_data = sub_snapshot_data[key];
equal_path.push(key);
} else if (!sub_snapshot_data || !sub_snapshot_data.hasOwnProperty(key)) {
found = true;
}
}
if (found) {
path = equal_path;
}
var op = {
p: path
};
if (sub_data) {
op.oi = sub_data;
}
if (sub_snapshot_data) {
op.od = sub_snapshot_data;
}
return shareModel.applyOp(documentId, {
op: [op],
v: doc.v
}, function (err, result) {
if (err) {
config.error && config.error('ERR updateShareDocumentPath shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocumentPath shareModel.applyOp', documentId, op, err, result);
return callback && callback();
});
});
}
function updateShareDocument(documentId, data, keys, callback) {
if (!keys) {
// no keys no update
return callback && callback();
}
var ops = [];
return shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.getSnapshot', documentId, err);
return callback && callback();
}
if (doc.type.name === 'text') {
ops.push({
d: doc.snapshot,
p: 0
});
ops.push({
i: data,
p: 0
});
} else if (doc.type.name === 'json') {
_.forEach(keys, function (key) {
if (key !== '_id') {
var op = {
p: [key]
};
if (doc.snapshot[key]) {
op.od = doc.snapshot[key];
}
if (data[key]) {
op.oi = data[key];
}
ops.push(op);
}
});
}
return shareModel.applyOp(documentId, {
op: ops,
v: doc.v
}, function (err, result) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocument shareModel.applyOp', documentId, ops, err, result);
return callback && callback();
});
});
}
var ensuring = false;
function ensureContent(options, callback) {
if (ensuring) {
//noinspection JSUnresolvedFunction
setImmediate(function rescheduling() {
config.debug && config.debug('Ensuring, rescheduling', options);
return ensureContent(options, callback);
});
} else {
ensuring = true;
if (!options.query) {
options.query = {
name: options.name
};
}
var stopEnsuring = function (err, result, col) {
config.debug && config.debug('Stop ensuring', options);
ensuring = false;
if (err) {
return callback && callback(err);
}
return callback && callback(null, result, col);
};
getMongoContent(options, function document(err, result, col) {
if (err) {
if (/Data not found*/.test(err.message)) {
var documentId = 'json:' + options.collection + ':' + options.name;
var data = {name: options.name};
setMongoContent(data, options, function (err, content_result, col) {
var keys = _.keys(data); // reset all attributes;
return updateShareDocument(documentId, data, keys, function updatedShareDocument() {
stopEnsuring(err, content_result, col);
});
});
} else {
return stopEnsuring(err);
}
} else {
return stopEnsuring(null, result, col);
}
});
}
}
/* options:
* no_share (optional): prevent share from updating itself.
*/
function setMongoAttribute(data, options, callback) {
config.debug && config.debug('setMongoAttribute options', options);
ensureContent(options, function document(err, result, col) {
if (err) {
config.error && config.error('ERR1 setMongoAttribute', err);
return callback && callback(err);
}
var attribute_options = {
collection: options.collection,
name: result.name + '.' + options.attribute
};
if (result.hasOwnProperty(options.attribute) &&
result[options.attribute].guid) {
attribute_options.query = {
_id: result[options.attribute].guid
};
} else {
attribute_options.query = {
parent: result._id,
name: result.name + '.' + options.attribute
};
}
config.debug && config.debug('getMongoAttribute parent found, get child and save', result, attribute_options);
return ensureContent(attribute_options, function attribute(err, attribute_result) {
if (err) {
config.error && config.error('ERR2 setMongoAttribute ensureContent', err);
return callback && callback(err);
}
var updateContent = true;
if (result[options.attribute]) {
if (attribute_result._id.toString() === String(result[options.attribute].guid)) {
updateContent = false;
} else {
result[options.attribute].guid = attribute_result._id;
}
} else {
result[options.attribute] = {guid: attribute_result._id};
}
attribute_result.parent = result._id;
attribute_result.name = result.name + '.' + options.attribute;
attribute_result[options.attribute] = data;
if (options.operation) {
attribute_result.version = options.operation.v;
}
return saveData(col, attribute_result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var documentId = 'json:' + options.collection + ':' + result.name;
var type = options.type || 'text';
var attributeDocumentId = type + ':' + options.collection + ':' + result.name + ':' + options.attribute;
var keys = null;
var share_data = null;
if (type === 'json') {
keys = _.keys(attribute_result); // reset all attributes;
share_data = attribute_result;
} else {
keys = [attribute_result.name];
share_data = data;
}
if (options.no_share) {
keys = null;
}
return updateShareDocument(attributeDocumentId, share_data, keys, function updatedShareAttribute() {
if (updateContent) {
updateData(col, result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var path = [options.attribute, 'guid']; // reset just guid attribute;
return updateShareDocumentPath(documentId, result, path, function updatedShareContent() {
return callback && callback(null, attribute_result, col);
});
});
} else {
return callback && callback(null, attribute_result, col);
}
});
});
});
});
}
return {
getMongoAttribute: getMongoAttribute,
getMongoContent: getMongoContent,
setMongoAttribute: setMongoAttribute,
setMongoContent: setMongoContent,
ensureContent: ensureContent,
updateShareDocument: updateShareDocument
saveData: saveData,
updateData: updateData,
getAttribute: getMongoAttribute,
getContent: getMongoContent,
setContent: setMongoContent
};
};

View File

@ -1,7 +1,7 @@
'use strict';
var _ = require('underscore');
module.exports = function (config, r, connection, shareModel) {
module.exports = function (config, r, connection) {
/*
* options:
* collection (mandatory)
@ -225,251 +225,11 @@ module.exports = function (config, r, connection, shareModel) {
});
}
function updateShareDocumentPath(documentId, data, path, callback) {
shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN updateShareDocumentPath shareModel.getSnapshot', documentId, err);
return callback && callback();
}
var sub_data = data;
var sub_snapshot_data = doc.snapshot;
var equal_path = [];
var found = false;
var x;
for (x = 0; !found && x < path.length; x += 1) {
var key = path[x];
if (sub_data && sub_data.hasOwnProperty(key) &&
sub_snapshot_data &&
sub_snapshot_data.hasOwnProperty(key)) {
sub_data = sub_data[key];
sub_snapshot_data = sub_snapshot_data[key];
equal_path.push(key);
} else if (!sub_snapshot_data || !sub_snapshot_data.hasOwnProperty(key)) {
found = true;
}
}
if (found) {
path = equal_path;
}
var op = {
p: path
};
if (sub_data) {
op.oi = sub_data;
}
if (sub_snapshot_data) {
op.od = sub_snapshot_data;
}
return shareModel.applyOp(documentId, {
op: [op],
v: doc.v
}, function (err, result) {
if (err) {
config.error && config.error('ERR updateShareDocumentPath shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocumentPath shareModel.applyOp', documentId, op, err, result);
return callback && callback();
});
});
}
function updateShareDocument(documentId, data, keys, callback) {
if (!keys) {
// no keys no update
return callback && callback();
}
var ops = [];
return shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.getSnapshot', documentId, err);
return callback && callback();
}
if (doc.type.name === 'text') {
ops.push({
d: doc.snapshot,
p: 0
});
ops.push({
i: data,
p: 0
});
} else if (doc.type.name === 'json') {
_.forEach(keys, function (key) {
if (key !== 'id') {
var op = {
p: [key]
};
if (doc.snapshot[key]) {
op.od = doc.snapshot[key];
}
if (data[key]) {
op.oi = data[key];
}
ops.push(op);
}
});
}
return shareModel.applyOp(documentId, {
op: ops,
v: doc.v
}, function (err, result) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocument shareModel.applyOp', documentId, ops, err, result);
return callback && callback();
});
});
}
var ensuring = false;
function ensureContent(options, callback) {
if (ensuring) {
//noinspection JSUnresolvedFunction
setImmediate(function rescheduling() {
config.debug && config.debug('Ensuring, rescheduling', options);
return ensureContent(options, callback);
});
} else {
ensuring = true;
if (!options.query) {
options.query = {
name: options.name
};
}
var stopEnsuring = function (err, result, col) {
config.debug && config.debug('Stop ensuring', options);
ensuring = false;
if (err) {
return callback && callback(err);
}
return callback && callback(null, result, col);
};
return getRethinkContent(options, function document(err, result, col) {
if (err) {
if (/Data not found*/.test(err.message)) {
var documentId = 'json:' + options.collection + ':' + options.name;
var data = {name: options.name};
return setRethinkContent(data, options, function (err, content_result, col) {
var keys = _.keys(data); // reset all attributes;
return updateShareDocument(documentId, data, keys, function updatedShareDocument() {
stopEnsuring(err, content_result, col);
});
});
} else {
return stopEnsuring(err);
}
} else {
return stopEnsuring(null, result, col);
}
});
}
}
/* options:
* no_share (optional): prevent share from updating itself.
*/
function setRethinkAttribute(data, options, callback) {
config.debug && config.debug('setMongoAttribute options', options);
return ensureContent(options, function document(err, result, col) {
if (err) {
config.error && config.error('ERR1 setMongoAttribute', err);
return callback && callback(err);
}
var attribute_options = {
collection: options.collection,
name: result.name + '.' + options.attribute
};
if (result.hasOwnProperty(options.attribute) &&
result[options.attribute].guid) {
attribute_options.query = {
id: result[options.attribute].guid
};
} else {
attribute_options.query = {
parent: result.id,
name: result.name + '.' + options.attribute
};
}
config.debug && config.debug('getMongoAttribute parent found, get child and save', result, attribute_options);
return ensureContent(attribute_options, function attribute(err, attribute_result) {
if (err) {
config.error && config.error('ERR2 setMongoAttribute ensureContent', err);
return callback && callback(err);
}
var updateContent = true;
if (result[options.attribute]) {
if (attribute_result.id === String(result[options.attribute].guid)) {
updateContent = false;
} else {
result[options.attribute].guid = attribute_result.id;
}
} else {
result[options.attribute] = {guid: attribute_result.id};
}
attribute_result.parent = result.id;
attribute_result.name = result.name + '.' + options.attribute;
attribute_result[options.attribute] = data;
if (options.operation) {
attribute_result.version = options.operation.v;
}
return saveData(col, attribute_result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var documentId = 'json:' + options.collection + ':' + result.name;
var type = options.type || 'text';
var attributeDocumentId = type + ':' + options.collection + ':' + result.name + ':' + options.attribute;
var keys = null;
var share_data = null;
if (type === 'json') {
keys = _.keys(attribute_result); // reset all attributes;
share_data = attribute_result;
} else {
keys = [attribute_result.name];
share_data = data;
}
if (options.no_share) {
keys = null;
}
return updateShareDocument(attributeDocumentId, share_data, keys, function updatedShareAttribute() {
if (updateContent) {
updateData(col, result, function saved(err) {
if (err) {
config.error && config.error('ERR3 setMongoAttribute', err);
return callback && callback(err);
}
var path = [options.attribute, 'guid']; // reset just guid attribute;
return updateShareDocumentPath(documentId, result, path, function updatedShareContent() {
return callback && callback(null, attribute_result, col);
});
});
} else {
return callback && callback(null, attribute_result, col);
}
});
});
});
});
}
return {
getMongoAttribute: getRethinkAttribute,
getMongoContent: getRethinkContent,
setMongoAttribute: setRethinkAttribute,
setMongoContent: setRethinkContent,
ensureContent: ensureContent,
updateShareDocument: updateShareDocument
saveData: saveData,
updateData: updateData,
getAttribute: getRethinkAttribute,
getContent: getRethinkContent,
setContent: setRethinkContent
};
};

110
lib/updateShareDocument.js Normal file
View File

@ -0,0 +1,110 @@
'use strict';
var _ = require('underscore');
module.exports = function (config, shareModel) {
function updateShareDocumentPath(documentId, data, path, callback) {
shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN updateShareDocumentPath shareModel.getSnapshot', documentId, err);
return callback && callback();
}
var sub_data = data;
var sub_snapshot_data = doc.snapshot;
var equal_path = [];
var found = false;
var x;
for (x = 0; !found && x < path.length; x += 1) {
var key = path[x];
if (sub_data && sub_data.hasOwnProperty(key) &&
sub_snapshot_data &&
sub_snapshot_data.hasOwnProperty(key)) {
sub_data = sub_data[key];
sub_snapshot_data = sub_snapshot_data[key];
equal_path.push(key);
} else if (!sub_snapshot_data || !sub_snapshot_data.hasOwnProperty(key)) {
found = true;
}
}
if (found) {
path = equal_path;
}
var op = {
p: path
};
if (sub_data) {
op.oi = sub_data;
}
if (sub_snapshot_data) {
op.od = sub_snapshot_data;
}
return shareModel.applyOp(documentId, {
op: [op],
v: doc.v
}, function (err, result) {
if (err) {
config.error && config.error('ERR updateShareDocumentPath shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocumentPath shareModel.applyOp', documentId, op, err, result);
return callback && callback();
});
});
}
function updateShareDocument(documentId, data, keys, callback) {
if (!keys) {
// no keys no update
return callback && callback();
}
var ops = [];
return shareModel.getSnapshot(documentId, function (err, doc) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.getSnapshot', documentId, err);
return callback && callback();
}
if (doc.type.name === 'text') {
ops.push({
d: doc.snapshot,
p: 0
});
ops.push({
i: data,
p: 0
});
} else if (doc.type.name === 'json') {
_.forEach(keys, function (key) {
if (key !== 'id') {
var op = {
p: [key]
};
if (doc.snapshot[key]) {
op.od = doc.snapshot[key];
}
if (data[key]) {
op.oi = data[key];
}
ops.push(op);
}
});
}
return shareModel.applyOp(documentId, {
op: ops,
v: doc.v
}, function (err, result) {
if (err) {
config.warn && config.warn('WARN updateShareDocument shareModel.applyOp', documentId, err);
return callback && callback();
}
config.debug && config.debug('updateShareDocument shareModel.applyOp', documentId, ops, err, result);
return callback && callback();
});
});
}
return {
document: updateShareDocument,
documentPath: updateShareDocumentPath
};
};

View File

@ -1,21 +1,23 @@
'use strict';
process.title = 'Prototyper';
var connect = require('connect');
var express = require('express');
var rethink = require('rethinkdb');
var addRoutes = require('./lib/routes.js');
var shareServer = require('./lib/share.js');
var shareHandlers = require('./lib/shareHandlers.js');
var rethinkData = require('./lib/rethinkData.js');
var preview = require('./lib/preview.js');
var importer = require('./lib/importer.js');
var handlers = require('./lib/handlers.js');
var markers = require('./lib/markers.js');
var helpers = require('./lib/helpers.js');
var connect = require('connect');
var express = require('express');
var rethink = require('rethinkdb');
var addRoutes = require('./lib/routes.js');
var shareServer = require('./lib/share.js');
var shareHandlers = require('./lib/shareHandlers.js');
var updateShareDocument = require('./lib/updateShareDocument.js');
var rethinkData = require('./lib/rethinkData.js');
var dataAccessor = require('./lib/data.js');
var preview = require('./lib/preview.js');
var importer = require('./lib/importer.js');
var handlers = require('./lib/handlers.js');
var markers = require('./lib/markers.js');
var helpers = require('./lib/helpers.js');
var config = {
debug: function () {
debug: function () {
if (process.env.DEBUG) {
var error = arguments[0] && arguments[0].message ||
arguments[1] && arguments[1].message ||
@ -30,7 +32,7 @@ var config = {
console.log(JSON.stringify(log));
}
},
info: function () {
info: function () {
var error = arguments[0] && arguments[0].message ||
arguments[1] && arguments[1].message ||
arguments[2] && arguments[2].message;
@ -43,7 +45,7 @@ var config = {
};
console.log(JSON.stringify(log));
},
warn: function () {
warn: function () {
var error = arguments[0] && arguments[0].message ||
arguments[1] && arguments[1].message ||
arguments[2] && arguments[2].message;
@ -56,7 +58,7 @@ var config = {
};
console.warn(JSON.stringify(log));
},
error: function () {
error: function () {
var error = arguments[0] && arguments[0].message ||
arguments[1] && arguments[1].message ||
arguments[2] && arguments[2].message;
@ -69,8 +71,8 @@ var config = {
};
console.error(JSON.stringify(log));
},
port: process.env.npm_package_config_port || 8000,
mongo: {
port: process.env.npm_package_config_port || 8000,
mongo: {
server: 'mongodb://localhost:27017/Prototyper',
options: {
db: {
@ -84,14 +86,14 @@ var config = {
},
savedelay: 200
},
rethink: {
rethink: {
server: {
host: 'rethinkdb.40n8.me',
port: 28015,
db: 'Prototyper'
}
},
share: {
share: {
sockjs: {
prefix: '',
response_limit: 128 * 1024,
@ -119,13 +121,13 @@ var config = {
// opsCollectionPerDoc: false
// }
},
api: {
api: {
content: '/content',
data: '/data',
preview: '/page',
importer: '/importer'
},
statics: {
statics: {
dev_favicon_path: __dirname + '/public/favicon_dev.ico',
importer_path: __dirname + '/public',
public_path: __dirname + '/public',
@ -216,7 +218,9 @@ rethink.connect(config.rethink.server, function connection_result(err, connectio
config.debug && config.debug('share attached');
var dataInstance = rethinkData(config, rethink, connection, model);
var updateShare = updateShareDocument(config, model);
var dataBaseInstance = rethinkData(config, rethink, connection);
var dataInstance = dataAccessor(config, dataBaseInstance, updateShare);
config.debug && config.debug('dataInstance initialized');