diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fd609..19f2f0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +##### 1.1.0 - 27 March 2015 + +###### Backwards compatible API changes +- #4 - Support loading relations in find() +- #5 - Support loading relations in findAll() + +###### Backwards compatible bug fixes +- #2 - Should not be saving relations (duplicating data) +- #3 - Should be using removeCircular + ##### 1.0.3 - 10 March 2015 Rebuild. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ec5ec9..2b6dc0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,16 @@ # Contributing Guide -First, feel free to contact me with questions. [Mailing List](https://groups.io/org/groupsio/jsdata). [Issues](https://github.com/js-data/js-data-redis/issues). +First, support is handled via the [Mailing List](https://groups.io/org/groupsio/jsdata). Ask your questions there. + +When submitting issues on GitHub, please include as much detail as possible to make debugging quick and easy. + +- good - Your versions of js-data, js-data-redis, etc., relevant console logs/error, code examples that revealed the issue +- better - A [plnkr](http://plnkr.co/), [fiddle](http://jsfiddle.net/), or [bin](http://jsbin.com/?html,output) that demonstrates the issue +- best - A Pull Request that fixes the issue, including test coverage for the issue and the fix + +[Github Issues](https://github.com/js-data/js-data-redis/issues). + +#### Pull Requests 1. Contribute to the issue that is the reason you'll be developing in the first place 1. Fork js-data-redis @@ -10,3 +20,4 @@ First, feel free to contact me with questions. [Mailing List](https://groups.io/ 1. (in another terminal) `grunt karma:dev` (runs the tests) 1. Write your code, including relevant documentation and tests 1. Submit a PR and we'll review + diff --git a/Gruntfile.js b/Gruntfile.js index 6df53c8..1aeb3d0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,13 +1,11 @@ /* * js-data-redis - * http://github.com/js-data/js-data-redis + * https://github.com/js-data/js-data-redis * * Copyright (c) 2014-2015 Jason Dobry * Licensed under the MIT license. */ module.exports = function (grunt) { - 'use strict'; - require('jit-grunt')(grunt, { coveralls: 'grunt-karma-coveralls' }); @@ -49,6 +47,7 @@ module.exports = function (grunt) { externals: [ 'mout/string/underscore', 'mout/random/guid', + 'mout/object/omit', 'js-data', 'js-data-schema', 'redis' diff --git a/README.md b/README.md index fef769b..f073d6a 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,17 @@ store.registerAdapter('redis', adapter, { default: true }); ### Contributing -First, feel free to contact me with questions. [Mailing List](https://groups.io/org/groupsio/jsdata). [Issues](https://github.com/js-data/js-data-redis/issues). +First, support is handled via the [Mailing List](https://groups.io/org/groupsio/jsdata). Ask your questions there. + +When submitting issues on GitHub, please include as much detail as possible to make debugging quick and easy. + +- good - Your versions of js-data, js-data-redis, etc., relevant console logs/error, code examples that revealed the issue +- better - A [plnkr](http://plnkr.co/), [fiddle](http://jsfiddle.net/), or [bin](http://jsbin.com/?html,output) that demonstrates the issue +- best - A Pull Request that fixes the issue, including test coverage for the issue and the fix + +[Github Issues](https://github.com/js-data/js-data-redis/issues). + +#### Pull Requests 1. Contribute to the issue that is the reason you'll be developing in the first place 1. Fork js-data-redis diff --git a/dist/js-data-redis.js b/dist/js-data-redis.js index 92f3842..20d2118 100644 --- a/dist/js-data-redis.js +++ b/dist/js-data-redis.js @@ -45,25 +45,37 @@ module.exports = /* 0 */ /***/ function(module, exports, __webpack_require__) { + var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; + var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; - var JSData = __webpack_require__(1); - var redis = __webpack_require__(2); - var underscore = __webpack_require__(3); - var guid = __webpack_require__(4); - var P = JSData.DSUtils.Promise; - var deepMixIn = JSData.DSUtils.deepMixIn; - var forEach = JSData.DSUtils.forEach; + var JSData = _interopRequire(__webpack_require__(1)); + + var redis = _interopRequire(__webpack_require__(2)); + + var underscore = _interopRequire(__webpack_require__(3)); + + var guid = _interopRequire(__webpack_require__(4)); + + var omit = _interopRequire(__webpack_require__(5)); + + var DSUtils = JSData.DSUtils; + var P = DSUtils.Promise; + var deepMixIn = DSUtils.deepMixIn; + var removeCircular = DSUtils.removeCircular; + var forEach = DSUtils.forEach; + var contains = DSUtils.contains; + var emptyStore = new JSData.DS(); var filter = emptyStore.defaults.defaultFilter; - function getPath(resourceConfig) { + var getPath = function (resourceConfig) { if (resourceConfig) { return resourceConfig.table || underscore(resourceConfig.name); } - } + }; var Defaults = function Defaults() { _classCallCheck(this, Defaults); @@ -83,6 +95,7 @@ module.exports = getIds: { value: function getIds(resourceConfig) { var _this = this; + return new P(function (resolve, reject) { return _this.client.SMEMBERS(getPath(resourceConfig), function (err, ids) { return err ? reject(err) : resolve(ids); @@ -93,6 +106,7 @@ module.exports = GET: { value: function GET(path) { var _this = this; + return new P(function (resolve, reject) { return _this.client.GET(path, function (err, value) { return err ? reject(err) : resolve(JSON.parse(value)); @@ -101,9 +115,12 @@ module.exports = } }, find: { - value: function find(resourceConfig, id) { + value: function find(resourceConfig, id, options) { var _this = this; + + var fields = []; return new P(function (resolve, reject) { + options = options || {}; return _this.client.GET("" + getPath(resourceConfig) + "-" + id, function (err, item) { if (err) { reject(err); @@ -113,13 +130,65 @@ module.exports = resolve(JSON.parse(item)); } }); + }).then(function (instance) { + if (!options["with"]) { + return instance; + } + var tasks = []; + forEach(resourceConfig.relationList, function (def) { + var relationName = def.relation; + var relationDef = resourceConfig.getResource(relationName); + var __options = DSUtils._(relationDef, options); + if (contains(options["with"], relationName) || contains(options["with"], def.localField)) { + var task = undefined; + var params = {}; + if (__options.allowSimpleWhere) { + params[def.foreignKey] = instance[resourceConfig.idAttribute]; + } else { + params.where = {}; + params.where[def.foreignKey] = { + "==": instance[resourceConfig.idAttribute] + }; + } + + if (def.type === "hasMany" && params[def.foreignKey]) { + task = _this.findAll(relationDef, params, omit(__options.orig(), ["with"])); + } else if (def.type === "hasOne") { + if (def.localKey && instance[def.localKey]) { + task = _this.find(relationDef, instance[def.localKey], omit(__options.orig(), ["with"])); + } else if (def.foreignKey && params[def.foreignKey]) { + task = _this.findAll(relationDef, params, omit(__options.orig(), ["with"])).then(function (hasOnes) { + return hasOnes.length ? hasOnes[0] : null; + }); + } + } else if (instance[def.localKey]) { + task = _this.find(relationDef, instance[def.localKey], omit(__options.orig(), ["with"])); + } + + if (task) { + tasks.push(task); + fields.push(def.localField); + } + } + }); + if (tasks.length) { + return P.all(tasks).then(function (loadedRelations) { + forEach(fields, function (field, index) { + instance[field] = loadedRelations[index]; + }); + return instance; + }); + } + return instance; }); } }, findAll: { - value: function findAll(resourceConfig, params) { + value: function findAll(resourceConfig, params, options) { var _this = this; - return _this.getIds(resourceConfig).then(function (ids) { + + return this.getIds(resourceConfig).then(function (ids) { + options = options || {}; var tasks = []; var path = getPath(resourceConfig); forEach(ids, function (id) { @@ -128,13 +197,72 @@ module.exports = return P.all(tasks); }).then(function (items) { return filter.call(emptyStore, items, resourceConfig.name, params, { allowSimpleWhere: true }); + }).then(function (items) { + if (!options["with"]) { + return items; + } + var topTasks = []; + forEach(items, function (instance) { + var tasks = []; + var fields = []; + forEach(resourceConfig.relationList, function (def) { + var relationName = def.relation; + var relationDef = resourceConfig.getResource(relationName); + var __options = DSUtils._(relationDef, options); + if (contains(options["with"], relationName) || contains(options["with"], def.localField)) { + var task = undefined; + var _params = {}; + if (__options.allowSimpleWhere) { + _params[def.foreignKey] = instance[resourceConfig.idAttribute]; + } else { + _params.where = {}; + _params.where[def.foreignKey] = { + "==": instance[resourceConfig.idAttribute] + }; + } + + if (def.type === "hasMany" && _params[def.foreignKey]) { + task = _this.findAll(relationDef, _params, omit(__options.orig(), ["with"])); + } else if (def.type === "hasOne") { + if (def.localKey && instance[def.localKey]) { + task = _this.find(relationDef, instance[def.localKey], omit(__options.orig(), ["with"])); + } else if (def.foreignKey && _params[def.foreignKey]) { + task = _this.findAll(relationDef, _params, omit(__options.orig(), ["with"])).then(function (hasOnes) { + return hasOnes.length ? hasOnes[0] : null; + }); + } + } else if (instance[def.localKey]) { + task = _this.find(relationDef, instance[def.localKey], omit(__options.orig(), ["with"])); + } + + if (task) { + tasks.push(task); + fields.push(def.localField); + } + } + }); + if (tasks.length) { + topTasks.push(P.all(tasks).then(function (loadedRelations) { + forEach(fields, function (field, index) { + instance[field] = loadedRelations[index]; + }); + return instance; + })); + } + }); + if (topTasks.length) { + return P.all(topTasks); + } + return items; }); } }, create: { value: function create(resourceConfig, attrs) { var _this = this; + return new P(function (resolve, reject) { + attrs = removeCircular(omit(attrs, resourceConfig.relationFields || [])); attrs[resourceConfig.idAttribute] = attrs[resourceConfig.idAttribute] || guid(); return _this.client.multi().SET("" + getPath(resourceConfig) + "-" + attrs[resourceConfig.idAttribute], JSON.stringify(attrs)).SADD(getPath(resourceConfig), attrs[resourceConfig.idAttribute]).exec(function (err) { return err ? reject(err) : resolve(attrs); @@ -145,7 +273,9 @@ module.exports = update: { value: function update(resourceConfig, id, attrs) { var _this = this; + return new P(function (resolve, reject) { + attrs = removeCircular(omit(attrs, resourceConfig.relationFields || [])); var path = "" + getPath(resourceConfig) + "-" + id; return _this.client.GET(path, function (err, value) { if (err) { @@ -166,7 +296,8 @@ module.exports = updateAll: { value: function updateAll(resourceConfig, attrs, params) { var _this = this; - return _this.findAll(resourceConfig, params).then(function (items) { + + return this.findAll(resourceConfig, params).then(function (items) { var tasks = []; forEach(items, function (item) { return tasks.push(_this.update(resourceConfig, item[resourceConfig.idAttribute], attrs)); @@ -178,6 +309,7 @@ module.exports = destroy: { value: function destroy(resourceConfig, id) { var _this = this; + return new P(function (resolve, reject) { var path = getPath(resourceConfig); return _this.client.multi().DEL("" + path + "-" + id).SREM(path, id).exec(function (err) { @@ -189,7 +321,8 @@ module.exports = destroyAll: { value: function destroyAll(resourceConfig, params) { var _this = this; - return _this.findAll(resourceConfig, params).then(function (items) { + + return this.findAll(resourceConfig, params).then(function (items) { var tasks = []; forEach(items, function (item) { return tasks.push(_this.destroy(resourceConfig, item[resourceConfig.idAttribute])); @@ -229,5 +362,11 @@ module.exports = module.exports = require("mout/random/guid"); +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = require("mout/object/omit"); + /***/ } /******/ ]); \ No newline at end of file diff --git a/mocha.start.js b/mocha.start.js index 1bfb41f..fd3cfca 100644 --- a/mocha.start.js +++ b/mocha.start.js @@ -7,7 +7,7 @@ var sinon = require('sinon'); var DSRedisAdapter = require('./'); var JSData = require('js-data'); -var adapter, store, DSUtils, DSErrors, User; +var adapter, store, DSUtils, DSErrors, User, Post, Comment; var globals = module.exports = { fail: function (msg) { @@ -34,7 +34,8 @@ var globals = module.exports = { TYPES_EXCEPT_FUNCTION: ['string', 123, 123.123, null, undefined, {}, [], true, false], assert: assert, sinon: sinon, - adapter: undefined + adapter: undefined, + store: undefined }; var test = new mocha(); @@ -54,7 +55,56 @@ beforeEach(function () { adapter = new DSRedisAdapter(); DSUtils = JSData.DSUtils; DSErrors = JSData.DSErrors; - globals.User = global.User = User = store.defineResource('user'); + globals.User = global.User = User = store.defineResource({ + name: 'user', + relations: { + hasMany: { + post: { + localField: 'posts', + foreignKey: 'userId' + }, + comment: { + localField: 'comments', + foreignKey: 'userId' + } + } + } + }); + globals.Post = global.Post = Post = store.defineResource({ + name: 'post', + relations: { + belongsTo: { + user: { + localField: 'user', + localKey: 'userId' + } + }, + hasMany: { + comment: { + localField: 'comments', + foreignKey: 'postId' + } + } + } + }); + globals.Comment = global.Comment = Comment = store.defineResource({ + name: 'comment', + relations: { + belongsTo: { + post: { + localField: 'post', + localKey: 'postId' + }, + user: { + localField: 'user', + localKey: 'userId' + } + } + } + }); + + globals.store = store; + global.store = globals.store; globals.adapter = adapter; global.adapter = globals.adapter; @@ -70,7 +120,11 @@ afterEach(function (done) { globals.adapter = null; global.adapter = null; - adapter.destroyAll(User, {}).then(function () { + adapter.destroyAll(User).then(function () { + return adapter.destroyAll(Post); + }).then(function () { + return adapter.destroyAll(Comment); + }).then(function () { done(); - }, done); + }).catch(done); }); diff --git a/package.json b/package.json index 9ed3ae7..81ac7db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "js-data-redis", "description": "Redis adapter for js-data.", - "version": "1.0.3", + "version": "1.1.0", "homepage": "http://www.js-data.io/docs/dsredisadapter", "repository": { "type": "git", @@ -27,8 +27,8 @@ "redis" ], "devDependencies": { - "babel-core": "4.7.7", - "babel-loader": "4.1.0", + "babel-core": "4.7.16", + "babel-loader": "4.2.0", "chai": "2.1.1", "grunt": "0.4.5", "grunt-contrib-watch": "0.6.1", @@ -40,15 +40,17 @@ "jshint-loader": "0.8.3", "sinon": "1.13.0", "time-grunt": "1.1.0", - "webpack": "1.7.2", + "webpack": "1.7.3", "webpack-dev-server": "1.7.0" }, "scripts": { "test": "grunt test" }, "dependencies": { - "js-data": ">=1.1.0", - "mout": "0.11.0", + "mout": "0.11.0" + }, + "peerDependencies": { + "js-data": ">=1.5.7", "redis": ">=0.12.1" } } diff --git a/src/index.js b/src/index.js index 02feb66..7791164 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,18 @@ -let JSData = require('js-data'); -let redis = require('redis'); -let underscore = require('mout/string/underscore'); -let guid = require('mout/random/guid'); -let P = JSData.DSUtils.Promise; -let deepMixIn = JSData.DSUtils.deepMixIn; -let forEach = JSData.DSUtils.forEach; +import JSData from 'js-data'; +import redis from 'redis'; +import underscore from 'mout/string/underscore'; +import guid from 'mout/random/guid'; +import omit from 'mout/object/omit'; +let { DSUtils } = JSData; +let { Promise: P, deepMixIn, removeCircular, forEach, contains } = DSUtils; let emptyStore = new JSData.DS(); let filter = emptyStore.defaults.defaultFilter; -function getPath(resourceConfig) { +let getPath = resourceConfig => { if (resourceConfig) { return resourceConfig.table || underscore(resourceConfig.name); } -} +}; class Defaults { @@ -27,23 +27,22 @@ class DSRedisAdapter { } getIds(resourceConfig) { - let _this = this; return new P((resolve, reject) => { - return _this.client.SMEMBERS(getPath(resourceConfig), (err, ids) => err ? reject(err) : resolve(ids)); + return this.client.SMEMBERS(getPath(resourceConfig), (err, ids) => err ? reject(err) : resolve(ids)); }); } GET(path) { - let _this = this; return new P((resolve, reject) => { - return _this.client.GET(path, (err, value) => err ? reject(err) : resolve(JSON.parse(value))); + return this.client.GET(path, (err, value) => err ? reject(err) : resolve(JSON.parse(value))); }); } - find(resourceConfig, id) { - let _this = this; + find(resourceConfig, id, options) { + let fields = []; return new P((resolve, reject) => { - return _this.client.GET(`${getPath(resourceConfig)}-${id}`, (err, item) => { + options = options || {}; + return this.client.GET(`${getPath(resourceConfig)}-${id}`, (err, item) => { if (err) { reject(err); } else if (!item) { @@ -52,24 +51,129 @@ class DSRedisAdapter { resolve(JSON.parse(item)); } }); - }); + }).then(instance => { + if (!options.with) { + return instance; + } + let tasks = []; + forEach(resourceConfig.relationList, def => { + let relationName = def.relation; + let relationDef = resourceConfig.getResource(relationName); + let __options = DSUtils._(relationDef, options); + if (contains(options.with, relationName) || contains(options.with, def.localField)) { + let task; + let params = {}; + if (__options.allowSimpleWhere) { + params[def.foreignKey] = instance[resourceConfig.idAttribute]; + } else { + params.where = {}; + params.where[def.foreignKey] = { + '==': instance[resourceConfig.idAttribute] + }; + } + + if (def.type === 'hasMany' && params[def.foreignKey]) { + task = this.findAll(relationDef, params, omit(__options.orig(), ['with'])); + } else if (def.type === 'hasOne') { + if (def.localKey && instance[def.localKey]) { + task = this.find(relationDef, instance[def.localKey], omit(__options.orig(), ['with'])); + } else if (def.foreignKey && params[def.foreignKey]) { + task = this.findAll(relationDef, params, omit(__options.orig(), ['with'])).then(hasOnes => hasOnes.length ? hasOnes[0] : null); + } + } else if (instance[def.localKey]) { + task = this.find(relationDef, instance[def.localKey], omit(__options.orig(), ['with'])); + } + + if (task) { + tasks.push(task); + fields.push(def.localField); + } + } + }); + if (tasks.length) { + return P.all(tasks).then(loadedRelations => { + forEach(fields, (field, index) => { + instance[field] = loadedRelations[index]; + }); + return instance; + }); + } + return instance; + }); } - findAll(resourceConfig, params) { - let _this = this; - return _this.getIds(resourceConfig).then(ids => { + findAll(resourceConfig, params, options) { + return this.getIds(resourceConfig).then(ids => { + options = options || {}; let tasks = []; let path = getPath(resourceConfig); - forEach(ids, id => tasks.push(_this.GET(`${path}-${id}`))); + forEach(ids, id => tasks.push(this.GET(`${path}-${id}`))); return P.all(tasks); - }).then(items => filter.call(emptyStore, items, resourceConfig.name, params, { allowSimpleWhere: true })); + }) + .then(items => filter.call(emptyStore, items, resourceConfig.name, params, { allowSimpleWhere: true })) + .then(items => { + if (!options.with) { + return items; + } + let topTasks = []; + forEach(items, instance => { + let tasks = []; + let fields = []; + forEach(resourceConfig.relationList, def => { + let relationName = def.relation; + let relationDef = resourceConfig.getResource(relationName); + let __options = DSUtils._(relationDef, options); + if (contains(options.with, relationName) || contains(options.with, def.localField)) { + let task; + let params = {}; + if (__options.allowSimpleWhere) { + params[def.foreignKey] = instance[resourceConfig.idAttribute]; + } else { + params.where = {}; + params.where[def.foreignKey] = { + '==': instance[resourceConfig.idAttribute] + }; + } + + if (def.type === 'hasMany' && params[def.foreignKey]) { + task = this.findAll(relationDef, params, omit(__options.orig(), ['with'])); + } else if (def.type === 'hasOne') { + if (def.localKey && instance[def.localKey]) { + task = this.find(relationDef, instance[def.localKey], omit(__options.orig(), ['with'])); + } else if (def.foreignKey && params[def.foreignKey]) { + task = this.findAll(relationDef, params, omit(__options.orig(), ['with'])).then(hasOnes => hasOnes.length ? hasOnes[0] : null); + } + } else if (instance[def.localKey]) { + task = this.find(relationDef, instance[def.localKey], omit(__options.orig(), ['with'])); + } + + if (task) { + tasks.push(task); + fields.push(def.localField); + } + } + }); + if (tasks.length) { + topTasks.push(P.all(tasks).then(loadedRelations => { + forEach(fields, (field, index) => { + instance[field] = loadedRelations[index]; + }); + return instance; + })); + } + }); + if (topTasks.length) { + return P.all(topTasks); + } + return items; + }); } create(resourceConfig, attrs) { - let _this = this; return new P((resolve, reject) => { + attrs = removeCircular(omit(attrs, resourceConfig.relationFields || [])); attrs[resourceConfig.idAttribute] = attrs[resourceConfig.idAttribute] || guid(); - return _this.client + return this.client .multi() .SET(`${getPath(resourceConfig)}-${attrs[resourceConfig.idAttribute]}`, JSON.stringify(attrs)) .SADD(getPath(resourceConfig), attrs[resourceConfig.idAttribute]) @@ -78,10 +182,10 @@ class DSRedisAdapter { } update(resourceConfig, id, attrs) { - let _this = this; return new P((resolve, reject) => { + attrs = removeCircular(omit(attrs, resourceConfig.relationFields || [])); let path = `${getPath(resourceConfig)}-${id}`; - return _this.client.GET(path, (err, value) => { + return this.client.GET(path, (err, value) => { if (err) { reject(err); } else if (!value) { @@ -89,26 +193,24 @@ class DSRedisAdapter { } else { value = JSON.parse(value); deepMixIn(value, attrs); - _this.client.SET(path, JSON.stringify(value), err => err ? reject(err) : resolve(value)); + this.client.SET(path, JSON.stringify(value), err => err ? reject(err) : resolve(value)); } }); }); } updateAll(resourceConfig, attrs, params) { - let _this = this; - return _this.findAll(resourceConfig, params).then(items => { + return this.findAll(resourceConfig, params).then(items => { let tasks = []; - forEach(items, item => tasks.push(_this.update(resourceConfig, item[resourceConfig.idAttribute], attrs))); + forEach(items, item => tasks.push(this.update(resourceConfig, item[resourceConfig.idAttribute], attrs))); return P.all(tasks); }); } destroy(resourceConfig, id) { - let _this = this; return new P((resolve, reject) => { let path = getPath(resourceConfig); - return _this.client + return this.client .multi() .DEL(`${path}-${id}`) .SREM(path, id) @@ -117,10 +219,9 @@ class DSRedisAdapter { } destroyAll(resourceConfig, params) { - let _this = this; - return _this.findAll(resourceConfig, params).then(items => { + return this.findAll(resourceConfig, params).then(items => { let tasks = []; - forEach(items, item => tasks.push(_this.destroy(resourceConfig, item[resourceConfig.idAttribute]))); + forEach(items, item => tasks.push(this.destroy(resourceConfig, item[resourceConfig.idAttribute]))); return P.all(tasks); }); } diff --git a/test/find.spec.js b/test/find.spec.js index ea0d776..ec0490f 100644 --- a/test/find.spec.js +++ b/test/find.spec.js @@ -1,8 +1,9 @@ describe('DSRedisAdapter#find', function () { it('should find a user in Redis', function () { - var id; + var id, id2, _user, _post, _comments; return adapter.create(User, { name: 'John' }) .then(function (user) { + _user = user; id = user.id; assert.equal(user.name, 'John'); assert.isString(user.id); @@ -12,17 +13,58 @@ describe('DSRedisAdapter#find', function () { assert.equal(user.name, 'John'); assert.isString(user.id); assert.deepEqual(user, { id: id, name: 'John' }); + return adapter.create(Post, { + content: 'test', + userId: user.id + }); + }) + .then(function (post) { + _post = post; + id2 = post.id; + assert.equal(post.content, 'test'); + assert.isString(post.id); + assert.isString(post.userId); + return Promise.all([ + adapter.create(Comment, { + content: 'test2', + postId: post.id, + userId: _user.id + }), + adapter.create(Comment, { + content: 'test3', + postId: post.id, + userId: _user.id + }) + ]); + }) + .then(function (comments) { + _comments = comments; + _comments.sort(function (a, b) { + return a.content > b.content; + }); + return adapter.find(Post, _post.id, { with: ['user', 'comment'] }); + }) + .then(function (post) { + post.comments.sort(function (a, b) { + return a.content > b.content; + }); + assert.equal(JSON.stringify(post.user), JSON.stringify(_user)); + assert.equal(JSON.stringify(post.comments), JSON.stringify(_comments)); return adapter.destroy(User, id); }) .then(function (user) { assert.isFalse(!!user); return adapter.find(User, id); }) + .then(function () { + return adapter.destroy(Post, id2); + }) .then(function () { throw new Error('Should not have reached here!'); }) .catch(function (err) { assert.equal(err.message, 'Not Found!'); + return true; }); }); }); diff --git a/test/findAll.spec.js b/test/findAll.spec.js index af4b670..adb9cce 100644 --- a/test/findAll.spec.js +++ b/test/findAll.spec.js @@ -20,4 +20,37 @@ describe('DSRedisAdapter#findAll', function () { assert.isFalse(!!destroyedUser); }); }); + it('should load relations', function () { + return store.utils.Promise.all([ + adapter.create(User, { name: 'foo' }).then(function (user) { + return store.utils.Promise.all([ + adapter.create(Post, { name: 'foo2', userId: user.id }).then(function (post) { + return store.utils.Promise.all([ + adapter.create(Comment, { name: 'foo4', userId: user.id, postId: post.id }), + adapter.create(Comment, { name: 'bar4', userId: user.id, postId: post.id }) + ]) + }) + ]); + }), + adapter.create(User, { name: 'bar' }).then(function (user) { + return store.utils.Promise.all([ + adapter.create(Post, { name: 'foo3', userId: user.id }).then(function (post) { + return store.utils.Promise.all([ + adapter.create(Comment, { name: 'foo5', userId: user.id, postId: post.id }), + adapter.create(Comment, { name: 'bar5', userId: user.id, postId: post.id }) + ]) + }) + ]); + }) + ]).then(function () { + return adapter.findAll(Post, null, { with: ['user', 'comment'] }); + }).then(function (posts) { + assert.equal(posts.length, 2); + assert.equal(posts[0].comments.length, 2); + assert.equal(posts[1].comments.length, 2); + assert.equal(posts[0].user.id, posts[0].userId); + assert.equal(posts[1].user.id, posts[1].userId); + return adapter.destroyAll(Post); + }); + }); });