realtime-storage
Version:
The Realtime Framework Cloud Storage client for Node.js
488 lines (438 loc) • 19.1 kB
JavaScript
var Messaging = require("./Messaging.js");
var Operation = require("./Operation.js");
var TableRef = require("./TableRef.js");
var TableSnapshot = require("./TableSnapshot.js");;
var Event = require("./Event.js");
/**
* @class Realtime.Storage.StorageRef Class with the definition of a storage reference.
*/
var StorageRef = function (args, success, error) {
var Storage = require("./Storage.js");
error = error ? error : function(err) {
if(Storage.debug && console && console.error) console.error(err);
};
var self = this;
var operationManager = new Operation(args);
var connection = this.connection = Messaging.ConnectionManager.create({
url: args.isSecure ? "https://ortc-storage.realtime.co/server/ssl/2.1/" : "http://ortc-storage.realtime.co/server/2.1/",
appKey: args.applicationKey,
privateKey: args.privateKey,
authToken: args.authenticationToken,
connectAttempts: 100,
autoConnect: false,
metadata: args.metadata,
onConnect: typeof args.onConnect == "function" ? function() {
args.onConnect.call(self, self);
// remove the error handler so it isn't called on an unrelated operation.
if(success) success(self);
} : function() { if(success) success(self); },
onDisconnect: typeof args.onDisconnect == "function" ? function() {
args.onDisconnect.call(self, self);
} : undefined,
onReconnect: typeof args.onReconnect == "function" ? function() {
args.onReconnect.call(self, self);
} : undefined,
onReconnecting: typeof args.onReconnecting == "function" ? function() {
args.onReconnecting.call(self, self);
} : undefined,
onException: typeof args.onException == "function" ? function(err) {
args.onException.call(self, self);
error(err);
} : error
});
this.applicationKey = args.applicationKey;
this.authenticationToken = args.authenticationToken;
this.privateKey = args.privateKey;
var meta = {
tables: {},
add: function(table) {
if(!meta.tables[table]) {
meta.tables[table] = { pending: true };
operationManager.describeTable({
applicationKey: this.applicationKey,
authenticationToken: this.authenticationToken,
privateKey: this.privateKey,
table: table
}, function(metadata) {
meta.tables[metadata.name].pending = false;
meta.tables[metadata.name].metadata = metadata;
var event = {};
event["meta_" + table] = metadata;
meta.fire(event);
}, function(error) {
var event = {};
event["meta_" + table] = { error: error };
meta.fire(event);
});
}
},
remove: function(table) {
if(meta.tables[table]) delete meta.tables[table];
},
get: function(table, success, error) {
if(meta.tables[table] && !meta.tables[table].pending) success(meta.tables[table].metadata);
else {
var event = {};
event["meta_" + table] = function(data) {
if(data.error) {
if(error) error(data.error);
return;
}
if(success) success(data);
};
meta.once(event);
}
return undefined;
}
};
Event.extend(meta);
// queues requests while checking if the token is authenticated
var auth = (function() {
var isAuthenticated;
// queued
var ops = [];
if(!args.privateKey) {
// authentication check
operationManager.isAuthenticated({
authenticationToken: args.authenticationToken
}, function(response) {
isAuthenticated = response;
if(isAuthenticated) {
authenticationDone();
}
else {
ops = [];
error("Token " + self.authenticationToken + " is not authenticated.");
}
}, error);
}
// existence of a private key overrides authentication verification procedure
else {
isAuthenticated = true;
if(args.authenticationToken) {
authenticationDone();
}
}
// establishes the ORTC connection and executes the waiting requests
function authenticationDone() {
connection.connect();
for(var i = 0; i < ops.length; ++i) {
check.apply(this, ops[i]);
}
};
// receives the function and its arguments
function check() {
// check is done
if(typeof isAuthenticated != "undefined") {
if(isAuthenticated) {
var args = Array.prototype.slice.call(arguments);
var fn = args.shift();
fn.apply(fn, args);
}
else {
error("Token " + self.authenticationToken + " is not authenticated.");
}
}
// waiting check to complete
else {
ops.push(Array.prototype.slice.call(arguments));
}
};
return check;
})();
/**
* @function {TableRef} table Creates a new table reference.
* @param {String} name The table name.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return The new item reference
* @see StorageRef\table.js
*/
this.table = function(name, error) {
if (!name) {
if(error) error("Name of the table is mandatory");
return null;
}
auth(meta.add, name);
return new TableRef({
name: name,
connection: connection,
operationManager: operationManager,
meta: function(success, error) {
auth(meta.get, name, success, error);
}
});
};
/**
* @function {StorageRef} authenticate Authenticate a token with the given permissions.
* @param {String} authenticationToken The token to authenticate.
* @param {Number} timeout The time (in seconds) that the token is valid.
* @param {optional Role[]} roles The list of roles assigned.
* @param {optional Policy[]} policies Additional policies particular to this token.
* @param {function(Boolean data)} success Function called when the operation completes successfully.
* @... {Boolean} data Indicates if the token was successfully authenticated.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\authenticate.js
*/
this.authenticate = function (args, success, error) {
if (!args.authenticationToken) {
if (error) {
error("The authenticationToken is missing.");
}
return this;
}
if (args.roles && !Array.isArray(args.roles)) {
if (error) {
error("The roles property must be an Array with the name of the roles.");
}
return this;
}
if (!args.timeout || typeof args.timeout != "number") {
if (error) {
error("Timeout must be a number.");
}
return this;
}
operationManager.authenticate({
authenticationToken: args.authenticationToken,
roles: args.roles,
timeout: args.timeout,
policies: typeof args.policies == "object" ? args.policies : undefined
}, success, error);
return this;
};
/**
* @function {StorageRef} isAuthenticated Verifies if the specified token is authenticated.
* @param {String} authenticationToken The token to verify.
* @param {function(Boolean data)} callback Function called when the operation completes successfully.
* @... {Boolean} data Indicates if the token is authenticated.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\isAuthenticated.js
*/
this.isAuthenticated = function(authenticationToken, success, error) {
if (typeof authenticationToken != "string") {
if (error) {
error("Must specify an authentication token.");
}
return this;
}
operationManager.isAuthenticated({
authenticationToken: authenticationToken
}, success, error);
return this;
};
/**
* @function {StorageRef} getTables Retrieves a list of the names of the tables created by the user’s application.
* @param {function(String[] tableNames)} success Function called when the operation completes successfully and for each existing table.
* @... {String[]} tableNames The names of the tables.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\getTables.js
*/
this.getTables = function(success, error, stopTable) {
var that = this;
operationManager.listTables({ startTable: stopTable }, function(res) {
if (success) {
var table;
for (var i = 0; i < res.tables.length; i++) {
table = res.tables[i];
success(new TableSnapshot({
name: table,
connection: connection,
operationManager: operationManager,
meta: (function(name) {
return function(success, error) {
auth(meta.get, name, success, error);
};
})(table)
}));
}
typeof res.stopTable != 'undefined' ?
that.getTables(success, error, res.stopTable) :
success(null);
}
}, error);
return this;
};
/**
* @function {StorageRef} listRoles Retrieves a list of the names of the roles created by the user’s application.
* @param {function(String[] roleNames)} success Function called, for each existing role name, when the operation completes successfully.
* @... {String[]} roleNames The names of the roles.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\listRoles.js
*/
this.listRoles = function (success, error) {
operationManager.listRoles({}, success, error);
};
/**
* @function {StorageRef} getRoles Retrieves the specified roles policies associated with the subscription.
* @param {String[]} roles The names of the roles to retrieve.
* @param {function(Role[] roles)} success Function called when the operation completes successfully and for each existing role.
* @... {Role[]} roles The retrieved roles.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\getRoles.js
*/
this.getRoles = function (roles, success, error) {
if (!roles) {
if (error) {
error("Must specify a list of roles.");
}
return this;
}
if (!Array.isArray(roles)) {
if (error) {
error("Roles must be an array.");
}
return this;
}
operationManager.getRoles({ roles: roles }, success, error);
};
/**
* @function {StorageRef} getRole Retrieves the policies that compose the role.
* @param {String} role The name of the role to retrieve.
* @param {function(Role role)} success Function called when the operation completes successfully.
* @... {Role} role The retrieved role.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\getRole.js
*/
this.getRole = function (role, success, error) {
if (!role) {
if (error) {
error("Must specify the name of the role.");
}
return this;
}
operationManager.getRole({ role: role }, success, error);
};
/**
* @function {StorageRef} deleteRole Removes a role associated with the subscription.
* @param {String} role The name of the role to delete.
* @param {function(Boolean data)} success Function called when the operation completes successfully.
* @... {Boolean} data Indicates if the role was successfully deleted. If false, no role was found to delete.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\deleteRole.js
*/
this.deleteRole = function (role, success, error) {
if (!role) {
if (error) {
error("Must specify the name of the role.");
}
return this;
}
operationManager.deleteRole({ role: role }, success, error);
};
/**
* @function {StorageRef} setRole Stores a set of rules that control access to the Storage database.
* @param {String} role The name of the role to set.
* @param {Policy} policies An object with access rules.
* @param {function(Boolean data)} success Function called when the operation completes successfully.
* @... {Boolean} data Indicates if role was successfully inserted.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\setRole.js
*/
this.setRole = function (role, policies, success, error) {
if (!role) {
if (error) {
error("Must specify the name of the role.");
}
return this;
}
if (!policies) {
if (error) {
error("Must specify the policies of the role.");
}
return this;
}
if (typeof policies != "object") {
if (error) {
error("Policies must be an object.");
}
return this;
}
operationManager.setRole({
role: role,
policies: policies
}, success, error);
};
/**
* @function {StorageRef} updateRole Modifies a set of existing rules that control access to the Storage database.
* @param {String} role The name of the role to set.
* @param {Policy} policies An object with access rules.
* @param {function(Boolean data)} success Function called when the operation completes successfully.
* @... {Boolean} data Indicates if role was successfully inserted.
* @param {optional function(String message)} error Response if client side validation failed or if an error was returned from the server.
* @... {String} message The message detailing the error.
* @return This storage reference.
* @see StorageRef\setRole.js
*/
this.updateRole = function (role, policies, success, error) {
if (!role) {
if (error) {
error("Must specify the name of the role.");
}
return this;
}
if (!policies) {
if (error) {
error("Must specify the policies of the role.");
}
return this;
}
if (typeof policies != "object") {
if (error) {
error("Policies must be an object.");
}
return this;
}
operationManager.updateRole({
role: role,
policies: policies
}, success, error);
};
// quick-fix...
// no connection and is authenticated.
if(!!args.authenticationToken && success) success(self);
/**
* @object Realtime.Storage.StorageRef.Role Defines a role which is a set of rules that control access to the Storage database.
*/
/**
* @property {String} name The name of the role.
*/
/**
* @property {Policy} policies Contains the storage access rules.
*/
/**
* @object Realtime.Storage.StorageRef.Policy Defines a set of rules that control access to the Storage database.
*/
/**
* @property {Object} database Rules regarding the use of operations
*/
/**
* @property {Object} tables Rules regarding the access to tables and their keys.
*/
/**
* @object Realtime.Storage.StorageRef.PresenceInfo Specification of the presence data structure.
*/
/**
* @property {read write Number} subscriptions The number of subscriptions.
*/
/**
* @property {read write Object[]} metadata Information regarding the connection of each subscriber (limited to 100). Each object's key is the connection metadata and the value the number of subscribers with that same metadata.
*/
};
module.exports = StorageRef;