kinvey-local
Version:
Tool to mock the Kinvey environment for testing.
333 lines (282 loc) • 13.5 kB
JavaScript
/**
* collectionAccess.js
* Access to the data stores.
*
* Copyright (C) 2014 Karim Alibhai.
**/
/*jslint unparam:true*/
(function () {
"use strict";
var deepExtend = require('deep-extend'),
deepEqual = require('deep-equal'),
isEmptyObject = function (object) {
var i;
// stop if any valid properties
// are found
for (i in object) {
if (object.hasOwnProperty(i)) {
return false;
}
}
// object is empty if it has no
// original (owned) properties
return true;
},
typeEqual = function (one, two) {
one = typeof one;
two = typeof two;
return one === two;
},
nor = function (fn) {
return typeof fn === 'function' ? fn : function () {
return;
};
},
nonulls = function (arr) {
var array = [],
i;
for (i = 0; i < arr.length; i += 1) {
if (arr[i]) {
if (typeof arr[i] === 'object') {
if (!isEmptyObject(arr[i])) {
array.push(arr[i]);
}
} else {
array.push(arr[i]);
}
}
}
return array;
};
module.exports = function (Kinvey) {
var colAccess = {
// fetch value from object
// using a 'JSON selector'
// i.e. "outer.inner"
deepValue: function (property, object) {
var value, i;
// delimeter is always classic dot
// notation
property = String(property).split('.');
value = object;
// iterate through the tree
for (i = 0; i < property.length; i += 1) {
try {
value = value[property[i]];
} catch (err) {
value = undefined;
break;
}
}
// no property, no value
return property.length === 0 ? undefined : value;
},
// use simple strings to simplify
// things
objectID: function (id) {
return String(id);
},
// check if a data store by that name
// exists at all (and is usable)
collectionExists: function (name) {
return Kinvey._collections.hasOwnProperty(name) && Kinvey._collections[name] instanceof Array;
},
// grab entire data store
collection: function (name) {
// Kinvey thinks it is a good idea to
// use an empty collection when the real
// collection isn't found rather than throw
// an error, so that's what we'll do too
var col = [],
tmp,
i,
colHdl = {
// simple insert new document into
// collection
insert: function (document, callback) {
callback = nor(callback);
// copy over new records
col.push(document);
Kinvey._collections[name] = nonulls(col);
col = Kinvey._collections[name];
callback(null);
},
// simply delete any records
// that match the query
remove: function (query, callback) {
var match,
val,
x,
y;
query = query || {};
callback = nor(callback);
// an empty query object should
// act as a wildcard (no filters)
if (!isEmptyObject(query)) {
for (x = 0; x < col.length; x += 1) {
for (y in query) {
if (query.hasOwnProperty(y)) {
val = colAccess.deepValue(y, col[x]);
if (typeEqual(val, query[y])) {
if (deepEqual(val, query[y])) {
delete col[x];
}
} else if (typeof query[y] === 'object') {
// greater than and less than
if (typeof val === 'number') {
match = query[y].hasOwnProperty('$gt') || query[y].hasOwnProperty('$lt');
if (match && query[y].hasOwnProperty('$gt')) {
match = val > query[y].$gt;
}
if (match && query[y].hasOwnProperty('$lt')) {
match = val < query[y].$lt;
}
if (match) {
delete col[x];
}
}
}
}
}
}
// copy over new records
Kinvey._collections[name] = nonulls(col);
col = Kinvey._collections[name];
}
callback(null);
},
// lookup records matching the
// query (see Kinvey docs for query
// specs)
find: function (query, options, callback) {
var data = [],
match,
val,
x,
y;
query = query || {};
if (typeof options === 'function') {
callback = options;
options = {};
} else {
callback = nor(callback);
}
// an empty query object should
// act as a wildcard (no filters)
if (isEmptyObject(query)) {
data = col;
} else {
for (x = 0; x < col.length; x += 1) {
for (y in query) {
if (query.hasOwnProperty(y)) {
val = colAccess.deepValue(y, col[x]);
if (typeEqual(val, query[y])) {
if (deepEqual(val, query[y])) {
data.push(col[x]);
}
} else if (typeof query[y] === 'object') {
// greater than and less than
if (typeof val === 'number') {
match = query[y].hasOwnProperty('$gt') || query[y].hasOwnProperty('$lt');
if (match && query[y].hasOwnProperty('$gt')) {
match = val > query[y].$gt;
}
if (match && query[y].hasOwnProperty('$lt')) {
match = val < query[y].$lt;
}
if (match) {
data.push(col[x]);
}
}
}
}
}
}
}
// return no data if error occurs
// to make the programmer realize
// to check the error object
callback(null, nonulls(data));
},
// like find, but stops after first
// match is found
findOne: function (query, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
} else {
callback = nor(callback);
}
colHdl.find(query, options, function (err, data) {
callback(err, data && data.length > 0 ? data[0] : null);
});
},
// update existing record
update: function (document, callback) {
var x;
callback = nor(callback);
// an empty query object should
// act as a wildcard (no filters)
if (!isEmptyObject(document)) {
for (x = 0; x < col.length; x += 1) {
if (col[x]._id === document._id) {
col[x] = document;
break;
}
}
// copy over new records
Kinvey._collections[name] = nonulls(col);
col = Kinvey._collections[name];
}
callback(null);
},
// either update existing record
// or insert new one
save: function (document, callback) {
var match = false,
x;
callback = nor(callback);
// an empty query object should
// act as a wildcard (no filters)
if (!isEmptyObject(document)) {
for (x = 0; x < col.length; x += 1) {
if (col[x]._id === document._id) {
col[x] = document;
match = true;
break;
}
}
if (!match) {
col.push(document);
}
// copy over new records
Kinvey._collections[name] = nonulls(col);
col = Kinvey._collections[name];
}
callback(null);
}
};
// if real collection, copy over data
// to a new state to stop any code from
// changing the data without using the
// colHdl object
if (colAccess.collectionExists(name)) {
for (i = 0; i < Kinvey._collections[name].length; i += 1) {
if (!isEmptyObject(Kinvey._collections[name][i])) {
tmp = {};
deepExtend(tmp, Kinvey._collections[name][i]);
col.push(tmp);
}
}
}
// copy over new records
Kinvey._collections[name] = nonulls(col);
col = Kinvey._collections[name];
// return the handle wrapper
// over the collection for usage
return colHdl;
}
};
return colAccess;
};
}());