Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

issue #19735: Update backbone to 0.9.10 and backbone realtional to 0.8.0

  • Loading branch information...
commit 40be4977474610ffc561499e09837c52de729824 1 parent 194df4b
John Rogelstad authored
View
62 enyo-client/application/source/ext/datasource.js
@@ -19,7 +19,7 @@ white:true*/
@param {Object} query
@param {Object} options
*/
- fetch: function (options) {
+ fetch: function (collection, options) {
options = options ? _.clone(options) : {};
var that = this,
payload = {},
@@ -42,7 +42,7 @@ white:true*/
// currently dealing with two different protocols for response formatting
dataHash = response.data.rows ? JSON.parse(response.data.rows[0].fetch) : response.data;
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, collection, dataHash, options);
}
};
@@ -96,7 +96,7 @@ white:true*/
@param {Number} id
@param {Object} options
*/
- retrieveRecord: function (recordType, id, options) {
+ retrieveRecord: function (model, options) {
var that = this,
payload = {},
complete = function (response) {
@@ -126,13 +126,13 @@ white:true*/
// Handle success
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, model, dataHash, options);
}
};
payload.requestType = 'retrieveRecord';
- payload.recordType = recordType;
- payload.id = id;
+ payload.recordType = model.recordType;
+ payload.id = options.id || model.id;
payload.databaseType = options.databaseType;
payload.options = { context: options.context };
@@ -169,7 +169,7 @@ white:true*/
dataHash = response.data.rows ? JSON.parse(response.data.rows[0].commit_record) : response.data;
//dataHash = JSON.parse(response.data.rows[0].commit_record);
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, model, dataHash, options);
}
};
@@ -222,7 +222,7 @@ white:true*/
dataHash = response.data.rows ? JSON.parse(response.data.rows[0].dispatch) : response.data;
//dataHash = JSON.parse(response.data.rows[0].dispatch);
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, dataHash, options);
}
};
@@ -243,13 +243,13 @@ white:true*/
*/
resetPassword: function (id, options) {
var payload = {
- id: id
- },
- ajax = new enyo.Ajax({
- url: "/resetPassword",
- success: options ? options.success : undefined,
- error: options ? options.error : undefined
- });
+ id: id
+ },
+ ajax = new enyo.Ajax({
+ url: "/resetPassword",
+ success: options ? options.success : undefined,
+ error: options ? options.error : undefined
+ });
if (options.newUser) {
// we don't want to send false at all, because false turns
@@ -269,14 +269,14 @@ white:true*/
*/
changePassword: function (params, options) {
var payload = {
- oldPassword: params.oldPassword,
- newPassword: params.newPassword
- },
- ajax = new enyo.Ajax({
- url: "/changePassword",
- success: options ? options.success : undefined,
- error: options ? options.error : undefined
- });
+ oldPassword: params.oldPassword,
+ newPassword: params.newPassword
+ },
+ ajax = new enyo.Ajax({
+ url: "/changePassword",
+ success: options ? options.success : undefined,
+ error: options ? options.error : undefined
+ });
ajax.response(this.ajaxSuccess);
ajax.go(payload);
@@ -313,10 +313,10 @@ white:true*/
*/
sendEmail: function (payload, options) {
var ajax = new enyo.Ajax({
- url: "/email",
- success: options ? options.success : undefined,
- error: options ? options.error : undefined
- });
+ url: "/email",
+ success: options ? options.success : undefined,
+ error: options ? options.error : undefined
+ });
if (payload.body && !payload.text) {
// be flexible with the inputs. Node-emailer prefers the term text, but
@@ -337,10 +337,10 @@ white:true*/
*/
getExtensionList: function (options) {
var ajax = new enyo.Ajax({
- url: "/extensions",
- success: options ? options.success : undefined,
- error: options ? options.error : undefined
- });
+ url: "/extensions",
+ success: options ? options.success : undefined,
+ error: options ? options.error : undefined
+ });
ajax.response(this.ajaxSuccess);
ajax.go();
2  lib/backbone-x/lib/Backbone-relational
@@ -1 +1 @@
-Subproject commit b3dbb3c16ceb9f3f3dba96ac0587c7be9c6cc0f2
+Subproject commit fb837d3f3ebd9f44e25b5f514e934e4870631a25
2  lib/backbone-x/lib/backbone
@@ -1 +1 @@
-Subproject commit 863814e519e630806096aa3ddeef520afbb263ff
+Subproject commit 836a8cb4d8744fcd71d6ca227dda051a4e5cb325
View
11 lib/backbone-x/source/collection.js
@@ -223,14 +223,7 @@ white:true*/
//
options = options ? _.clone(options) :
this.orderAttribute ? { query: this.orderAttribute } : {};
- /*
- var that = this,
- success = options.success;
- options.success = function (resp) {
- XT.log("Successfully fetched:" + that.model.prototype.recordType, resp);
- if (success) { success(resp); }
- };
- */
+
return Backbone.Collection.prototype.fetch.call(this, options);
},
@@ -244,7 +237,7 @@ white:true*/
options.databaseType = model.model.prototype.databaseType;
if (method === 'read' && options.query.recordType && options.success) {
- return XT.dataSource.fetch(options);
+ return XT.dataSource.fetch(this, options);
}
return false;
View
19 lib/backbone-x/source/model.js
@@ -1118,7 +1118,7 @@ white:true*/
// Read
if (method === 'read' && recordType && id && options.success) {
- result = dataSource.retrieveRecord(recordType, id, options);
+ result = dataSource.retrieveRecord(this, options);
// Write
} else if (method === 'create' || method === 'update' || method === 'delete') {
@@ -1515,18 +1515,20 @@ white:true*/
Overload: Need to handle status here
*/
findOrCreate: function (attributes, options) {
- options = options ? _.clone(options) : {};
- // Try to find an instance of 'this' model type in the store
- var model = Backbone.Relational.store.find(this, attributes),
- K = XM.Model;
+ options = options ? options : {};
+ var parsedAttributes = (_.isObject(attributes) && options.parse && this.prototype.parse) ?
+ this.prototype.parse(attributes) : attributes;
+
+ // Try to find an instance of 'this' model type in the store
+ var model = Backbone.Relational.store.find(this, parsedAttributes);
// If we found an instance, update it with the data in 'item'; if not, create an instance
// (unless 'options.create' is false).
if (_.isObject(attributes)) {
- if (model) {
- model.setStatus(K.BUSY_FETCHING);
+ if (model && options.merge !== false) {
+ model.setStatus(XM.Model.BUSY_FETCHING);
model.set(attributes, options);
- } else if (options.create !== false) {
+ } else if (!model && options.create !== false) {
model = this.build(attributes, options);
}
}
@@ -1748,5 +1750,4 @@ white:true*/
if (options && options.isFetching) { this.status = XM.Model.BUSY_FETCHING; }
};
-
}());
View
21 node-datasource/lib/ext/datasource.js
@@ -14,7 +14,7 @@ white:true*/
@param {Object} query
@param {Object} options
*/
- fetch: function (options) {
+ fetch: function (collection, options) {
options = options ? _.clone(options) : {};
var that = this,
payload = {},
@@ -38,7 +38,12 @@ white:true*/
// Handle success
dataHash = JSON.parse(response.rows[0].fetch);
if (options && options.success) {
- options.success.call(that, dataHash);
+ if (collection) {
+ options.success.call(that, collection, dataHash, options);
+ // Support for legacy code
+ } else {
+ options.success.call(that, dataHash);
+ }
}
};
@@ -91,7 +96,7 @@ white:true*/
@param {Number} id
@param {Object} options
*/
- retrieveRecord: function (recordType, id, options) {
+ retrieveRecord: function (model, options) {
var that = this,
payload = {},
conn = X.options.globalDatabase,
@@ -121,12 +126,12 @@ white:true*/
// Handle success
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, model, dataHash, options);
}
};
- payload.recordType = recordType;
- payload.id = id;
+ payload.recordType = model.recordType;
+ payload.id = options.id || model.id;
payload.options = { context: options.context };
payload.username = options.username;
payload = JSON.stringify(payload);
@@ -163,7 +168,7 @@ white:true*/
// Handle ok or complete hash response
dataHash = JSON.parse(response.rows[0].commit_record);
if (options && options.success) {
- options.success.call(that, dataHash);
+ options.success.call(that, model, dataHash, options);
}
};
@@ -218,7 +223,7 @@ white:true*/
} catch (err) {
dataHash = response.rows[0].dispatch;
}
- options.success.call(that, dataHash);
+ options.success.call(that, dataHash, options);
}
};
payload = JSON.stringify(payload);
View
8 node-datasource/lib/ext/session.js
@@ -206,11 +206,11 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
// let the user of this dispatch be the global username of the user making the request
options.username = this.get("details").id;
if (payload.requestType === 'fetch') {
- XT.dataSource.fetch(options);
+ XT.dataSource.fetch(null, options);
} else if (payload.requestType === 'dispatch') {
XT.dataSource.dispatch(payload.className, payload.functionName, payload.parameters, options);
} else if (payload.requestType === 'retrieveRecord') {
- XT.dataSource.retrieveRecord(payload.recordType, payload.id, options);
+ XT.dataSource.retrieveRecord(payload, options);
} else if (payload.requestType === 'commitRecord') {
if (!payload.dataHash) { return this.set("error", "invalid commit"); }
// Passing payload through, but trick dataSource into thinking it's a Model:
@@ -303,7 +303,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
recordType: "XM.Session",
parameters: [
{ attribute: "id", value: this.get("id") || -1 },
- { attribute: "sid", value: this.get("sid") || -1 },
+ { attribute: "sid", value: this.get("sid") || -1 }
// TODO - This doesn't work.
// PostgreSQL error: Error: operator does not exist: bigint = date
//{ attribute: "created", value: this.get("created") || -1 }
@@ -325,7 +325,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
callback(err);
};
- XT.dataSource.fetch(validateOptions);
+ XT.dataSource.fetch(null, validateOptions);
return this;
},
View
948 node-datasource/oauth2/db/connect-xt-pg.js
@@ -1,474 +1,476 @@
-/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
-regexp:true, undef:true, strict:true, trailing:true, white:true */
-/*global X:true, XM:true, XT:true, console:true, _:true*/
-
-/*!
- * Connect - xTuple - PostgreSQL Store
- *
- * connect-xt-pg is a PostgreSQL session store that uses xTuple's glabal database
- * to persist session data. To speed up session reads, Express's Session MemoryStore
- * is used as a cache.
- *
- * Modeled off of:
- * - https://github.com/visionmedia/connect-redis/blob/master/lib/connect-redis.js
- * - https://github.com/kcbanner/connect-mongo/blob/master/lib/connect-mongo.js
- * - https://github.com/jebas/connect-pg/blob/master/lib/connect-pg.js
- */
-
-
-/**
- * Return the `XTPGStore` extending `connect`'s session Store.
- *
- * @param {object} connect
- * @return {Function}
- * @api public
- */
-
-module.exports = function (connect) {
- "use strict";
-
- /**
- * Connect's Store.
- */
- var Store = connect.session.Store,
- // TODO - If we ever run multiple processes/servers, MemoryStore must be replaced with something
- // all processes/servers can use/share and keep in sync like Redis.
- MemoryStore = new connect.session.MemoryStore();
-
- /**
- * Initialize XTPGStore with the given `options`.
- *
- * @param {Object} options
- * @api public
- */
- function XTPGStore(options) {
- var self = this;
-
- options = options || {};
- Store.call(this, options);
- this.prefix = null == options.prefix ? 'sess:' : options.prefix;
- this.hybridCache = null == options.hybridCache ? false : options.hybridCache;
- this.sessions = {};
-
- // Load all the data from XM.SessionStore into the Express MemoryStore for caching.
- this.loadSessions = function (options, callback) {
- // TODO - options could be used to only load parital dataset of recently active sessions.
- // It could also be used to help process/server syncing if we move to something like Redis.
-
- var fetchOptions = {};
-
- fetchOptions.success = function (sessionstore) {
- var sid,
- sess;
-
- // Flush any sessions before reloading it.
- self.sessions = {};
-
- _.each(sessionstore, function (model, id, collection) {
- sid = model.id;
- sess = JSON.parse(model.session);
-
- if (self.hybridCache) {
- // Store the session data in the Express MemeoryStore for caching.
- MemoryStore.set(sid, sess, function () {});
- }
-
- self.sessions[sid] = sess;
- });
-
- // Now that sessions are loaded, we'll call the callback that was waiting for them.
- if (callback && typeof callback === 'function') {
- callback();
- }
- };
- fetchOptions.error = function (sessionstore, err) {
- X.debug("Session Collection fetch failed.");
-
- // Now that sessions are loaded, we'll call the callback that was waiting for them.
- if (callback && typeof callback === 'function') {
- callback("Session Collection fetch failed.");
- }
- };
-
- // TODO - This is REALLY SLOW if there are 10,000 sessions in the table and we use collection.fetch().
- // var sessionsCollection = new XM.SessionStoreCollection();
- //sessionsCollection.fetch(fetchOptions);
-
- // Fetch all records from XM.SessionStore and load them into the Express MemoryStore.
- fetchOptions.query = {
- requestType: "fetch",
- recordType: "XM.SessionStore"
- };
-
- // fetchOptions.username = GLOBAL_USERNAME; // TODO
- fetchOptions.username = 'node';
- XT.dataSource.fetch(fetchOptions);
- };
-
- // Loops through the sessions, find the ones that are expired and sends that session data
- // to the callback function. This allows us to expire session from code that has access to
- // stuff like socket.io in main.js.
- this.expireSessions = function (callback) {
- this.loadSessions(null, function (err) {
- if (err) {
- return;
- }
-
- _.each(self.sessions, function (val, key, list) {
- var expires = new Date(val.cookie.expires),
- now = new Date();
-
- if ((expires - now) <= 0) {
- //X.debug("Session: ", key, " expired ", (expires - now));
- callback(key, val);
- } else {
- //X.debug("Session: ", key, " expires in ", (expires - now));
- }
- });
- });
- };
-
- X.debug("XTPGStore SessionStore using hybridCache = ", this.hybridCache);
- // Prime this.sessions and MemoryCache on initialization.
- this.loadSessions();
- }
-
- /**
- * Inherit from `Store`.
- */
- XTPGStore.prototype = new Store();
-
- /**
- * Attempt to fetch session by the given `sid`.
- *
- * @param {String} sid
- * @param {Function} fn
- * @api public
- */
- XTPGStore.prototype.get = function (sid, done) {
- try {
- var fetchCache,
- fetchDB,
- that = this;
-
- sid = this.prefix + sid;
-
- fetchDB = function () {
- var fetchOptions = {},
- result,
- sessionStore = {};
-
- sessionStore = new XM.SessionStore();
- fetchOptions.id = sid;
-
- fetchOptions.success = function (model) {
- // We have a matching session store cookie
-
- // TODO - update lastModified time to extend timeout?
- // Doing this will complicate things with a hybridCache MemoryStore.
- // Do we really need to set that here for just in XM.Session and have CleanupTask
- // clean up XM.SessionStore on timeouts?
- //model.set("lastModified", new Date().getTime());
- //model.save(null, saveOptions);
-
- if (model.get("session")) {
- result = JSON.parse(model.get("session"));
- }
-
- if (that.hybridCache) {
- // We fetched from the database because this session was not in the MemoryStore.
- // Load this data into the MemoryStore to speed this up next time.
- MemoryStore.set(sid, result, function (err, cache) {
- if (err) {
- // Could not set. This shouldn't happen. Return error and move along...
- console.trace("MemoryStore.set error:");
- done && done(err);
- } else {
- // Return the session data back to Express to be used by routes and functors.
- return done(null, result);
- }
- });
- } else {
- // Return the session data back to Express to be used by routes and functors.
- return done(null, result);
- }
- };
- fetchOptions.error = function (model, err) {
- // Session was not found. This can happen if cookie is still in the browser, but
- // db record was removed by CleanupTask because it has timed out.
-
- // This is called when MemoryStore did not find session data.
- // No need to touch MemoryStore here.
-
- // Nothing found anywhere, no need to return the err, return nothing and move along...
- done && done();
- };
-
- // Try to fetch a session matching the user's cookie sid.
- sessionStore.fetch(fetchOptions);
- };
-
- if (this.hybridCache) {
- // Try to get session data from MemoryStore.
- fetchCache = function (err, cache) {
- if (cache && !err) {
- return done(null, cache);
- } else {
- fetchDB();
- }
- };
- MemoryStore.get(sid, fetchCache);
- } else {
- fetchDB();
- }
- } catch (err) {
- done && done(err);
- }
- };
-
- /**
- * Commit the given `sess` object associated with the given `sid`.
- *
- * @param {String} sid
- * @param {Session} sess
- * @param {Function} fn
- * @api public
- */
- XTPGStore.prototype.set = function (sid, sess, done) {
- try {
- var fetchCache,
- fetchDB,
- that = this;
-
- sid = this.prefix + sid;
- sess = JSON.stringify(sess);
-
- fetchDB = function () {
- var fetchOptions = {},
- saveOptions = {},
- sessionAttributes = {},
- sessionStore = {};
-
- sessionStore = new XM.SessionStore();
-
- saveOptions.success = function (model) {
- if (that.hybridCache) {
- // Overwrite the MemoryStore with the new data, replacing old or creating new entry.
- MemoryStore.set(sid, JSON.parse(sess), function (err, cache) {
- if (err) {
- // Could not set. Return error and move along.
- console.trace("MemoryStore.set error:");
- done && done(err);
- } else {
- // Success, MemoryStore updated, move along.
- done && done();
- }
- });
- } else {
- // Success, move along.
- done && done();
- }
- };
- saveOptions.error = function (model, err) {
- var that = this;
- // This shouldn't happen. How did we get here? Log trace.
- console.trace("XM.SessionStore save error. This shouldn't happen.");
-
- if (that.hybridCache) {
- // Delete any match in MemoryStore.
- MemoryStore.destroy(sid, function (err, cache) {
- if (err) {
- // Could not destroy. This shouldn't happen. Return error and move along.
- console.trace("MemoryStore.destroy error:");
- done && done(err);
- } else {
- // TODO - This might throw an error because our err object does not includes a stack.
- // https://github.com/senchalabs/connect/blob/master/lib/middleware/errorHandler.js#L48
- // MemoryStore destroyed, move along.
- done && done(that.err);
- }
- });
- } else {
- // TODO - This might throw an error because our err object does not includes a stack.
- // https://github.com/senchalabs/connect/blob/master/lib/middleware/errorHandler.js#L48
- // Return nothing and move along.
- done && done(err);
- }
- };
-
- fetchOptions.id = sid;
-
- fetchOptions.success = function (model) {
- // Fetch found this session, update it and save.
- model.set("session", sess);
-
- // Set gets called a lot. There isn't always a change to save and save will fail.
- // Check if this model has changed before trying to save it.
- if (model.getStatusString() === "READY_CLEAN") {
- if (that.hybridCache) {
- // MemoryStore did not have a matching session, update existing or add new.
- MemoryStore.set(sid, JSON.parse(sess), function (err, cache) {
- if (err) {
- // Could not set. This shouldn't happen. Return error and move along.
- console.trace("MemoryStore.set error:");
- done && done(err);
- } else {
- // Nothing to save, MemoryStore updated, move along.
- done && done();
- }
- });
- } else {
- // Nothing to save, move along.
- done && done();
- }
- } else if (model.getStatusString() === "READY_DIRTY") {
- // Try to save XM.SessionStore to database.
- model.save(null, saveOptions);
- }
- };
- fetchOptions.error = function (model, err) {
- // Fetch did not find this session, initialize new and save.
- // Create new XM.SessionStore object.
- sessionStore = new XM.SessionStore();
- // TODO - Is this redundant? Can I just call model.initialize...???
- sessionStore.initialize(null, {isNew: true});
-
- sessionAttributes = {
- id: sid,
- session: sess
- };
-
- // Try to save XM.SessionStore to database.
- sessionStore.save(sessionAttributes, saveOptions);
- };
-
- // Try to fetch a session matching the user's cookie sid to
- // see if we need to make a new one or update the old one.
- sessionStore.fetch(fetchOptions);
- };
-
- if (this.hybridCache) {
- // Try to get session data from MemoryStore.
- fetchCache = function (err, cache) {
- if (cache && !err) {
- if (JSON.stringify(cache, null, null) === sess) {
- // No change to the session data, nothing to save, move along.
- done && done();
- } else {
- fetchDB();
- }
- } else {
- fetchDB();
- }
- };
- MemoryStore.get(sid, fetchCache);
- } else {
- fetchDB();
- }
- } catch (err) {
- done && done(err);
- }
- };
-
- /**
- * Destroy the session associated with the given `sid`.
- *
- * @param {String} sid
- * @api public
- */
- XTPGStore.prototype.destroy = function (sid, done) {
- try {
- var sessionStore = {},
- fetchOptions = {},
- that = this;
-
- sid = this.prefix + sid;
- sessionStore = new XM.SessionStore();
- fetchOptions.id = sid;
-
- fetchOptions.success = function (model) {
- // Delete this session from the db store.
- model.destroy();
-
- if (that.hybridCache) {
- // Delete this session from the MemoryStore as well.
- MemoryStore.destroy(sid, function (err, cache) {
- if (err) {
- // Could not destroy. This shouldn't happen. Return error and move along.
- console.trace("MemoryStore.destroy error:");
- done && done(err);
- } else {
- // MemoryStore destroyed, move along.
- done && done();
- }
- });
- } else {
- done && done();
- }
- };
- fetchOptions.error = function (model, err) {
- if (that.hybridCache) {
- // Did not find a match in the db, delete this session from the MemoryStore, it's invalid.
- MemoryStore.destroy(sid, function (err, cache) {
- if (err) {
- // Could not destroy. This shouldn't happen. Return error and move along.
- console.trace("MemoryStore.destroy error:");
- done && done(err);
- } else {
- // MemoryStore destroyed. Return nothing and move along.
- done && done();
- }
- });
- } else {
- // Return nothing and move along.
- done && done();
- }
- };
-
- // Try to fetch a session matching the user's cookie sid.
- sessionStore.fetch(fetchOptions);
- } catch (err) {
- done && done(err);
- }
- };
-
- /**
- * Invoke the given callback `fn` with all active sessions.
- *
- * @param {Function} fn
- * @api public
- */
- XTPGStore.prototype.all = function (fn) {
- this.loadSessions(null, function (err) {
- if (err) {
- return fn(err);
- }
-
- var arr = [],
- keys = Object.keys(this.sessions);
-
- for (var i = 0, len = keys.length; i < len; ++i) {
- arr.push(this.sessions[keys[i]]);
- }
- fn(null, arr);
- });
- };
-
- /**
- * Fetch number of sessions.
- *
- * @param {Function} fn
- * @api public
- */
- XTPGStore.prototype.length = function (fn) {
- this.loadSessions(null, function (err) {
- if (err) {
- return fn(err);
- }
-
- fn(null, Object.keys(this.sessions).length);
- });
- };
-
- return XTPGStore;
+/*jshint node:true, indent:2, curly:false, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
+regexp:true, undef:true, strict:true, trailing:true, white:true */
+/*global X:true, XM:true, XT:true, console:true, _:true*/
+
+/*!
+ * Connect - xTuple - PostgreSQL Store
+ *
+ * connect-xt-pg is a PostgreSQL session store that uses xTuple's glabal database
+ * to persist session data. To speed up session reads, Express's Session MemoryStore
+ * is used as a cache.
+ *
+ * Modeled off of:
+ * - https://github.com/visionmedia/connect-redis/blob/master/lib/connect-redis.js
+ * - https://github.com/kcbanner/connect-mongo/blob/master/lib/connect-mongo.js
+ * - https://github.com/jebas/connect-pg/blob/master/lib/connect-pg.js
+ */
+
+
+/**
+ * Return the `XTPGStore` extending `connect`'s session Store.
+ *
+ * @param {object} connect
+ * @return {Function}
+ * @api public
+ */
+
+module.exports = function (connect) {
+ "use strict";
+
+ /**
+ * Connect's Store.
+ */
+ var Store = connect.session.Store,
+ // TODO - If we ever run multiple processes/servers, MemoryStore must be replaced with something
+ // all processes/servers can use/share and keep in sync like Redis.
+ MemoryStore = new connect.session.MemoryStore();
+
+ /**
+ * Initialize XTPGStore with the given `options`.
+ *
+ * @param {Object} options
+ * @api public
+ */
+ function XTPGStore(options) {
+ var self = this;
+
+ options = options || {};
+ Store.call(this, options);
+ this.prefix = null === options.prefix ? 'sess:' : options.prefix;
+ this.hybridCache = null === options.hybridCache ? false : options.hybridCache;
+ this.sessions = {};
+
+ // Load all the data from XM.SessionStore into the Express MemoryStore for caching.
+ this.loadSessions = function (options, callback) {
+ // TODO - options could be used to only load parital dataset of recently active sessions.
+ // It could also be used to help process/server syncing if we move to something like Redis.
+
+ var fetchOptions = {};
+
+ fetchOptions.success = function (sessionstore) {
+ var sid,
+ sess;
+
+ // Flush any sessions before reloading it.
+ self.sessions = {};
+
+ _.each(sessionstore, function (model, id, collection) {
+ sid = model.id;
+ sess = JSON.parse(model.session);
+
+ if (self.hybridCache) {
+ // Store the session data in the Express MemeoryStore for caching.
+ MemoryStore.set(sid, sess, function () {});
+ }
+
+ self.sessions[sid] = sess;
+ });
+
+ // Now that sessions are loaded, we'll call the callback that was waiting for them.
+ if (callback && typeof callback === 'function') {
+ callback();
+ }
+ };
+ fetchOptions.error = function (sessionstore, err) {
+ X.debug("Session Collection fetch failed.");
+
+ // Now that sessions are loaded, we'll call the callback that was waiting for them.
+ if (callback && typeof callback === 'function') {
+ callback("Session Collection fetch failed.");
+ }
+ };
+
+ // TODO - This is REALLY SLOW if there are 10,000 sessions in the table and we use collection.fetch().
+ // var sessionsCollection = new XM.SessionStoreCollection();
+ //sessionsCollection.fetch(fetchOptions);
+
+ // Fetch all records from XM.SessionStore and load them into the Express MemoryStore.
+ fetchOptions.query = {
+ requestType: "fetch",
+ recordType: "XM.SessionStore"
+ };
+
+ // fetchOptions.username = GLOBAL_USERNAME; // TODO
+ fetchOptions.username = 'node';
+ XT.dataSource.fetch(null, fetchOptions);
+ };
+
+ // Loops through the sessions, find the ones that are expired and sends that session data
+ // to the callback function. This allows us to expire session from code that has access to
+ // stuff like socket.io in main.js.
+ this.expireSessions = function (callback) {
+ this.loadSessions(null, function (err) {
+ if (err) {
+ return;
+ }
+
+ _.each(self.sessions, function (val, key, list) {
+ var expires = new Date(val.cookie.expires),
+ now = new Date();
+
+ if ((expires - now) <= 0) {
+ //X.debug("Session: ", key, " expired ", (expires - now));
+ callback(key, val);
+ } else {
+ //X.debug("Session: ", key, " expires in ", (expires - now));
+ }
+ });
+ });
+ };
+
+ X.debug("XTPGStore SessionStore using hybridCache = ", this.hybridCache);
+ // Prime this.sessions and MemoryCache on initialization.
+ this.loadSessions();
+ }
+
+ /**
+ * Inherit from `Store`.
+ */
+ XTPGStore.prototype = new Store();
+
+ /**
+ * Attempt to fetch session by the given `sid`.
+ *
+ * @param {String} sid
+ * @param {Function} fn
+ * @api public
+ */
+ XTPGStore.prototype.get = function (sid, done) {
+ try {
+ var fetchCache,
+ fetchDB,
+ that = this;
+
+ sid = this.prefix + sid;
+
+ fetchDB = function () {
+ var fetchOptions = {},
+ result,
+ sessionStore = {};
+
+ sessionStore = new XM.SessionStore();
+ fetchOptions.id = sid;
+
+ fetchOptions.success = function (model) {
+ // We have a matching session store cookie
+
+ // TODO - update lastModified time to extend timeout?
+ // Doing this will complicate things with a hybridCache MemoryStore.
+ // Do we really need to set that here for just in XM.Session and have CleanupTask
+ // clean up XM.SessionStore on timeouts?
+ //model.set("lastModified", new Date().getTime());
+ //model.save(null, saveOptions);
+
+ if (model.get("session")) {
+ result = JSON.parse(model.get("session"));
+ }
+
+ if (that.hybridCache) {
+ // We fetched from the database because this session was not in the MemoryStore.
+ // Load this data into the MemoryStore to speed this up next time.
+ MemoryStore.set(sid, result, function (err, cache) {
+ if (err) {
+ // Could not set. This shouldn't happen. Return error and move along...
+ console.trace("MemoryStore.set error:");
+ done && done(err);
+ } else {
+ // Return the session data back to Express to be used by routes and functors.
+ return done(null, result);
+ }
+ });
+ } else {
+ // Return the session data back to Express to be used by routes and functors.
+ return done(null, result);
+ }
+ };
+ fetchOptions.error = function (model, err) {
+ // Session was not found. This can happen if cookie is still in the browser, but
+ // db record was removed by CleanupTask because it has timed out.
+
+ // This is called when MemoryStore did not find session data.
+ // No need to touch MemoryStore here.
+
+ // Nothing found anywhere, no need to return the err, return nothing and move along...
+ done && done();
+ };
+
+ // Try to fetch a session matching the user's cookie sid.
+ sessionStore.fetch(fetchOptions);
+ };
+
+ if (this.hybridCache) {
+ // Try to get session data from MemoryStore.
+ fetchCache = function (err, cache) {
+ if (cache && !err) {
+ return done(null, cache);
+ } else {
+ fetchDB();
+ }
+ };
+ MemoryStore.get(sid, fetchCache);
+ } else {
+ fetchDB();
+ }
+ } catch (err) {
+ done && done(err);
+ }
+ };
+
+ /**
+ * Commit the given `sess` object associated with the given `sid`.
+ *
+ * @param {String} sid
+ * @param {Session} sess
+ * @param {Function} fn
+ * @api public
+ */
+ XTPGStore.prototype.set = function (sid, sess, done) {
+ try {
+ var fetchCache,
+ fetchDB,
+ that = this,
+ K = XM.Model;
+
+ sid = this.prefix + sid;
+ sess = JSON.stringify(sess);
+
+ fetchDB = function () {
+ var fetchOptions = {},
+ saveOptions = {},
+ sessionAttributes = {},
+ sessionStore = {};
+
+ sessionStore = new XM.SessionStore();
+
+ saveOptions.success = function (model) {
+ if (that.hybridCache) {
+ // Overwrite the MemoryStore with the new data, replacing old or creating new entry.
+ MemoryStore.set(sid, JSON.parse(sess), function (err, cache) {
+ if (err) {
+ // Could not set. Return error and move along.
+ console.trace("MemoryStore.set error:");
+ done && done(err);
+ } else {
+ // Success, MemoryStore updated, move along.
+ done && done();
+ }
+ });
+ } else {
+ // Success, move along.
+ done && done();
+ }
+ };
+ saveOptions.error = function (model, err) {
+ var that = this;
+ // This shouldn't happen. How did we get here? Log trace.
+ console.trace("XM.SessionStore save error. This shouldn't happen.");
+
+ if (that.hybridCache) {
+ // Delete any match in MemoryStore.
+ MemoryStore.destroy(sid, function (err, cache) {
+ if (err) {
+ // Could not destroy. This shouldn't happen. Return error and move along.
+ console.trace("MemoryStore.destroy error:");
+ done && done(err);
+ } else {
+ // TODO - This might throw an error because our err object does not includes a stack.
+ // https://github.com/senchalabs/connect/blob/master/lib/middleware/errorHandler.js#L48
+ // MemoryStore destroyed, move along.
+ done && done(that.err);
+ }
+ });
+ } else {
+ // TODO - This might throw an error because our err object does not includes a stack.
+ // https://github.com/senchalabs/connect/blob/master/lib/middleware/errorHandler.js#L48
+ // Return nothing and move along.
+ done && done(err);
+ }
+ };
+
+ fetchOptions.id = sid;
+
+ fetchOptions.success = function (model, resp) {
+ var status = model.getStatus();
+ // Fetch found this session, update it and save.
+ model.set("session", sess);
+
+ // Set gets called a lot. There isn't always a change to save and save will fail.
+ // Check if this model has changed before trying to save it.
+ if (status === K.READY_CLEAN) {
+ if (that.hybridCache) {
+ // MemoryStore did not have a matching session, update existing or add new.
+ MemoryStore.set(sid, JSON.parse(sess), function (err, cache) {
+ if (err) {
+ // Could not set. This shouldn't happen. Return error and move along.
+ console.trace("MemoryStore.set error:");
+ done && done(err);
+ } else {
+ // Nothing to save, MemoryStore updated, move along.
+ done && done();
+ }
+ });
+ } else {
+ // Nothing to save, move along.
+ done && done();
+ }
+ } else if (status === K.READY_DIRTY) {
+ // Try to save XM.SessionStore to database.
+ model.save(null, saveOptions);
+ }
+ };
+ fetchOptions.error = function (model, err) {
+ // Fetch did not find this session, initialize new and save.
+ // Create new XM.SessionStore object.
+ sessionStore = new XM.SessionStore();
+ // TODO - Is this redundant? Can I just call model.initialize...???
+ sessionStore.initialize(null, {isNew: true});
+
+ sessionAttributes = {
+ id: sid,
+ session: sess
+ };
+
+ // Try to save XM.SessionStore to database.
+ sessionStore.save(sessionAttributes, saveOptions);
+ };
+
+ // Try to fetch a session matching the user's cookie sid to
+ // see if we need to make a new one or update the old one.
+ sessionStore.fetch(fetchOptions);
+ };
+
+ if (this.hybridCache) {
+ // Try to get session data from MemoryStore.
+ fetchCache = function (err, cache) {
+ if (cache && !err) {
+ if (JSON.stringify(cache, null, null) === sess) {
+ // No change to the session data, nothing to save, move along.
+ done && done();
+ } else {
+ fetchDB();
+ }
+ } else {
+ fetchDB();
+ }
+ };
+ MemoryStore.get(sid, fetchCache);
+ } else {
+ fetchDB();
+ }
+ } catch (err) {
+ done && done(err);
+ }
+ };
+
+ /**
+ * Destroy the session associated with the given `sid`.
+ *
+ * @param {String} sid
+ * @api public
+ */
+ XTPGStore.prototype.destroy = function (sid, done) {
+ try {
+ var sessionStore = {},
+ fetchOptions = {},
+ that = this;
+
+ sid = this.prefix + sid;
+ sessionStore = new XM.SessionStore();
+ fetchOptions.id = sid;
+
+ fetchOptions.success = function (model) {
+ // Delete this session from the db store.
+ model.destroy();
+
+ if (that.hybridCache) {
+ // Delete this session from the MemoryStore as well.
+ MemoryStore.destroy(sid, function (err, cache) {
+ if (err) {
+ // Could not destroy. This shouldn't happen. Return error and move along.
+ console.trace("MemoryStore.destroy error:");
+ done && done(err);
+ } else {
+ // MemoryStore destroyed, move along.
+ done && done();
+ }
+ });
+ } else {
+ done && done();
+ }
+ };
+ fetchOptions.error = function (model, err) {
+ if (that.hybridCache) {
+ // Did not find a match in the db, delete this session from the MemoryStore, it's invalid.
+ MemoryStore.destroy(sid, function (err, cache) {
+ if (err) {
+ // Could not destroy. This shouldn't happen. Return error and move along.
+ console.trace("MemoryStore.destroy error:");
+ done && done(err);
+ } else {
+ // MemoryStore destroyed. Return nothing and move along.
+ done && done();
+ }
+ });
+ } else {
+ // Return nothing and move along.
+ done && done();
+ }
+ };
+
+ // Try to fetch a session matching the user's cookie sid.
+ sessionStore.fetch(fetchOptions);
+ } catch (err) {
+ done && done(err);
+ }
+ };
+
+ /**
+ * Invoke the given callback `fn` with all active sessions.
+ *
+ * @param {Function} fn
+ * @api public
+ */
+ XTPGStore.prototype.all = function (fn) {
+ this.loadSessions(null, function (err) {
+ if (err) {
+ return fn(err);
+ }
+
+ var arr = [],
+ keys = Object.keys(this.sessions);
+
+ for (var i = 0, len = keys.length; i < len; ++i) {
+ arr.push(this.sessions[keys[i]]);
+ }
+ fn(null, arr);
+ });
+ };
+
+ /**
+ * Fetch number of sessions.
+ *
+ * @param {Function} fn
+ * @api public
+ */
+ XTPGStore.prototype.length = function (fn) {
+ this.loadSessions(null, function (err) {
+ if (err) {
+ return fn(err);
+ }
+
+ fn(null, Object.keys(this.sessions).length);
+ });
+ };
+
+ return XTPGStore;
};
View
4 node-datasource/package.json
@@ -8,8 +8,8 @@
"url": "https://github.com/xtuple/xtuple.git"
},
"dependencies": {
- "backbone":"0.9.2",
- "backbone-relational":"0.6.x",
+ "backbone":"0.9.10",
+ "backbone-relational": "git://github.com/PaulUithol/Backbone-relational.git#0.8.0",
"connect-ensure-login": "0.1.x",
"connect-flash":"0.1.x",
"ejs": "0.8.x",
View
20 node-datasource/routes/data.js
@@ -69,9 +69,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
Can be called by websockets, or the express route (below), or REST, etc.
*/
var commitEngine = function (payload, session, callback) {
- var organization,
- query,
- binaryField = payload.binaryField,
+ var binaryField = payload.binaryField,
buffer,
binaryData,
options;
@@ -109,9 +107,7 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
Can be called by websockets, or the express route (below), or REST, etc.
*/
var dispatchEngine = function (payload, session, callback) {
- var organization,
- query,
- options;
+ var options;
if (payload && payload.databaseType === 'global') {
// Run this query against the global database.
options = createGlobalOptions(payload, session.passport.user.id, callback);
@@ -129,14 +125,12 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
Can be called by websockets, or the express route (below), or REST, etc.
*/
var fetchEngine = function (payload, session, callback) {
- var organization,
- query,
- options;
+ var options;
if (payload && payload.databaseType === 'global') {
// run this query against the global database
options = createGlobalOptions(payload, session.passport.user.id, callback);
- XT.dataSource.fetch(options);
+ XT.dataSource.fetch(null, options);
} else {
// run this query against an instance database
@@ -150,14 +144,12 @@ regexp:true, undef:true, strict:true, trailing:true, white:true */
Can be called by websockets, or the express route (below), or REST, etc.
*/
var retrieveEngine = function (payload, session, callback) {
- var organization,
- query,
- options;
+ var options;
if (payload && payload.databaseType === 'global') {
// run this query against the global database
options = createGlobalOptions(payload, session.passport.user.id, callback);
- XT.dataSource.retrieveRecord(payload.recordType, payload.id, options);
+ XT.dataSource.retrieveRecord(payload, options);
} else {
// run this query against an instance database
Please sign in to comment.
Something went wrong with that request. Please try again.