kinvey-local
Version:
Tool to mock the Kinvey environment for testing.
335 lines (287 loc) • 12.4 kB
JavaScript
/**
* index.js
* Script to tie everything together.
*
* Copyright (C) 2014 Karim Alibhai.
**/
(function () {
"use strict";
var path = require('path'),
deepExtend = require('deep-extend'),
EventEmitter = require('events').EventEmitter,
noop = function () {
return false;
},
// Kinvey's verification of a proper
// user object
isGoodUser = function (user) {
return user && user.hasOwnProperty('_id') && user.hasOwnProperty('_kmd') && user._kmd.hasOwnProperty('authtoken');
},
// the kinvey object
Kinvey = {
// some inner data
_didInit: false,
_events: new EventEmitter(),
_modules: require('./modules'),
_activeUser: null,
_collections: {
user: []
},
_mailTransport: null,
// app credentials
appKey: null,
appSecret: null,
masterSecret: null,
// endpoint request object
_request: function (name, data) {
return {
method: 'POST',
headers: [],
body: data,
params: [],
username: Kinvey.getActiveUser().username,
appKey: Kinvey.appKey,
collectionName: name
};
},
// endpoint reponse object
_response: function () {
var res = {
body: null,
complete: function () {
Kinvey._events.emit('complete', res.body);
Kinvey._events = new EventEmitter();
},
error: function (msg) {
Kinvey._events.emit('uderror', {
name: 'BLRuntimeError',
description: 'The Business Logic script has a runtime error. See debug message for details.',
debug: 'UserDefinedError: ' + msg
});
Kinvey._events = new EventEmitter();
}
};
return res;
},
// objects
Error: function (msg) {
return new Error(msg);
},
User: {
// verify data, and lookup user
// via username/password pair
login: function (username, password, callbacks) {
var options,
err = null,
users = Kinvey._collections.user,
user = null,
tmp,
i;
// don't allow login overlaps
if (Kinvey._activeUser) {
err = 'You are already logged in as a different user.';
} else {
// always use object data
if (typeof username === 'object') {
options = username;
callbacks = password;
} else {
options = {
username: username,
password: password
};
}
// noopify
callbacks = callbacks || {};
callbacks.success = callbacks.success || noop;
callbacks.error = callbacks.error || noop;
// unsupported: social logins
if (options.hasOwnProperty('_socialIdentity')) {
err = 'Social logins are currently unsupported.';
} else if (!options.hasOwnProperty('username') || !options.hasOwnProperty('password') || typeof options.password !== 'string') {
err = 'Argument must contain: username and password, or _socialIdentity.';
} else {
for (i = 0; i < users.length; i += 1) {
if (users[i].username === options.username && users[i].password === options.password) {
user = users[i];
break;
}
}
if (!user) {
callbacks.error('Wrong username/password combination.');
} else {
// falisfy authentication token
tmp = {
_kmd: {
authtoken: 'xxx-authtoken-xxx'
}
};
// save new object as user
deepExtend(tmp, user);
Kinvey.setActiveUser(tmp);
callbacks.success();
}
}
}
// throw error?
if (err) {
throw new Kinvey.Error(err);
}
},
// reset active user, and
// result in success
logout: function (callbacks) {
// noopify
callbacks = callbacks || {};
callbacks.success = callbacks.success || noop;
// reset user
Kinvey._activeUser = null;
// return
callbacks.success();
}
},
// configure phony local env
// using options
setOptions: function (options) {
var endBase;
// puts users store back into collections
if (options.hasOwnProperty('users')) {
options.collections = options.collections || {};
options.collections.user = options.users;
delete options.users;
}
// preset collections
if (options.hasOwnProperty('collections')) {
deepExtend(Kinvey._collections, options.collections);
}
// event-based push notifs
if (options.hasOwnProperty('push')) {
Kinvey._push = options.push;
}
// endpoint path base
if (options.hasOwnProperty('endpoints-base')) {
endBase = path.resolve(options['endpoints-base']);
// make property read-only
Kinvey.__defineGetter__("_endBase", function () {
return endBase;
});
} else {
throw new Kinvey.Error('The `endpoints-base` property is required.');
}
// nodemailer config
if (options.hasOwnProperty('email')) {
Kinvey._mailTransport = options.email || {};
}
},
// simple key/secret verification
// (does not verify against Kinvey)
init: function (creds) {
return {
then: function (success, error) {
var err = null;
// reset user login
Kinvey._activeUser = null;
Kinvey._didInit = null;
// noopify
success = success || noop;
error = error || noop;
// verify data like Kinvey
if (creds.hasOwnProperty('appKey')) {
if (creds.hasOwnProperty('masterSecret')) {
err = new Kinvey.Error('Never user the masterSecret on the client side.');
} else if (!creds.hasOwnProperty('appSecret')) {
err = new Kinvey.Error('options argument must contain: appSecret.');
}
} else {
err = new Kinvey.Error('options argument must contain: appKey.');
}
// execute correct callback
if (err) {
error(err);
} else {
// set state
Kinvey._didInit = true;
// save credentials
Kinvey.appKey = creds.appKey;
Kinvey.appSecret = creds.appSecret;
// return to callback
success();
}
}
};
},
// quick callback exec
// (nothing to ping)
ping: function () {
return {
then: function (success) {
(success || noop).call();
}
};
},
// grab user profile
getActiveUser: function () {
if (Kinvey._didInit !== true) {
throw new Kinvey.Error('Kinvey.getActiveUser can only be called after the ' +
'promise returned by Kinvey.init fulfills or rejects.');
}
return Kinvey._activeUser;
},
// set user profile
setActiveUser: function (user) {
// validate arguments
if (!isGoodUser(user)) {
throw new Kinvey.Error('user argument must contain: _id, _kmd.authtoken.');
}
// backup previous user
var result = Kinvey.getActiveUser();
Kinvey._activeUser = user;
// return previous user
return result;
},
// endpoint execute
execute: function (name, data, callbacks) {
if (!Kinvey._activeUser || !Kinvey._didInit) {
throw new Kinvey.Error('Kinvey.getActiveUser can only be called after the ' +
'promise returned by Kinvey.init fulfills or rejects.');
}
callbacks = callbacks || {};
try {
// use hooked require to load the endpoint
var onRequest = require(path.join(Kinvey._endBase, name + '.js'));
// set once event handlers
Kinvey._events.once('complete', callbacks.success || noop);
Kinvey._events.once('uderror', callbacks.error || noop);
// execute the endpoint
onRequest(Kinvey._request(name, data), Kinvey._response(), Kinvey._modules);
} catch (err) {
callbacks.error({
name: 'BLInternalError',
description: 'The Business Logic script did not complete. See debug message for details',
debug: String(err)
});
}
}
},
hooks = {
hook: require('istanbul').hook,
// only allow transformation of
// modules starting with the
// endpoints' path
match: function (file) {
return file.substr(0, Kinvey._endBase.length) === Kinvey._endBase;
},
// simply extend the endpoint code
// to export the endpoint using node's
// module.exports style
transform: function (code) {
return code + ';module.exports=onRequest;';
}
};
// give modules the env as well
Kinvey._modules = Kinvey._modules(Kinvey);
// add hook to require() for endpoints
hooks.hook.hookRequire(hooks.match, hooks.transform);
// expose full object
module.exports = Kinvey;
}());