var cloneDeep = require('lodash.clonedeep'); var assign = require('object-assign'); var isobject = require('lodash.isobject'); var Q = require('q'); /** * OKQuery! * Takes a query spec for a resource and maps it the proper read * methods of that resource. This is simply to separate the notions * of queries and resources. */ function OKQuery(options) { if (!(this instanceof OKQuery)) return new OKQuery(options); options = options || {}; if (!options.resource) throw new Error('No resource provided to query'); var resource = options.resource; var type = resource.type; var query = options.query || '*'; // Ensure immutability if (isobject(query)) query = cloneDeep(query); Object.defineProperty(this, 'resource', { value: resource, writable: false, enumerable: true }); Object.defineProperty(this, 'type', { value: resource.type, writable: false, enumerable: true }); Object.defineProperty(this, 'as', { value: options.as, writable: false, enumerable: true }); this.get = createQuery(resource, query, { default: options.default }); } function createQuery(resource, query, options) { options = options || {}; if (resource.bound) { query = queryBound(resource); } else if (isobject(query)) { query = queryComplex(resource, query) } else if (isDynamic(query)) { query = queryDynamic(resource); } else if (isSet(query)) { query = queryAll(resource); } else { query = querySingle(resource, query); } if (options.default) { query = withDefault(query, options.default); } return query; } function queryComplex(resource, query) { var dynamicProp; // Query is an object specifying key value pairs against which // to match DB entries. Iterate through and check if any of the values // is unbound e.g. :id var notDynamic = Object.keys(query).every(function(prop) { var matcher = query[prop]; if (isDynamic(matcher)) { dynamicProp = prop; return false; } else { return true; } }); if (notDynamic) { return function() { return resource.find(query); } } else { return function(id) { // Bind the dynamic property to its value // and add the pair to the query var dynamicQuery = {}; dynamicQuery[dynamicProp] = id; var query = assign({}, query, dynamicQuery); return resource.find(query); } } } function queryDynamic(resource) { return function(id) { return resource.get(id); }; } function queryAll(resource) { return function() { // Always return sorted results return resource.sortBy('__index'); }; } function querySingle(resource, id) { return function() { return resource.get(id); }; } function queryBound(resource) { return function() { return resource.get(); }; } function withDefault(queryFn, resultDefault) { return function() { return Q.Promise(function(resolve, reject) { queryFn().then(function(data) { data = data || resultDefault; resolve(data); }, reject); }); }; } function isDynamic(query) { return query && query.charAt(0) === ':'; } function isSet(query) { return query && query === '*'; } module.exports = OKQuery;