diff --git a/.gitignore b/.gitignore index 088ccb8..e776e27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Created by .ignore support plugin (hsz.mobi) bower_components node_modules -coverage \ No newline at end of file +coverage +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e20a2ea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# CHANGELOG + +## 2.0.5 + +- Fix `beforeSave` on object with nested model without `beforeSave` function + +## 2.0.0 + +- Diff on save +From 2.0.0 on, the default behaviour is to keep a copy of the entire object, and make a diff comparison on all the model keys to try to determine which keys have to be saved +If the object doesn't have an _id, the copy will be empty, allowing the entire object to be saved. + +If you wish to disable this change, pass `{force: true}` as an option object to the save method + +If you rewrote beforeSave methods, be careful and pass the optional `opts` object to the parents beforeSave method + +- BREAKING CHANGES + - Save behaviour + If you wish to keep sending all your fields to the server each you save (which you shouldn't). Pass `{force: true}` as options in the `save` method. + - Save behaviour + Only fields declared in the model are to be saved. This should actually be beneficial, since we don't send metadata or externally added values, but be carefull if you rely on the orm saving values your added manually. + Also, If you rewrote the before-save method, you are responsible for what you put in the object to be saved. Namely, you have to implement the behaviour if you want it. + - re-population after save. + Until 2.0.0 when you wanted to repopulate an object after saving it (ie making another query), you had to pass it as the first argument of the save method. Now, it has to be given as an options object containing `{populate: whateverYouNeedToPopulate}` + - Empty arrays: when building an active record, undefined arrays will not be instantiated to empty arrays (to prevent overriding unselected fields). Check for the actual presence of the array before trying to push. diff --git a/build/app.js b/build/app.js index 2bfc079..03177b5 100644 --- a/build/app.js +++ b/build/app.js @@ -18,10 +18,20 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'd function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } +var _Discriminator = require('./Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +var deep = require('deep-diff').diff; + /** * * Can be inherited or used as is. Holds the model definition, @@ -34,353 +44,455 @@ var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); */ function ActiveRecord(model, name) { - var sl = _ServiceLocator2['default'].instance; - var ActiveRecord = (function () { - function ActiveRecord($injector, rootUrl, options) { - _classCallCheck(this, ActiveRecord); - - this.$injector = $injector; - // this.$http = $injector.get('$http'); - this.rootUrl = rootUrl; - this.build(options); - } + var SManager = arguments.length <= 2 || arguments[2] === undefined ? (0, _SessionManager2['default'])(model) : arguments[2]; + return (function () { + var sl = _ServiceLocator2['default'].instance; + var session = new SManager(); + + var ActiveRecord = (function () { + function ActiveRecord(rootUrl, options) { + _classCallCheck(this, ActiveRecord); - _createClass(ActiveRecord, [{ - key: 'build', - value: function build(options) { - // istanbul ignore next + //this.$injector = $injector + this.rootUrl = rootUrl; + this.build(options); + } - var _this = this; + _createClass(ActiveRecord, [{ + key: 'build', + value: function build(options) { + // istanbul ignore next - _.each(model, function (field, key) { - if (options && (options[key] || options[key] === 0)) { - var name = _.isArray(field) ? field[0].ref : field.ref; - var dao = sl.getDao(name); - if (dao) { - /** - * Deal with populated fields when building a pre-existing object - * - * Some fields might be populated in existing object, but not in the incoming one - * To prevent things from disappearing, determine when new data should overwrite exiting - * - * In the case of an array, look at each incoming value. - * If it is populated, keep it - * If it isn't, try to find the populated value in the existing array, and use it instead. - */ - if (Array.isArray(options[key])) { - var populated = options[key].map(function (value) { - if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { - var found = _this[key].find(function (element) { - return element && element._id === value; - }); - if (found) return found; + var _this = this; + + var sess = {}; + _.each(model, function (field, key) { + if (options && (options[key] || options[key] === 0)) { + var name = _.isArray(field) ? field[0].ref : field.ref; + var dao = sl.getDao(name); + if (dao) { + /** + * Deal with populated fields when building a pre-existing object + * + * Some fields might be populated in existing object, but not in the incoming one + * To prevent things from disappearing, determine when new data should overwrite exiting + * + * In the case of an array, look at each incoming value. + * If it is populated, keep it + * If it isn't, try to find the populated value in the existing array, and use it instead. + */ + if (Array.isArray(options[key])) { + var populated = options[key].map(function (value) { + if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { + var found = _this[key].find(function (element) { + return element && element._id === value; + }); + if (found) return found; + } + return value; + }); + _this[key] = dao.build(_.clone(populated)); + } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { + _this[key] = dao.build(_.clone(options[key])); + } + } else { + _this[key] = _this.buildField(field, options[key]); + } + } + /** Save state of the object for diff purpose */ + if (options && options._id && _this[key]) { + var toSave = undefined; + /** If the field is a ref, only save id or the ids array */ + if ((_.isArray(field) && field[0].ref || field.ref) && + /** Unless it is marked nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + if (_.isArray(field)) { + toSave = _this[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (entry) { + return typeof entry === 'string' ? entry : entry._id; + }); + } else { + toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; + } + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + toSave = _this[key].map(function (e) { + if (e.beforeSave) return e.beforeSave(null, { force: true });else { + console.warn('The values at ' + key + ' should be an ActiveRecord instance for diff purpose'); + return e; + } + }); + } else { + if (_this[key].beforeSave) { + toSave = _this[key].beforeSave(null, { force: true }); + } else { + toSave = _this[key]; + console.warn('The value at ' + key + ' should be an ActiveRecord instance for diff purpose'); } - return value; - }); - _this[key] = dao.build(_.clone(populated)); - } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { - _this[key] = dao.build(_.clone(options[key])); + } + } else { + toSave = _this[key]; } - } else { - _this[key] = _this.buildField(field, options[key]); + sess[key] = _.cloneDeep(toSave); } - } else if (_.isArray(field)) { - _this[key] = []; - } - }); - return this; - } - }, { - key: 'clone', - value: function clone() { - var m = sl.getModel(name); - var ob = new m(this.$injector, this.rootUrl, this); - delete ob._id; - return ob; - } + }); + session.save(sess); + return this; + } + }, { + key: 'clone', + value: function clone() { + var m = sl.getModel(name); + var ob = new m(this.rootUrl, this); + delete ob._id; + return ob; + } - /** - * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! - */ - }, { - key: 'cloneDeep', - value: function cloneDeep() { - var clone = this.clone(); - /** Find ref properties that need their _id to be removed */ - _.each(model, function (v, k) { - if (clone[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Need to clone each */ - clone[k] = clone[k].map(function (e) { - return e.cloneDeep ? e.cloneDeep() : e; - }); + /** + * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! + */ + }, { + key: 'cloneDeep', + value: function cloneDeep() { + var clone = this.clone(); + /** Find ref properties that need their _id to be removed */ + _.each(model, function (v, k) { + if (clone[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Need to clone each */ + clone[k] = clone[k].map(function (e) { + return e.cloneDeep ? e.cloneDeep() : e; + }); + } + } else if (v.ref) { + /** Single nested object, replace it */ + clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } else if (v.ref) { - /** Single nested object, replace it */ - clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } - }); - return clone; - } - }, { - key: 'buildField', - value: function buildField(model, value) { - return _.clone(value); - } - }, { - key: 'archive', - value: function archive() { - return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); - } - }, { - key: 'restore', - value: function restore() { - return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); - } - }, { - key: 'remove', - value: function remove() { - return this['delete'](); - } - }, { - key: 'delete', - value: function _delete() { - return this.$http['delete'](this.rootUrl + '/' + this._id); - } - }, { - key: 'populate', - value: function populate(field, query) { - // istanbul ignore next + }); + return clone; + } + }, { + key: 'buildField', + value: function buildField(model, value) { + return _.clone(value); + } + }, { + key: 'archive', + value: function archive() { + return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); + } + }, { + key: 'restore', + value: function restore() { + return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); + } + }, { + key: 'remove', + value: function remove() { + return this['delete'](); + } + }, { + key: 'delete', + value: function _delete() { + return this.$http['delete'](this.rootUrl + '/' + this._id); + } + }, { + key: 'populate', + value: function populate(field, query, opts) { + // istanbul ignore next - var _this2 = this; + var _this2 = this; - var $q = this.$injector.get('$q'); + var $q = this.$injector.get('$q'); - if (Array.isArray(field)) { - return $q.all(field.map(function (f) { - return _this2.populate(f, query); - })).then(function () { - return _this2; - }); - } + if (Array.isArray(field)) { + return $q.all(field.map(function (f) { + return _this2.populate(f, query); + })).then(function () { + return _this2; + }); + } - var deferred = $q.defer(); - var self = this; - if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { - /** - * The field is an array. It may contain mixed populated and unpopulated data. - * first, sort it out (typeof unpopulated is string) - */ - var grouped = _.groupBy(this[field], function (i) { - return typeof i; - }); - /** No string = everything is populated (or array is empty), we're all set */ - if (!grouped.string) { + var deferred = $q.defer(); + var self = this; + if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { + /** + * The field is an array. It may contain mixed populated and unpopulated data. + * first, sort it out (typeof unpopulated is string) + */ + var grouped = _.groupBy(this[field], function (i) { + return typeof i; + }); + /** No string = everything is populated (or array is empty), we're all set */ + if (!grouped.string) { + deferred.resolve(this[field]); + } else { + /** Resolve the model name and the corresponding DAO */ + var name = model[field][0].ref; + if (!name) deferred.reject('Cannot Populate: unknown model');else { + var dao = sl.getDao(name); + if (!dao) { + deferred.reject('Cannot Populate: unknown DAO'); + } else { + return dao.get(dao.query(query).select(grouped.string), opts).then(function (d) { + /** To preserve order, we map the existing field, replacing only the populated values */ + self[field] = self[field].map(function (f) { + if (typeof f === 'string') { + return _.find(d.data, function (foo) { + return foo._id === f; + }); + } + return f; + }); + return self; + }); + } + } + } + } + + if (!this[field] || typeof this[field] === 'object') { + // The field is empty or already populated. return. deferred.resolve(this[field]); } else { - /** Resolve the model name and the corresponding DAO */ - var name = model[field][0].ref; + var name = model[field].ref; if (!name) deferred.reject('Cannot Populate: unknown model');else { var dao = sl.getDao(name); if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { - /** To preserve order, we map the existing field, replacing only the populated values */ - self[field] = self[field].map(function (f) { - if (typeof f === 'string') { - return _.find(d.data, function (foo) { - return foo._id === f; - }); - } - return f; - }); + return dao.getById(this[field], query, opts).then(function (sub) { + self[field] = sub; return self; }); } } } - } - if (!this[field] || typeof this[field] === 'object') { - // The field is empty or already populated. return. - deferred.resolve(this[field]); - } else { - var name = model[field].ref; - if (!name) deferred.reject('Cannot Populate: unknown model');else { - var dao = sl.getDao(name); - if (!dao) { - deferred.reject('Cannot Populate: unknown DAO'); - } else { - return dao.getById(this[field]).then(function (sub) { - self[field] = sub; - return self; - }); - } - } + return deferred.promise; } + }, { + key: 'beforeSave', + value: function beforeSave(obj) { + // istanbul ignore next - return deferred.promise; - } - }, { - key: 'beforeSave', - value: function beforeSave(obj) { - obj = obj || _.cloneDeep(this); - _.each(model, function (field, key) { - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + var _this3 = this; + + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ + if (!obj) { + obj = {}; + _.each(_.keys(model), function (k) { + if (!_.isUndefined(_this3[k])) { + obj[k] = _this3[k]; + } + }); + } + /** Retrieve object saved in session to perform the diff */ + var old = session.retrieve(this._id) || {}; + + _.each(model, function (field, key) { + if (!_.isUndefined(obj[key])) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || _.isArray(field) && field[0].ref) && + /** Unless it is specifically marked as nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[key] = _.get(obj, [key, '_id']) || obj[key]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + obj[key] = obj[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (e) { + return e.beforeSave ? e.beforeSave(null, { force: true }) : e; + }); + } else { + if (obj[key] && obj[key] !== null) { + obj[key] = obj[key].beforeSave ? obj[key].beforeSave(null, { force: true }) : obj[key]; + } + } + } else if (_.isDate(obj[key])) { + /** Make sure the date is an ISOString */ + obj[key] = new Date(obj[key]).toISOString(); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); - } - }); - delete obj.rootUrl; - delete obj.$injector; - return obj; - } - }, { - key: 'save', - value: function save(populate) { - // istanbul ignore next + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ + if (!opts.force && !deep(old[key], obj[key])) { + delete obj[key]; + } + }); + return obj; + } + }, { + key: 'save', + value: function save() { + // istanbul ignore next - var _this3 = this; + var _this4 = this; - var toSave = this.beforeSave(); - var callback; - if (populate) { - var dao = sl.getDao(name); - callback = function () { - return dao.getById(_this3._id, dao.query().populate(populate)); - }; - } else { - callback = function (data) { - _this3.build(data.data); - return data; - }; - } + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - if (this._id) { - return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); - } else { - return this.$http.post(this.rootUrl, toSave).then(callback); + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this4 }); + }); + } + var callback; + if (opts.populate) { + var dao = sl.getDao(name); + callback = function () { + return dao.getById(_this4._id, dao.query().populate(opts.populate)); + }; + } else { + callback = function (data) { + _this4.build(data.data); + return data; + }; + } + + if (this._id) { + return this.$http.put(this.rootUrl + '/' + this._id, toSave, opts).then(callback); + } else { + return this.$http.post(this.rootUrl, toSave, opts).then(callback); + } } - } - }, { - key: 'saveDeep', - value: function saveDeep(populate) { - // istanbul ignore next - - var _this4 = this; - - var promises = []; - /** Find ref properties that might need to be saved */ - _.each(model, function (v, k) { - if (_this4[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { - if (!e._id && e.saveDeep) promises.push(e.saveDeep()); - }); + }, { + key: 'saveDeep', + value: function saveDeep(populate) { + // istanbul ignore next + + var _this5 = this; + + var promises = []; + /** Find ref properties that might need to be saved */ + _.each(model, function (v, k) { + if (_this5[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Check if need to save each */ + _this5[k].forEach(function (e) { + if (!e._id && e.saveDeep) promises.push(e.saveDeep()); + }); + } + } else if (v.ref) { + /** Single nested object, save it if needed */ + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } - } else if (v.ref) { - /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); } + }); + var $q = this.$injector.get('$q'); + return $q.all(promises).then(function () { + return _this5.save(populate); + }); + } + }, { + key: '$injector', + get: function get() { + return sl.getInjector(); + } + }, { + key: '$http', + get: function get() { + return this.$injector.get('$http'); + } + }, { + key: '$$pristine', + get: function get() { + return _.isEmpty(this.beforeSave()); + } + }], [{ + key: 'getName', + value: function getName() { + return name; + } + }, { + key: 'makePopObject', + value: function makePopObject(pop) { + return _.map(pop, function (p, k) { + p = Array.isArray(p) ? p[0] : p; + var ob = { path: k }; + var sub = ActiveRecord.findSubPopulates(p.ref); + if (sub && sub.length) { + ob.populate = sub; + } + return ob; + }); + } + }, { + key: 'findSubPopulates', + value: function findSubPopulates(ref) { + var serviceLocator = _ServiceLocator2['default'].instance; + var linkedModel = serviceLocator.getModel(ref); + if (linkedModel && linkedModel !== model) { + return linkedModel.populateParams(); } - }); - var $q = this._injector.get('$q'); - return $q.all(promises).then(function () { - return _this4.save(populate); - }); - } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } - }, { - key: '$http', - get: function get() { - return this.$injector.get('$http'); - } - }], [{ - key: 'getName', - value: function getName() { - return name; - } - }, { - key: 'makePopObject', - value: function makePopObject(pop) { - return _.map(pop, function (p, k) { - p = Array.isArray(p) ? p[0] : p; - var ob = { path: k }; - var sub = ActiveRecord.findSubPopulates(p.ref); - if (sub && sub.length) { - ob.populate = sub; - } - return ob; - }); - } - }, { - key: 'findSubPopulates', - value: function findSubPopulates(ref) { - var serviceLocator = _ServiceLocator2['default'].instance; - var linkedModel = serviceLocator.getModel(ref); - if (linkedModel && linkedModel !== model) { - return linkedModel.populateParams(); } - } - }, { - key: 'populateParams', - value: function populateParams(populateArray) { - - // Get from the model the fields that reference another object - var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; - }); + }, { + key: 'populateParams', + value: function populateParams(populateArray) { + + // Get from the model the fields that reference another object + var pop = _.pick(model, function (v) { + if (_.isArray(v)) return v[0].ref && !v[0].nested; + return v.ref && !v.nested; + }); - if (populateArray === 'all') { - return ActiveRecord.makePopObject(pop); + if (populateArray === 'all') { + return ActiveRecord.makePopObject(pop); + } + var toPopulate = populateArray ? _.pick(pop, function (p, k) { + return _.contains(populateArray, k); + }) : _.pick(pop, function (v) { + return v.populateDefault || v[0] && v[0].populateDefault; + }); + return ActiveRecord.makePopObject(toPopulate); } - var toPopulate = populateArray ? _.pick(pop, function (p, k) { - return _.contains(populateArray, k); - }) : _.pick(pop, function (v) { - return v.populateDefault || v[0] && v[0].populateDefault; - }); - return ActiveRecord.makePopObject(toPopulate); - } - }, { - key: 'getModel', - value: function getModel() { - return model; - } - }]); + }, { + key: 'getModel', + value: function getModel() { + return model; + } + }, { + key: 'getSession', + value: function getSession() { + return session; + } + }]); + + return ActiveRecord; + })(); return ActiveRecord; })(); - // sl.registerModel(name, ActiveRecord); - return ActiveRecord; } module.exports = exports['default']; -},{"./ServiceLocator":5}],2:[function(require,module,exports){ +},{"./Discriminator":3,"./ServiceLocator":6,"./SessionManager":7,"deep-diff":17}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -432,6 +544,15 @@ var DaoHelper = (function () { value: function setRootUrl(url) { this.dao.url = url; } + }, { + key: 'setDiscriminatorUrl', + value: function setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, function (discriminator) { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url; + } + }); + } }, { key: '$get', value: function $get() { @@ -446,7 +567,8 @@ var DaoHelper = (function () { key: 'registerService', value: function registerService(module, name, dao) { module.provider(name, DaoHelper.getProvider(dao)).run([name, '$injector', function (service, $injector) { - service.setInjector($injector); + _ServiceLocator2['default'].instance.registerInjector($injector); + //service.setInjector($injector); }]); } }]); @@ -456,7 +578,68 @@ var DaoHelper = (function () { exports['default'] = DaoHelper; module.exports = exports['default']; -},{"./ServiceLocator":5}],3:[function(require,module,exports){ +},{"./ServiceLocator":6}],3:[function(require,module,exports){ +//_ = require('lodash'); +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +exports['default'] = Discriminator; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +function Discriminator(Model, type) { + + var Discriminator = (function (_Model) { + _inherits(Discriminator, _Model); + + function Discriminator() { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).apply(this, arguments); + } + + _createClass(Discriminator, null, [{ + key: 'type', + get: function get() { + return type; + } + }]); + + return Discriminator; + })(Model); + + return Discriminator; +} + +module.exports = exports['default']; +},{"./ServiceLocator":6,"./SessionManager":7}],4:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -483,25 +666,19 @@ var _QueryBuilder = require('./QueryBuilder'); var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); -function GenericDao(model, qb) { +function GenericDao(model, qb, discriminators) { var sl = _ServiceLocator2['default'].instance; sl.registerModel(model.getName(), model); var myClass = (function () { function myClass(url) { _classCallCheck(this, myClass); - // this.$injector = $injector; - // this.$http = $injector.get('$http'); this.url = url; this.model = model; + this.discriminators = discriminators; } _createClass(myClass, [{ - key: 'setInjector', - value: function setInjector($injector) { - this.$injector = $injector; - } - }, { key: 'getModel', value: function getModel() { return model; @@ -518,13 +695,17 @@ function GenericDao(model, qb) { } }, { key: 'getHeaders', - value: function getHeaders() {} + value: function getHeaders() { + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + return opts.headers; + } }, { key: 'getOptions', value: function getOptions() { var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } }, { @@ -572,17 +753,38 @@ function GenericDao(model, qb) { if (Array.isArray(data)) { return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + if (this.discriminators && data.__t) { + var disc = _.find(this.discriminators, { type: data.__t }); + if (disc) { + return new disc(disc.discriminatorUrl, data); + } + } + return new model(this.url, data); } }, { key: 'create', value: function create(params) { - return new this.model(this.$injector, this.url, params); + if (this.discriminators && params.__t) { + var disc = _.find(this.discriminators, { type: params.__t }); + if (disc) { + return new disc(disc.discriminatorUrl, params); + } + } + return new this.model(this.url, params); + } + + // get discriminators(){ + // return discriminators + // } + }, { + key: '$injector', + get: function get() { + return sl.getInjector(); } }, { key: '$http', get: function get() { - return this.$injector.get('$http'); + return sl.getInjector().get('$http'); } }]); @@ -606,20 +808,20 @@ function GenericDao(model, qb) { var _this2 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query : arguments[1]; + var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - return this.$http.get(this.url + '/' + value, { params: qb.opts }).then(function (data) { - return new model(_this2.$injector, _this2.url, data.data); + opts = this.getOptions(opts); + return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { + return new model(_this2.url, data.data); }); }; } else { myClass.prototype['selectBy' + _.capitalize(key)] = function (toSelect) { - // istanbul ignore next - - var _this3 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); if (toSelect && toSelect.length) { if (value.ref) { toSelect = extractId(toSelect); @@ -634,14 +836,7 @@ function GenericDao(model, qb) { } qb.setQuery(obj); } - return this.$http.get(this.url, { params: qb.opts }).then(function (data) { - if (!data.data) { - data.data = []; - } - return { - data: data.data.map(_this3.build, _this3), meta: { total: data.headers('X-Total-Count') } - }; - }); + return this.get(qb, opts); }; } //myClass.prototype @@ -651,7 +846,7 @@ function GenericDao(model, qb) { } module.exports = exports['default']; -},{"./QueryBuilder":4,"./ServiceLocator":5}],4:[function(require,module,exports){ +},{"./QueryBuilder":5,"./ServiceLocator":6}],5:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -819,7 +1014,7 @@ var QueryBuilder = (function () { exports['default'] = QueryBuilder; module.exports = exports['default']; -},{}],5:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -864,6 +1059,11 @@ var ServiceLocator = (function () { value: function getDao(name) { return this.daoRegistry[name]; } + }, { + key: "getInjector", + value: function getInjector() { + return this.injector; + } }, { key: "registerDao", value: function registerDao(name, dao) { @@ -874,6 +1074,11 @@ var ServiceLocator = (function () { value: function registerModel(name, model) { this.modelRegistry[name] = model; } + }, { + key: "registerInjector", + value: function registerInjector(injector) { + this.injector = injector; + } }], [{ key: "instance", get: function get() { @@ -889,30 +1094,101 @@ var ServiceLocator = (function () { exports["default"] = ServiceLocator; module.exports = exports["default"]; -},{}],6:[function(require,module,exports){ +},{}],7:[function(require,module,exports){ 'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true +}); // istanbul ignore next -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); -var _DaoHelper = require('./DaoHelper'); +exports['default'] = SessionManager; +// istanbul ignore next -var _DaoHelper2 = _interopRequireDefault(_DaoHelper); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } -var _managersTstManager1 = require('./managers/tstManager1'); +// istanbul ignore next -var _managersTstManager12 = _interopRequireDefault(_managersTstManager1); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } -var _managersTstManager2 = require('./managers/tstManager2'); +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +function SessionManager(model) { + + var SessionManager = (function () { + function SessionManager() { + _classCallCheck(this, SessionManager); + + this.storage = {}; + } + + _createClass(SessionManager, [{ + key: 'save', + value: function save(obj) { + if (!obj._id) return; + this.storage[obj._id] = obj; + } + }, { + key: 'retrieve', + value: function retrieve(id) { + return this.storage[id]; + } + }, { + key: 'clean', + value: function clean() { + this.storage = {}; + } + }, { + key: 'model', + get: function get() { + return model; + } + }]); + + return SessionManager; + })(); + return SessionManager; +} + +module.exports = exports['default']; +},{"./ServiceLocator":6}],8:[function(require,module,exports){ +'use strict'; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _DaoHelper = require('./DaoHelper'); + +var _DaoHelper2 = _interopRequireDefault(_DaoHelper); + +var _managersTstManager1 = require('./managers/tstManager1'); + +var _managersTstManager12 = _interopRequireDefault(_managersTstManager1); + +var _managersTstManager2 = require('./managers/tstManager2'); var _managersTstManager22 = _interopRequireDefault(_managersTstManager2); +var _managersTstManager3 = require('./managers/tstManager3'); + +var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); + +var _managersTstManager4 = require('./managers/tstManager4'); + +var _managersTstManager42 = _interopRequireDefault(_managersTstManager4); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); -},{"./DaoHelper":2,"./managers/tstManager1":7,"./managers/tstManager2":8}],7:[function(require,module,exports){ +_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); +_DaoHelper2['default'].registerService(_module, 'ModelManager4', _managersTstManager42['default']); +},{"./DaoHelper":2,"./managers/tstManager1":9,"./managers/tstManager2":10,"./managers/tstManager3":11,"./managers/tstManager4":12}],9:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -963,7 +1239,7 @@ var ModelManager = (function (_DAO) { exports['default'] = ModelManager; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":9}],8:[function(require,module,exports){ +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel1.js":13}],10:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1014,7 +1290,112 @@ var ModelManager2 = (function (_DAO) { exports['default'] = ModelManager2; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":10}],9:[function(require,module,exports){ +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel2.js":14}],11:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel3Js = require('./../models/tstModel3.js'); + +var _modelsTstModel3Js2 = _interopRequireDefault(_modelsTstModel3Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _QueryBuilder = require('../QueryBuilder'); + +var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel3Js2['default']); + +var ModelManager3 = (function (_DAO) { + _inherits(ModelManager3, _DAO); + + function ModelManager3() { + _classCallCheck(this, ModelManager3); + + _get(Object.getPrototypeOf(ModelManager3.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager3; +})(DAO); + +exports['default'] = ModelManager3; +; +module.exports = exports['default']; +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel3.js":15}],12:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel4Js = require('./../models/tstModel4.js'); + +var _modelsTstModel4Js2 = _interopRequireDefault(_modelsTstModel4Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _Discriminator = require('../Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + +var D1 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type1'); +var D2 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type2'); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel4Js2['default'], undefined, [D1, D2]); + +var ModelManager4 = (function (_DAO) { + _inherits(ModelManager4, _DAO); + + function ModelManager4() { + _classCallCheck(this, ModelManager4); + + _get(Object.getPrototypeOf(ModelManager4.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager4; +})(DAO); + +exports['default'] = ModelManager4; +; +module.exports = exports['default']; +},{"../Discriminator":3,"../GenericDao":4,"./../models/tstModel4.js":16}],13:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1048,6 +1429,8 @@ var model = { }, //private: true + when: Date, + label: String, num: Number, @@ -1060,7 +1443,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; var AR = (0, _ActiveRecord2['default'])(model, 'Model1'); @@ -1079,7 +1472,7 @@ var Model = (function (_AR) { exports['default'] = Model; module.exports = exports['default']; -},{"../ActiveRecord":1}],10:[function(require,module,exports){ +},{"../ActiveRecord":1}],14:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1132,4 +1525,538 @@ var Model2 = (function (_AR) { exports['default'] = Model2; module.exports = exports['default']; -},{"../ActiveRecord":1}]},{},[6]); +},{"../ActiveRecord":1}],15:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model3'); + +var Model3 = (function (_AR) { + _inherits(Model3, _AR); + + function Model3() { + _classCallCheck(this, Model3); + + _get(Object.getPrototypeOf(Model3.prototype), 'constructor', this).apply(this, arguments); + } + + return Model3; +})(AR); + +exports['default'] = Model3; +module.exports = exports['default']; +},{"../ActiveRecord":1}],16:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String, + + __t: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model4'); + +var Model4 = (function (_AR) { + _inherits(Model4, _AR); + + function Model4() { + _classCallCheck(this, Model4); + + _get(Object.getPrototypeOf(Model4.prototype), 'constructor', this).apply(this, arguments); + } + + return Model4; +})(AR); + +exports['default'] = Model4; +module.exports = exports['default']; +},{"../ActiveRecord":1}],17:[function(require,module,exports){ +(function (global){ +/*! + * deep-diff. + * Licensed under the MIT License. + */ +;(function(root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function() { + return factory(); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.DeepDiff = factory(); + } +}(this, function(undefined) { + 'use strict'; + + var $scope, conflict, conflictResolution = []; + if (typeof global === 'object' && global) { + $scope = global; + } else if (typeof window !== 'undefined') { + $scope = window; + } else { + $scope = {}; + } + conflict = $scope.DeepDiff; + if (conflict) { + conflictResolution.push( + function() { + if ('undefined' !== typeof conflict && $scope.DeepDiff === accumulateDiff) { + $scope.DeepDiff = conflict; + conflict = undefined; + } + }); + } + + // nodejs compatible on server side and in the browser. + function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } + + function Diff(kind, path) { + Object.defineProperty(this, 'kind', { + value: kind, + enumerable: true + }); + if (path && path.length) { + Object.defineProperty(this, 'path', { + value: path, + enumerable: true + }); + } + } + + function DiffEdit(path, origin, value) { + DiffEdit.super_.call(this, 'E', path); + Object.defineProperty(this, 'lhs', { + value: origin, + enumerable: true + }); + Object.defineProperty(this, 'rhs', { + value: value, + enumerable: true + }); + } + inherits(DiffEdit, Diff); + + function DiffNew(path, value) { + DiffNew.super_.call(this, 'N', path); + Object.defineProperty(this, 'rhs', { + value: value, + enumerable: true + }); + } + inherits(DiffNew, Diff); + + function DiffDeleted(path, value) { + DiffDeleted.super_.call(this, 'D', path); + Object.defineProperty(this, 'lhs', { + value: value, + enumerable: true + }); + } + inherits(DiffDeleted, Diff); + + function DiffArray(path, index, item) { + DiffArray.super_.call(this, 'A', path); + Object.defineProperty(this, 'index', { + value: index, + enumerable: true + }); + Object.defineProperty(this, 'item', { + value: item, + enumerable: true + }); + } + inherits(DiffArray, Diff); + + function arrayRemove(arr, from, to) { + var rest = arr.slice((to || from) + 1 || arr.length); + arr.length = from < 0 ? arr.length + from : from; + arr.push.apply(arr, rest); + return arr; + } + + function realTypeOf(subject) { + var type = typeof subject; + if (type !== 'object') { + return type; + } + + if (subject === Math) { + return 'math'; + } else if (subject === null) { + return 'null'; + } else if (Array.isArray(subject)) { + return 'array'; + } else if (Object.prototype.toString.call(subject) === '[object Date]') { + return 'date'; + } else if (typeof subject.toString !== 'undefined' && /^\/.*\//.test(subject.toString())) { + return 'regexp'; + } + return 'object'; + } + + function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { + path = path || []; + var currentPath = path.slice(0); + if (typeof key !== 'undefined') { + if (prefilter) { + if (typeof(prefilter) === 'function' && prefilter(currentPath, key)) { return; } + else if (typeof(prefilter) === 'object') { + if (prefilter.prefilter && prefilter.prefilter(currentPath, key)) { return; } + if (prefilter.normalize) { + var alt = prefilter.normalize(currentPath, key, lhs, rhs); + if (alt) { + lhs = alt[0]; + rhs = alt[1]; + } + } + } + } + currentPath.push(key); + } + + // Use string comparison for regexes + if (realTypeOf(lhs) === 'regexp' && realTypeOf(rhs) === 'regexp') { + lhs = lhs.toString(); + rhs = rhs.toString(); + } + + var ltype = typeof lhs; + var rtype = typeof rhs; + if (ltype === 'undefined') { + if (rtype !== 'undefined') { + changes(new DiffNew(currentPath, rhs)); + } + } else if (rtype === 'undefined') { + changes(new DiffDeleted(currentPath, lhs)); + } else if (realTypeOf(lhs) !== realTypeOf(rhs)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (Object.prototype.toString.call(lhs) === '[object Date]' && Object.prototype.toString.call(rhs) === '[object Date]' && ((lhs - rhs) !== 0)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (ltype === 'object' && lhs !== null && rhs !== null) { + stack = stack || []; + if (stack.indexOf(lhs) < 0) { + stack.push(lhs); + if (Array.isArray(lhs)) { + var i, len = lhs.length; + for (i = 0; i < lhs.length; i++) { + if (i >= rhs.length) { + changes(new DiffArray(currentPath, i, new DiffDeleted(undefined, lhs[i]))); + } else { + deepDiff(lhs[i], rhs[i], changes, prefilter, currentPath, i, stack); + } + } + while (i < rhs.length) { + changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++]))); + } + } else { + var akeys = Object.keys(lhs); + var pkeys = Object.keys(rhs); + akeys.forEach(function(k, i) { + var other = pkeys.indexOf(k); + if (other >= 0) { + deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack); + pkeys = arrayRemove(pkeys, other); + } else { + deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack); + } + }); + pkeys.forEach(function(k) { + deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack); + }); + } + stack.length = stack.length - 1; + } + } else if (lhs !== rhs) { + if (!(ltype === 'number' && isNaN(lhs) && isNaN(rhs))) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } + } + } + + function accumulateDiff(lhs, rhs, prefilter, accum) { + accum = accum || []; + deepDiff(lhs, rhs, + function(diff) { + if (diff) { + accum.push(diff); + } + }, + prefilter); + return (accum.length) ? accum : undefined; + } + + function applyArrayChange(arr, index, change) { + if (change.path && change.path.length) { + var it = arr[index], + i, u = change.path.length - 1; + for (i = 0; i < u; i++) { + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + applyArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + delete it[change.path[i]]; + break; + case 'E': + case 'N': + it[change.path[i]] = change.rhs; + break; + } + } else { + switch (change.kind) { + case 'A': + applyArrayChange(arr[index], change.index, change.item); + break; + case 'D': + arr = arrayRemove(arr, index); + break; + case 'E': + case 'N': + arr[index] = change.rhs; + break; + } + } + return arr; + } + + function applyChange(target, source, change) { + if (target && source && change && change.kind) { + var it = target, + i = -1, + last = change.path ? change.path.length - 1 : 0; + while (++i < last) { + if (typeof it[change.path[i]] === 'undefined') { + it[change.path[i]] = (typeof change.path[i] === 'number') ? [] : {}; + } + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + applyArrayChange(change.path ? it[change.path[i]] : it, change.index, change.item); + break; + case 'D': + delete it[change.path[i]]; + break; + case 'E': + case 'N': + it[change.path[i]] = change.rhs; + break; + } + } + } + + function revertArrayChange(arr, index, change) { + if (change.path && change.path.length) { + // the structure of the object at the index has changed... + var it = arr[index], + i, u = change.path.length - 1; + for (i = 0; i < u; i++) { + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + revertArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + it[change.path[i]] = change.lhs; + break; + case 'E': + it[change.path[i]] = change.lhs; + break; + case 'N': + delete it[change.path[i]]; + break; + } + } else { + // the array item is different... + switch (change.kind) { + case 'A': + revertArrayChange(arr[index], change.index, change.item); + break; + case 'D': + arr[index] = change.lhs; + break; + case 'E': + arr[index] = change.lhs; + break; + case 'N': + arr = arrayRemove(arr, index); + break; + } + } + return arr; + } + + function revertChange(target, source, change) { + if (target && source && change && change.kind) { + var it = target, + i, u; + u = change.path.length - 1; + for (i = 0; i < u; i++) { + if (typeof it[change.path[i]] === 'undefined') { + it[change.path[i]] = {}; + } + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + // Array was modified... + // it will be an array... + revertArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + // Item was deleted... + it[change.path[i]] = change.lhs; + break; + case 'E': + // Item was edited... + it[change.path[i]] = change.lhs; + break; + case 'N': + // Item is new... + delete it[change.path[i]]; + break; + } + } + } + + function applyDiff(target, source, filter) { + if (target && source) { + var onChange = function(change) { + if (!filter || filter(target, source, change)) { + applyChange(target, source, change); + } + }; + deepDiff(target, source, onChange); + } + } + + Object.defineProperties(accumulateDiff, { + + diff: { + value: accumulateDiff, + enumerable: true + }, + observableDiff: { + value: deepDiff, + enumerable: true + }, + applyDiff: { + value: applyDiff, + enumerable: true + }, + applyChange: { + value: applyChange, + enumerable: true + }, + revertChange: { + value: revertChange, + enumerable: true + }, + isConflict: { + value: function() { + return 'undefined' !== typeof conflict; + }, + enumerable: true + }, + noConflict: { + value: function() { + if (conflictResolution) { + conflictResolution.forEach(function(it) { + it(); + }); + conflictResolution = null; + } + return accumulateDiff; + }, + enumerable: true + } + }); + + return accumulateDiff; +})); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[8]); diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 3ee74cc..2848234 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -17,10 +17,20 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'd function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } +var _Discriminator = require('./Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +var deep = require('deep-diff').diff; + /** * * Can be inherited or used as is. Holds the model definition, @@ -33,349 +43,451 @@ var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); */ function ActiveRecord(model, name) { - var sl = _ServiceLocator2['default'].instance; - var ActiveRecord = (function () { - function ActiveRecord($injector, rootUrl, options) { - _classCallCheck(this, ActiveRecord); - - this.$injector = $injector; - // this.$http = $injector.get('$http'); - this.rootUrl = rootUrl; - this.build(options); - } - - _createClass(ActiveRecord, [{ - key: 'build', - value: function build(options) { - // istanbul ignore next - - var _this = this; - - _.each(model, function (field, key) { - if (options && (options[key] || options[key] === 0)) { - var name = _.isArray(field) ? field[0].ref : field.ref; - var dao = sl.getDao(name); - if (dao) { - /** - * Deal with populated fields when building a pre-existing object - * - * Some fields might be populated in existing object, but not in the incoming one - * To prevent things from disappearing, determine when new data should overwrite exiting - * - * In the case of an array, look at each incoming value. - * If it is populated, keep it - * If it isn't, try to find the populated value in the existing array, and use it instead. - */ - if (Array.isArray(options[key])) { - var populated = options[key].map(function (value) { - if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { - var found = _this[key].find(function (element) { - return element && element._id === value; - }); - if (found) return found; + var SManager = arguments.length <= 2 || arguments[2] === undefined ? (0, _SessionManager2['default'])(model) : arguments[2]; + return (function () { + var sl = _ServiceLocator2['default'].instance; + var session = new SManager(); + + var ActiveRecord = (function () { + function ActiveRecord(rootUrl, options) { + _classCallCheck(this, ActiveRecord); + + //this.$injector = $injector + this.rootUrl = rootUrl; + this.build(options); + } + + _createClass(ActiveRecord, [{ + key: 'build', + value: function build(options) { + // istanbul ignore next + + var _this = this; + + var sess = {}; + _.each(model, function (field, key) { + if (options && (options[key] || options[key] === 0)) { + var name = _.isArray(field) ? field[0].ref : field.ref; + var dao = sl.getDao(name); + if (dao) { + /** + * Deal with populated fields when building a pre-existing object + * + * Some fields might be populated in existing object, but not in the incoming one + * To prevent things from disappearing, determine when new data should overwrite exiting + * + * In the case of an array, look at each incoming value. + * If it is populated, keep it + * If it isn't, try to find the populated value in the existing array, and use it instead. + */ + if (Array.isArray(options[key])) { + var populated = options[key].map(function (value) { + if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { + var found = _this[key].find(function (element) { + return element && element._id === value; + }); + if (found) return found; + } + return value; + }); + _this[key] = dao.build(_.clone(populated)); + } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { + _this[key] = dao.build(_.clone(options[key])); + } + } else { + _this[key] = _this.buildField(field, options[key]); + } + } + /** Save state of the object for diff purpose */ + if (options && options._id && _this[key]) { + var toSave = undefined; + /** If the field is a ref, only save id or the ids array */ + if ((_.isArray(field) && field[0].ref || field.ref) && + /** Unless it is marked nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + if (_.isArray(field)) { + toSave = _this[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (entry) { + return typeof entry === 'string' ? entry : entry._id; + }); + } else { + toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; + } + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + toSave = _this[key].map(function (e) { + if (e.beforeSave) return e.beforeSave(null, { force: true });else { + console.warn('The values at ' + key + ' should be an ActiveRecord instance for diff purpose'); + return e; + } + }); + } else { + if (_this[key].beforeSave) { + toSave = _this[key].beforeSave(null, { force: true }); + } else { + toSave = _this[key]; + console.warn('The value at ' + key + ' should be an ActiveRecord instance for diff purpose'); } - return value; - }); - _this[key] = dao.build(_.clone(populated)); - } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { - _this[key] = dao.build(_.clone(options[key])); + } + } else { + toSave = _this[key]; } - } else { - _this[key] = _this.buildField(field, options[key]); + sess[key] = _.cloneDeep(toSave); } - } else if (_.isArray(field)) { - _this[key] = []; - } - }); - return this; - } - }, { - key: 'clone', - value: function clone() { - var m = sl.getModel(name); - var ob = new m(this.$injector, this.rootUrl, this); - delete ob._id; - return ob; - } + }); + session.save(sess); + return this; + } + }, { + key: 'clone', + value: function clone() { + var m = sl.getModel(name); + var ob = new m(this.rootUrl, this); + delete ob._id; + return ob; + } - /** - * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! - */ - }, { - key: 'cloneDeep', - value: function cloneDeep() { - var clone = this.clone(); - /** Find ref properties that need their _id to be removed */ - _.each(model, function (v, k) { - if (clone[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Need to clone each */ - clone[k] = clone[k].map(function (e) { - return e.cloneDeep ? e.cloneDeep() : e; - }); + /** + * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! + */ + }, { + key: 'cloneDeep', + value: function cloneDeep() { + var clone = this.clone(); + /** Find ref properties that need their _id to be removed */ + _.each(model, function (v, k) { + if (clone[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Need to clone each */ + clone[k] = clone[k].map(function (e) { + return e.cloneDeep ? e.cloneDeep() : e; + }); + } + } else if (v.ref) { + /** Single nested object, replace it */ + clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } else if (v.ref) { - /** Single nested object, replace it */ - clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } - }); - return clone; - } - }, { - key: 'buildField', - value: function buildField(model, value) { - return _.clone(value); - } - }, { - key: 'archive', - value: function archive() { - return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); - } - }, { - key: 'restore', - value: function restore() { - return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); - } - }, { - key: 'remove', - value: function remove() { - return this['delete'](); - } - }, { - key: 'delete', - value: function _delete() { - return this.$http['delete'](this.rootUrl + '/' + this._id); - } - }, { - key: 'populate', - value: function populate(field, query) { - // istanbul ignore next + }); + return clone; + } + }, { + key: 'buildField', + value: function buildField(model, value) { + return _.clone(value); + } + }, { + key: 'archive', + value: function archive() { + return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); + } + }, { + key: 'restore', + value: function restore() { + return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); + } + }, { + key: 'remove', + value: function remove() { + return this['delete'](); + } + }, { + key: 'delete', + value: function _delete() { + return this.$http['delete'](this.rootUrl + '/' + this._id); + } + }, { + key: 'populate', + value: function populate(field, query, opts) { + // istanbul ignore next - var _this2 = this; + var _this2 = this; - var $q = this.$injector.get('$q'); + var $q = this.$injector.get('$q'); - if (Array.isArray(field)) { - return $q.all(field.map(function (f) { - return _this2.populate(f, query); - })).then(function () { - return _this2; - }); - } + if (Array.isArray(field)) { + return $q.all(field.map(function (f) { + return _this2.populate(f, query); + })).then(function () { + return _this2; + }); + } - var deferred = $q.defer(); - var self = this; - if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { - /** - * The field is an array. It may contain mixed populated and unpopulated data. - * first, sort it out (typeof unpopulated is string) - */ - var grouped = _.groupBy(this[field], function (i) { - return typeof i; - }); - /** No string = everything is populated (or array is empty), we're all set */ - if (!grouped.string) { + var deferred = $q.defer(); + var self = this; + if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { + /** + * The field is an array. It may contain mixed populated and unpopulated data. + * first, sort it out (typeof unpopulated is string) + */ + var grouped = _.groupBy(this[field], function (i) { + return typeof i; + }); + /** No string = everything is populated (or array is empty), we're all set */ + if (!grouped.string) { + deferred.resolve(this[field]); + } else { + /** Resolve the model name and the corresponding DAO */ + var name = model[field][0].ref; + if (!name) deferred.reject('Cannot Populate: unknown model');else { + var dao = sl.getDao(name); + if (!dao) { + deferred.reject('Cannot Populate: unknown DAO'); + } else { + return dao.get(dao.query(query).select(grouped.string), opts).then(function (d) { + /** To preserve order, we map the existing field, replacing only the populated values */ + self[field] = self[field].map(function (f) { + if (typeof f === 'string') { + return _.find(d.data, function (foo) { + return foo._id === f; + }); + } + return f; + }); + return self; + }); + } + } + } + } + + if (!this[field] || typeof this[field] === 'object') { + // The field is empty or already populated. return. deferred.resolve(this[field]); } else { - /** Resolve the model name and the corresponding DAO */ - var name = model[field][0].ref; + var name = model[field].ref; if (!name) deferred.reject('Cannot Populate: unknown model');else { var dao = sl.getDao(name); if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { - /** To preserve order, we map the existing field, replacing only the populated values */ - self[field] = self[field].map(function (f) { - if (typeof f === 'string') { - return _.find(d.data, function (foo) { - return foo._id === f; - }); - } - return f; - }); + return dao.getById(this[field], query, opts).then(function (sub) { + self[field] = sub; return self; }); } } } + + return deferred.promise; } + }, { + key: 'beforeSave', + value: function beforeSave(obj) { + // istanbul ignore next - if (!this[field] || typeof this[field] === 'object') { - // The field is empty or already populated. return. - deferred.resolve(this[field]); - } else { - var name = model[field].ref; - if (!name) deferred.reject('Cannot Populate: unknown model');else { - var dao = sl.getDao(name); - if (!dao) { - deferred.reject('Cannot Populate: unknown DAO'); - } else { - return dao.getById(this[field]).then(function (sub) { - self[field] = sub; - return self; - }); - } + var _this3 = this; + + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ + if (!obj) { + obj = {}; + _.each(_.keys(model), function (k) { + if (!_.isUndefined(_this3[k])) { + obj[k] = _this3[k]; + } + }); } - } + /** Retrieve object saved in session to perform the diff */ + var old = session.retrieve(this._id) || {}; - return deferred.promise; - } - }, { - key: 'beforeSave', - value: function beforeSave(obj) { - obj = obj || _.cloneDeep(this); - _.each(model, function (field, key) { - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + _.each(model, function (field, key) { + if (!_.isUndefined(obj[key])) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || _.isArray(field) && field[0].ref) && + /** Unless it is specifically marked as nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[key] = _.get(obj, [key, '_id']) || obj[key]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + obj[key] = obj[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (e) { + return e.beforeSave ? e.beforeSave(null, { force: true }) : e; + }); + } else { + if (obj[key] && obj[key] !== null) { + obj[key] = obj[key].beforeSave ? obj[key].beforeSave(null, { force: true }) : obj[key]; + } + } + } else if (_.isDate(obj[key])) { + /** Make sure the date is an ISOString */ + obj[key] = new Date(obj[key]).toISOString(); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); - } - }); - delete obj.rootUrl; - delete obj.$injector; - return obj; - } - }, { - key: 'save', - value: function save(populate) { - // istanbul ignore next - - var _this3 = this; - - var toSave = this.beforeSave(); - var callback; - if (populate) { - var dao = sl.getDao(name); - callback = function () { - return dao.getById(_this3._id, dao.query().populate(populate)); - }; - } else { - callback = function (data) { - _this3.build(data.data); - return data; - }; + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ + if (!opts.force && !deep(old[key], obj[key])) { + delete obj[key]; + } + }); + return obj; } + }, { + key: 'save', + value: function save() { + // istanbul ignore next + + var _this4 = this; - if (this._id) { - return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); - } else { - return this.$http.post(this.rootUrl, toSave).then(callback); + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this4 }); + }); + } + var callback; + if (opts.populate) { + var dao = sl.getDao(name); + callback = function () { + return dao.getById(_this4._id, dao.query().populate(opts.populate)); + }; + } else { + callback = function (data) { + _this4.build(data.data); + return data; + }; + } + + if (this._id) { + return this.$http.put(this.rootUrl + '/' + this._id, toSave, opts).then(callback); + } else { + return this.$http.post(this.rootUrl, toSave, opts).then(callback); + } } - } - }, { - key: 'saveDeep', - value: function saveDeep(populate) { - // istanbul ignore next - - var _this4 = this; - - var promises = []; - /** Find ref properties that might need to be saved */ - _.each(model, function (v, k) { - if (_this4[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { - if (!e._id && e.saveDeep) promises.push(e.saveDeep()); - }); + }, { + key: 'saveDeep', + value: function saveDeep(populate) { + // istanbul ignore next + + var _this5 = this; + + var promises = []; + /** Find ref properties that might need to be saved */ + _.each(model, function (v, k) { + if (_this5[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Check if need to save each */ + _this5[k].forEach(function (e) { + if (!e._id && e.saveDeep) promises.push(e.saveDeep()); + }); + } + } else if (v.ref) { + /** Single nested object, save it if needed */ + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } - } else if (v.ref) { - /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); } + }); + var $q = this.$injector.get('$q'); + return $q.all(promises).then(function () { + return _this5.save(populate); + }); + } + }, { + key: '$injector', + get: function get() { + return sl.getInjector(); + } + }, { + key: '$http', + get: function get() { + return this.$injector.get('$http'); + } + }, { + key: '$$pristine', + get: function get() { + return _.isEmpty(this.beforeSave()); + } + }], [{ + key: 'getName', + value: function getName() { + return name; + } + }, { + key: 'makePopObject', + value: function makePopObject(pop) { + return _.map(pop, function (p, k) { + p = Array.isArray(p) ? p[0] : p; + var ob = { path: k }; + var sub = ActiveRecord.findSubPopulates(p.ref); + if (sub && sub.length) { + ob.populate = sub; + } + return ob; + }); + } + }, { + key: 'findSubPopulates', + value: function findSubPopulates(ref) { + var serviceLocator = _ServiceLocator2['default'].instance; + var linkedModel = serviceLocator.getModel(ref); + if (linkedModel && linkedModel !== model) { + return linkedModel.populateParams(); } - }); - var $q = this._injector.get('$q'); - return $q.all(promises).then(function () { - return _this4.save(populate); - }); - } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } - }, { - key: '$http', - get: function get() { - return this.$injector.get('$http'); - } - }], [{ - key: 'getName', - value: function getName() { - return name; - } - }, { - key: 'makePopObject', - value: function makePopObject(pop) { - return _.map(pop, function (p, k) { - p = Array.isArray(p) ? p[0] : p; - var ob = { path: k }; - var sub = ActiveRecord.findSubPopulates(p.ref); - if (sub && sub.length) { - ob.populate = sub; + } + }, { + key: 'populateParams', + value: function populateParams(populateArray) { + + // Get from the model the fields that reference another object + var pop = _.pick(model, function (v) { + if (_.isArray(v)) return v[0].ref && !v[0].nested; + return v.ref && !v.nested; + }); + + if (populateArray === 'all') { + return ActiveRecord.makePopObject(pop); } - return ob; - }); - } - }, { - key: 'findSubPopulates', - value: function findSubPopulates(ref) { - var serviceLocator = _ServiceLocator2['default'].instance; - var linkedModel = serviceLocator.getModel(ref); - if (linkedModel && linkedModel !== model) { - return linkedModel.populateParams(); + var toPopulate = populateArray ? _.pick(pop, function (p, k) { + return _.contains(populateArray, k); + }) : _.pick(pop, function (v) { + return v.populateDefault || v[0] && v[0].populateDefault; + }); + return ActiveRecord.makePopObject(toPopulate); } - } - }, { - key: 'populateParams', - value: function populateParams(populateArray) { - - // Get from the model the fields that reference another object - var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; - }); - - if (populateArray === 'all') { - return ActiveRecord.makePopObject(pop); + }, { + key: 'getModel', + value: function getModel() { + return model; } - var toPopulate = populateArray ? _.pick(pop, function (p, k) { - return _.contains(populateArray, k); - }) : _.pick(pop, function (v) { - return v.populateDefault || v[0] && v[0].populateDefault; - }); - return ActiveRecord.makePopObject(toPopulate); - } - }, { - key: 'getModel', - value: function getModel() { - return model; - } - }]); + }, { + key: 'getSession', + value: function getSession() { + return session; + } + }]); + + return ActiveRecord; + })(); return ActiveRecord; })(); - // sl.registerModel(name, ActiveRecord); - return ActiveRecord; } module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/DaoHelper.js b/dest/temp/DaoHelper.js index e654d49..af97c14 100644 --- a/dest/temp/DaoHelper.js +++ b/dest/temp/DaoHelper.js @@ -49,6 +49,15 @@ var DaoHelper = (function () { value: function setRootUrl(url) { this.dao.url = url; } + }, { + key: 'setDiscriminatorUrl', + value: function setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, function (discriminator) { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url; + } + }); + } }, { key: '$get', value: function $get() { @@ -63,7 +72,8 @@ var DaoHelper = (function () { key: 'registerService', value: function registerService(module, name, dao) { module.provider(name, DaoHelper.getProvider(dao)).run([name, '$injector', function (service, $injector) { - service.setInjector($injector); + _ServiceLocator2['default'].instance.registerInjector($injector); + //service.setInjector($injector); }]); } }]); diff --git a/dest/temp/Discriminator.js b/dest/temp/Discriminator.js new file mode 100644 index 0000000..120325a --- /dev/null +++ b/dest/temp/Discriminator.js @@ -0,0 +1,60 @@ +//_ = require('lodash'); +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +exports['default'] = Discriminator; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +function Discriminator(Model, type) { + + var Discriminator = (function (_Model) { + _inherits(Discriminator, _Model); + + function Discriminator() { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).apply(this, arguments); + } + + _createClass(Discriminator, null, [{ + key: 'type', + get: function get() { + return type; + } + }]); + + return Discriminator; + })(Model); + + return Discriminator; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/GenericDao.js b/dest/temp/GenericDao.js index dd7dc31..e47179c 100644 --- a/dest/temp/GenericDao.js +++ b/dest/temp/GenericDao.js @@ -24,25 +24,19 @@ var _QueryBuilder = require('./QueryBuilder'); var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); -function GenericDao(model, qb) { +function GenericDao(model, qb, discriminators) { var sl = _ServiceLocator2['default'].instance; sl.registerModel(model.getName(), model); var myClass = (function () { function myClass(url) { _classCallCheck(this, myClass); - // this.$injector = $injector; - // this.$http = $injector.get('$http'); this.url = url; this.model = model; + this.discriminators = discriminators; } _createClass(myClass, [{ - key: 'setInjector', - value: function setInjector($injector) { - this.$injector = $injector; - } - }, { key: 'getModel', value: function getModel() { return model; @@ -59,13 +53,17 @@ function GenericDao(model, qb) { } }, { key: 'getHeaders', - value: function getHeaders() {} + value: function getHeaders() { + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + return opts.headers; + } }, { key: 'getOptions', value: function getOptions() { var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } }, { @@ -113,17 +111,38 @@ function GenericDao(model, qb) { if (Array.isArray(data)) { return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + if (this.discriminators && data.__t) { + var disc = _.find(this.discriminators, { type: data.__t }); + if (disc) { + return new disc(disc.discriminatorUrl, data); + } + } + return new model(this.url, data); } }, { key: 'create', value: function create(params) { - return new this.model(this.$injector, this.url, params); + if (this.discriminators && params.__t) { + var disc = _.find(this.discriminators, { type: params.__t }); + if (disc) { + return new disc(disc.discriminatorUrl, params); + } + } + return new this.model(this.url, params); + } + + // get discriminators(){ + // return discriminators + // } + }, { + key: '$injector', + get: function get() { + return sl.getInjector(); } }, { key: '$http', get: function get() { - return this.$injector.get('$http'); + return sl.getInjector().get('$http'); } }]); @@ -147,20 +166,20 @@ function GenericDao(model, qb) { var _this2 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query : arguments[1]; + var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - return this.$http.get(this.url + '/' + value, { params: qb.opts }).then(function (data) { - return new model(_this2.$injector, _this2.url, data.data); + opts = this.getOptions(opts); + return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { + return new model(_this2.url, data.data); }); }; } else { myClass.prototype['selectBy' + _.capitalize(key)] = function (toSelect) { - // istanbul ignore next - - var _this3 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); if (toSelect && toSelect.length) { if (value.ref) { toSelect = extractId(toSelect); @@ -175,14 +194,7 @@ function GenericDao(model, qb) { } qb.setQuery(obj); } - return this.$http.get(this.url, { params: qb.opts }).then(function (data) { - if (!data.data) { - data.data = []; - } - return { - data: data.data.map(_this3.build, _this3), meta: { total: data.headers('X-Total-Count') } - }; - }); + return this.get(qb, opts); }; } //myClass.prototype diff --git a/dest/temp/ServiceLocator.js b/dest/temp/ServiceLocator.js index 1f1da93..899b748 100644 --- a/dest/temp/ServiceLocator.js +++ b/dest/temp/ServiceLocator.js @@ -42,6 +42,11 @@ var ServiceLocator = (function () { value: function getDao(name) { return this.daoRegistry[name]; } + }, { + key: "getInjector", + value: function getInjector() { + return this.injector; + } }, { key: "registerDao", value: function registerDao(name, dao) { @@ -52,6 +57,11 @@ var ServiceLocator = (function () { value: function registerModel(name, model) { this.modelRegistry[name] = model; } + }, { + key: "registerInjector", + value: function registerInjector(injector) { + this.injector = injector; + } }], [{ key: "instance", get: function get() { diff --git a/dest/temp/SessionManager.js b/dest/temp/SessionManager.js new file mode 100644 index 0000000..8ea8794 --- /dev/null +++ b/dest/temp/SessionManager.js @@ -0,0 +1,60 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +exports['default'] = SessionManager; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +function SessionManager(model) { + + var SessionManager = (function () { + function SessionManager() { + _classCallCheck(this, SessionManager); + + this.storage = {}; + } + + _createClass(SessionManager, [{ + key: 'save', + value: function save(obj) { + if (!obj._id) return; + this.storage[obj._id] = obj; + } + }, { + key: 'retrieve', + value: function retrieve(id) { + return this.storage[id]; + } + }, { + key: 'clean', + value: function clean() { + this.storage = {}; + } + }, { + key: 'model', + get: function get() { + return model; + } + }]); + + return SessionManager; + })(); + return SessionManager; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/app.js b/dest/temp/app.js index b2ba792..6375294 100644 --- a/dest/temp/app.js +++ b/dest/temp/app.js @@ -16,7 +16,17 @@ var _managersTstManager2 = require('./managers/tstManager2'); var _managersTstManager22 = _interopRequireDefault(_managersTstManager2); +var _managersTstManager3 = require('./managers/tstManager3'); + +var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); + +var _managersTstManager4 = require('./managers/tstManager4'); + +var _managersTstManager42 = _interopRequireDefault(_managersTstManager4); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); -_DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); \ No newline at end of file +_DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); +_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); +_DaoHelper2['default'].registerService(_module, 'ModelManager4', _managersTstManager42['default']); \ No newline at end of file diff --git a/dest/temp/managers/tstManager3.js b/dest/temp/managers/tstManager3.js new file mode 100644 index 0000000..7e6d774 --- /dev/null +++ b/dest/temp/managers/tstManager3.js @@ -0,0 +1,50 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel3Js = require('./../models/tstModel3.js'); + +var _modelsTstModel3Js2 = _interopRequireDefault(_modelsTstModel3Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _QueryBuilder = require('../QueryBuilder'); + +var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel3Js2['default']); + +var ModelManager3 = (function (_DAO) { + _inherits(ModelManager3, _DAO); + + function ModelManager3() { + _classCallCheck(this, ModelManager3); + + _get(Object.getPrototypeOf(ModelManager3.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager3; +})(DAO); + +exports['default'] = ModelManager3; +; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/managers/tstManager4.js b/dest/temp/managers/tstManager4.js new file mode 100644 index 0000000..7e718b0 --- /dev/null +++ b/dest/temp/managers/tstManager4.js @@ -0,0 +1,53 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel4Js = require('./../models/tstModel4.js'); + +var _modelsTstModel4Js2 = _interopRequireDefault(_modelsTstModel4Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _Discriminator = require('../Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + +var D1 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type1'); +var D2 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type2'); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel4Js2['default'], undefined, [D1, D2]); + +var ModelManager4 = (function (_DAO) { + _inherits(ModelManager4, _DAO); + + function ModelManager4() { + _classCallCheck(this, ModelManager4); + + _get(Object.getPrototypeOf(ModelManager4.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager4; +})(DAO); + +exports['default'] = ModelManager4; +; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/models/tstModel1.js b/dest/temp/models/tstModel1.js index 297d139..ab5a44b 100644 --- a/dest/temp/models/tstModel1.js +++ b/dest/temp/models/tstModel1.js @@ -31,6 +31,8 @@ var model = { }, //private: true + when: Date, + label: String, num: Number, @@ -43,7 +45,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; var AR = (0, _ActiveRecord2['default'])(model, 'Model1'); diff --git a/dest/temp/models/tstModel3.js b/dest/temp/models/tstModel3.js new file mode 100644 index 0000000..cf6de90 --- /dev/null +++ b/dest/temp/models/tstModel3.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model3'); + +var Model3 = (function (_AR) { + _inherits(Model3, _AR); + + function Model3() { + _classCallCheck(this, Model3); + + _get(Object.getPrototypeOf(Model3.prototype), 'constructor', this).apply(this, arguments); + } + + return Model3; +})(AR); + +exports['default'] = Model3; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/models/tstModel4.js b/dest/temp/models/tstModel4.js new file mode 100644 index 0000000..3ff267d --- /dev/null +++ b/dest/temp/models/tstModel4.js @@ -0,0 +1,54 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String, + + __t: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model4'); + +var Model4 = (function (_AR) { + _inherits(Model4, _AR); + + function Model4() { + _classCallCheck(this, Model4); + + _get(Object.getPrototypeOf(Model4.prototype), 'constructor', this).apply(this, arguments); + } + + return Model4; +})(AR); + +exports['default'] = Model4; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-base.specs.js b/dest/temp/specs/angular-dao-base.specs.js new file mode 100644 index 0000000..7f10126 --- /dev/null +++ b/dest/temp/specs/angular-dao-base.specs.js @@ -0,0 +1,98 @@ +'use strict'; + +describe('Angular DAO Basics', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('should have a modelManager', function () { + expect(ModelManager).toBeDefined(); + }); + + it('should query for the list', function () { + httpBackend.expectGET('http://MOCKURL.com/model1').respond(); + ModelManager.get(); + }); + + it('should query for one element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); + ModelManager.getById('ididid'); + }); + + it('should deserialize numbers', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); + ModelManager.getById('ididid').then(function (data) { + expect(data.num).toBeDefined(); + }); + httpBackend.flush(); + }); + + it('should provide with a query builder', function () { + var qb = ModelManager.query(); + expect(qb).toBeDefined(); + }); + + it('Should have created field specific methods', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); + ModelManager.selectByLabel('toto'); + httpBackend.flush(); + }); + + it('Should have created field specific methods and extract object', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([{ _id: "2" }]); + httpBackend.flush(); + }); + it('Should have created field specific methods and extract object with key', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2(["2"]); + httpBackend.flush(); + }); + + it('Should return an ActiveRecord element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ + _id: '1234656' + }); + ModelManager.getById('123').then(function (data) { + expect(data.save).toBeDefined(); + }); + httpBackend.flush(); + }); + + it('Should build an empty array when server return 204', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1')).respond(function (method, url, data) { + return [204, undefined, { 'X-Total-Count': 0 }]; + }); + ModelManager.get().then(function (data) { + expect(data).toEqual({ data: [] }); + }); + httpBackend.flush(); + }); + + it('Should not fail when a nested property is undefined', function () { + var m = ModelManager.create({ + _id: "111", + models2: [{ _id: "121212" }, undefined, { _id: null }, null], + model2: undefined + }); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-clone.specs.js b/dest/temp/specs/angular-dao-clone.specs.js new file mode 100644 index 0000000..8d5962d --- /dev/null +++ b/dest/temp/specs/angular-dao-clone.specs.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should have a clone function', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + var clone = model.clone(); + expect(clone._id).not.toBeDefined(); + }); + + it('Should Clone Deep', function () { + var model = ModelManager.create({ + _id: '123456', + model2: { + _id: '1111', + name: 'toto' + }, + models2: [{ + _id: '2222', + name: 'tutu' + }, { + _id: '3333', + name: 'titi' + }, '444'] + }); + var clone = model.cloneDeep(); + expect(clone._id).not.toBeDefined(); + expect(clone.model2._id).not.toBeDefined(); + expect(clone.models2[0]._id).not.toBeDefined(); + expect(clone.models2[1]._id).not.toBeDefined(); + expect(clone.models2[2]).toEqual('444'); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-discriminators.specs.js b/dest/temp/specs/angular-dao-discriminators.specs.js new file mode 100644 index 0000000..39d45fb --- /dev/null +++ b/dest/temp/specs/angular-dao-discriminators.specs.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManager4Provider) { + ModelManager4Provider.setRootUrl('http://MOCKURL.com/model2'); + ModelManager4Provider.setDiscriminatorUrl('Type1', 'http://fakeurl/type1'); + ModelManager4Provider.setDiscriminatorUrl('Type2', 'http://fakeurl/type2'); + }); + + inject(function (_ModelManager4_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager4 = _ModelManager4_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + it('should be possible to instantiate discriminators', function () { + expect(ModelManager4.discriminators.length).toEqual(2); + expect(ModelManager4.discriminators[0].type).toEqual('Type1'); + expect(ModelManager4.discriminators[1].type).toEqual('Type2'); + expect(ModelManager4.discriminators[0].discriminatorUrl).toEqual('http://fakeurl/type1'); + expect(ModelManager4.discriminators[1].discriminatorUrl).toEqual('http://fakeurl/type2'); + }); + + it('should make GET queries on parent url', function () { + httpBackend.expectGET('http://MOCKURL.com/model2').respond(); + ModelManager4.get(); + httpBackend.flush(); + }); + + it('should instanciate discriminators according to __t value', function () { + // TODO : make a GET request, respond with two types, and check their urls + httpBackend.expectGET('http://MOCKURL.com/model2').respond([{ _id: 123, name: 'Type1', __t: 'Type1' }, { _id: 321, name: 'Type2', __t: 'Type2' }]); + + ModelManager4.get().then(function (data) { + expect(data.data[0].rootUrl).toEqual('http://fakeurl/type1'); + expect(data.data[1].rootUrl).toEqual('http://fakeurl/type2'); + }); + httpBackend.flush(); + }); + + it('should make POST queries on correct discriminator URL', function () { + var obj = ModelManager4.createModel({ _id: 123456, name: '12345', __t: 'Type1' }); + obj.name = '23456'; + httpBackend.expectPUT('http://fakeurl/type1/123456', { name: '23456' }).respond(); + obj.save(); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-populate.specs.js b/dest/temp/specs/angular-dao-populate.specs.js new file mode 100644 index 0000000..194ac32 --- /dev/null +++ b/dest/temp/specs/angular-dao-populate.specs.js @@ -0,0 +1,186 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + + it('Should make subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + model.populate('models2').then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.models2[2]._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('Should not make useless subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' }] + }); + model.populate('models2').then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('888'); + }); + }); + + it('Should make subPopulate queries on several fields', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: ['77777', '4444'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('should populate again after save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: ['999'] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888', + models2: ['999'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [{ _id: '999' }] + }); + model.save({ force: true, populate: ['model2', 'models2'] }).then(function (pop) { + expect(pop.models2[0]._id).toEqual('999'); + }); + httpBackend.flush(); + }); + + it('should not override populated values on save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888' + }); + model.save({ force: true }).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('should override if populated value isnt the same', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '777' + }); + model.save({ force: true }).then(function () { + expect(model.model2).toEqual('777'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays on save', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['888'] + }); + model.save({ force: true }).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays values, but be able to add newly added values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['888', '999'] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2); + expect(model.models2[0]._id).toEqual('888'); + expect(model.models2[0].name).toEqual('tutu'); + expect(model.models2[1]).toEqual('999'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays values, but detect deleted values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['999', '111'] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2); + expect(model.models2[0]).toEqual('999'); + expect(model.models2[1]._id).toEqual('111'); + expect(model.models2[1].name).toEqual('toto'); + }); + httpBackend.flush(); + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-queries.specs.js b/dest/temp/specs/angular-dao-queries.specs.js new file mode 100644 index 0000000..5052054 --- /dev/null +++ b/dest/temp/specs/angular-dao-queries.specs.js @@ -0,0 +1,148 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should query by Id if not key specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select('2')); + httpBackend.flush(); + }); + + it('Should query directly if array size === 1', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select(['2'])); + httpBackend.flush(); + }); + + it('Should query with $in operator', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'])); + httpBackend.flush(); + }); + + it('Should query on another field if specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select(['2'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two single queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two arrays queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4', '5'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, single then array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select(['3', '4'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, array then single', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select('4', 'label')); + httpBackend.flush(); + }); + it('Should merge queries on the same field, array then single-array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4'], 'label')); + httpBackend.flush(); + }); + + it('Should query for archives', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); + ModelManager.get(ModelManager.query().archived(true)); + httpBackend.flush(); + }); + + it('Should query for deleted', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); + ModelManager.get(ModelManager.query().deleted(true)); + httpBackend.flush(); + }); + + it('Should paginate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); + ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); + httpBackend.flush(); + }); + it('Should limit', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); + ModelManager.get(ModelManager.query().limit(10)); + httpBackend.flush(); + }); + + it('Should sort', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); + ModelManager.get(ModelManager.query().sort("toto")); + httpBackend.flush(); + }); + + it('Should select specific fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); + ModelManager.get(ModelManager.query().fields(["toto", "tutu"])); + httpBackend.flush(); + }); + + it('Should search', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto')); + httpBackend.flush(); + }); + + it('Should search on another field', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', 'label')); + httpBackend.flush(); + }); + + it('Should search on multiple fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', ['name', 'label'])); + httpBackend.flush(); + }); + + it('Should count', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); + ModelManager.count(); + httpBackend.flush(); + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-save.specs.js b/dest/temp/specs/angular-dao-save.specs.js new file mode 100644 index 0000000..406a120 --- /dev/null +++ b/dest/temp/specs/angular-dao-save.specs.js @@ -0,0 +1,257 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('should prepare to save refs and nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] + }); + model.model3 = ModelManager3.create({ _id: '00002' }); + model.models3 = [ModelManager3.create({ _id: '0001' })]; + expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); + }); + + it('should diff nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }], + model3: { _id: '00002' }, + models3: [{ _id: '0001' }] + }); + expect(model.beforeSave()).toEqual({}); + }); + + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: ['7777'], + label: 'toto' + }); + model.models2.push('8888'); + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should save new objects', function () { + var model = ModelManager.createModel({ + model2: '77777' + }); + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + expect(model.$$pristine).toBeTruthy(); + model.save(); + }); + + it('Should detect change when change is falsy', function () { + var model = ModelManager.createModel({ + _id: '123456', + label: 'AZE', + num: 10 + }); + model.label = ''; + model.num = 0; + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: '', num: 0 }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should deep nested objects', function () { + var model = ModelManager.createModel({ + label: 'toto', + model2: { name: 'toto' } + }); + httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); + httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); + model.saveDeep(); + httpBackend.flush(); + expect(model.model2._id).toEqual('1111'); + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + }); + + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }); + model.save(); + }); + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }); + model.when = new Date(model.when); + model.save(); + }); + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }); + model.when = new Date(203939); + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save(); + }); + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: ['77777', '4444'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.model2 = { _id: '999' }; + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush(); + model.models2.push({ _id: '999' }); + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save(); + }); + + it('Should not save irrelevant fields', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am'; + model.save(); + }); + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + model.iShouldntBeThere = 'but I am'; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('should allow to save a nested field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model3: { + _id: '23456', + name: 'toto' + } + }); + + model.model3 = null; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model3: null }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('should allow to save a populated field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: { + _id: '234567', + name: 'toto' + } + }); + + model.model2 = null; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model2: null }).respond(); + model.save(); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js deleted file mode 100644 index f78a7f4..0000000 --- a/dest/temp/specs/angular-dao.specs.js +++ /dev/null @@ -1,414 +0,0 @@ -'use strict'; - -describe('Angular DAO', function () { - - var ModelManager, httpBackend, $rootScope, $timeout; - - beforeEach(function () { - - module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { - ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); - ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); - }); - - inject(function (_ModelManager_, $httpBackend, _$rootScope_, _$timeout_) { - ModelManager = _ModelManager_; - httpBackend = $httpBackend; - $rootScope = _$rootScope_; - $timeout = _$timeout_; - }); - }); - - afterEach(function () { - httpBackend.verifyNoOutstandingExpectation(); - }); - - it('should have a modelManager', function () { - expect(ModelManager).toBeDefined(); - }); - - it('should query for the list', function () { - httpBackend.expectGET('http://MOCKURL.com/model1').respond(); - ModelManager.get(); - }); - - it('should query for one element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); - ModelManager.getById('ididid'); - }); - - it('should deserialize numbers', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); - ModelManager.getById('ididid').then(function (data) { - expect(data.num).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('should provide with a query builder', function () { - var qb = ModelManager.query(); - expect(qb).toBeDefined(); - }); - - it('Should have created field specific methods', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); - ModelManager.selectByLabel('toto'); - httpBackend.flush(); - }); - - it('Should have created field specific methods and extract object', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([{ _id: "2" }]); - httpBackend.flush(); - }); - it('Should have created field specific methods and extract object with key', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2(["2"]); - httpBackend.flush(); - }); - - it('Should return an ActiveRecord element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ - _id: '1234656' - }); - ModelManager.getById('123').then(function (data) { - expect(data.save).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('Should query by Id if not key specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select('2')); - httpBackend.flush(); - }); - - it('Should query directly if array size === 1', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select(['2'])); - httpBackend.flush(); - }); - it('Should query with $in operator', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'])); - httpBackend.flush(); - }); - - it('Should query on another field if specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select(['2'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two single queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two arrays queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4', '5'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, single then array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select(['3', '4'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, array then single', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select('4', 'label')); - httpBackend.flush(); - }); - it('Should merge queries on the same field, array then single-array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4'], 'label')); - httpBackend.flush(); - }); - - it('Should query for archives', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); - ModelManager.get(ModelManager.query().archived(true)); - httpBackend.flush(); - }); - - it('Should query for deleted', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); - ModelManager.get(ModelManager.query().deleted(true)); - httpBackend.flush(); - }); - - it('Should paginate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); - ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); - httpBackend.flush(); - }); - it('Should limit', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); - ModelManager.get(ModelManager.query().limit(10)); - httpBackend.flush(); - }); - - it('Should sort', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); - ModelManager.get(ModelManager.query().sort("toto")); - httpBackend.flush(); - }); - - it('Should select specific fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); - ModelManager.get(ModelManager.query().fields(["toto", "tutu"])); - httpBackend.flush(); - }); - - it('Should search', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto')); - httpBackend.flush(); - }); - - it('Should search on another field', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', 'label')); - httpBackend.flush(); - }); - - it('Should search on multiple fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', ['name', 'label'])); - httpBackend.flush(); - }); - - it('Should count', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); - ModelManager.count(); - httpBackend.flush(); - }); - - it('Should make subPopulate queries', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); - model.populate('model2'); - httpBackend.flush(); - }); - - it('Should have a clone function', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - var clone = model.clone(); - expect(clone._id).not.toBeDefined(); - }); - - it('Should Clone Deep', function () { - var model = ModelManager.create({ - _id: '123456', - model2: { - _id: '1111', - name: 'toto' - }, - models2: [{ - _id: '2222', - name: 'tutu' - }, { - _id: '3333', - name: 'titi' - }, '444'] - }); - var clone = model.cloneDeep(); - expect(clone._id).not.toBeDefined(); - expect(clone.model2._id).not.toBeDefined(); - expect(clone.models2[0]._id).not.toBeDefined(); - expect(clone.models2[1]._id).not.toBeDefined(); - expect(clone.models2[2]).toEqual('444'); - }); - - it('Should make subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); - model.populate('models2').then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('4444'); - expect(model.models2[2]._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('Should not make useless subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' }] - }); - model.populate('models2').then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('888'); - }); - }); - - it('Should make subPopulate queries on several fields', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: ['77777', '4444'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate(['models2', 'model2']).then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('should populate again after save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: ['999'] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888', - models2: ['999'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [{ _id: '999' }] - }); - model.save(['model2', 'models2']).then(function (pop) { - expect(pop.models2[0]._id).toEqual('999'); - }); - httpBackend.flush(); - }); - - it('should not override populated values on save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888' - }); - model.save().then(function () { - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('should override if populated value isnt the same', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '777' - }); - model.save().then(function () { - expect(model.model2).toEqual('777'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays on save', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['888'] - }); - model.save().then(function () { - expect(model.models2[0]._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays values, but be able to add newly added values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['888', '999'] - }); - model.save().then(function () { - expect(model.models2.length).toEqual(2); - expect(model.models2[0]._id).toEqual('888'); - expect(model.models2[0].name).toEqual('tutu'); - expect(model.models2[1]).toEqual('999'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays values, but detect deleted values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['999', '111'] - }); - model.save().then(function () { - expect(model.models2.length).toEqual(2); - expect(model.models2[0]).toEqual('999'); - expect(model.models2[1]._id).toEqual('111'); - expect(model.models2[1].name).toEqual('toto'); - }); - httpBackend.flush(); - }); - - it('Should save objects', function () { - var model = ModelManager.createModel({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656').respond(); - model.save(); - httpBackend.flush(); - }); - - it('Should deep nested objects', function () { - var model = ModelManager.createModel({ - label: 'toto', - model2: { name: 'toto' } - }); - httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); - httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); - model.saveDeep(); - httpBackend.flush(); - expect(model.model2._id).toEqual('1111'); - }); - - it('Should make a query with populate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); - ModelManager.get(ModelManager.query().populate('model2')); - httpBackend.flush(); - }); - - it('Should build an empty array when server return 204', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1')).respond(function (method, url, data) { - return [204, undefined, { 'X-Total-Count': 0 }]; - }); - ModelManager.get().then(function (data) { - expect(data).toEqual({ data: [] }); - }); - httpBackend.flush(); - }); -}); \ No newline at end of file diff --git a/package.json b/package.json index 12aab9e..7963434 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { "author": "flogou ", "name": "angular-orm", - "version": "1.2.0", + "version": "2.0.7", "description": "", "homepage": "", - "dependencies": {}, + "dependencies": { + "deep-diff": "0.3.4" + }, "files": [ "src/**/*.js" ], diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 9e0956b..8873d9c 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -1,5 +1,8 @@ //_ = require('lodash'); +import Discriminator from './Discriminator' import ServiceLocator from './ServiceLocator' +import SessionManager from './SessionManager' +var deep = require('deep-diff').diff /** * @@ -11,17 +14,23 @@ import ServiceLocator from './ServiceLocator' * @param name String Name of the Model (must be the same as described in 'ref' of others models relations. * @returns {$ES6_CLASS$} */ -export default function ActiveRecord (model, name) { +export default function ActiveRecord (model, name, SManager = SessionManager(model)) { let sl = ServiceLocator.instance; + let session = new SManager() + let ActiveRecord = class { - constructor ($injector, rootUrl, options) { - this.$injector = $injector; - // this.$http = $injector.get('$http'); - this.rootUrl = rootUrl; + constructor (rootUrl, options) { + //this.$injector = $injector + this.rootUrl = rootUrl this.build(options); } + get $injector(){ + return sl.getInjector() + } + build (options) { + let sess = {} _.each(model, (field, key)=> { if (options && (options[ key ] || options[ key ] === 0)) { var name = _.isArray(field) ? field[ 0 ].ref : field.ref; @@ -59,17 +68,51 @@ export default function ActiveRecord (model, name) { } else { this[ key ] = this.buildField(field, options[ key ]) } - } else if (_.isArray(field)) { - this[ key ] = []; + } + /** Save state of the object for diff purpose */ + if (options && options._id && this[ key ]) { + let toSave + /** If the field is a ref, only save id or the ids array */ + if ((_.isArray(field) && field[ 0 ].ref || field.ref) + /** Unless it is marked nested */ + && !(field.nested || (_.isArray(field) && field[ 0 ].nested))) { + if (_.isArray(field)) { + toSave = this[ key ].filter(e => !_.isUndefined(e)).map(entry => (typeof entry === 'string') ? entry : entry._id) + } else { + toSave = typeof this[ key ] === 'string' ? this[ key ] : this[ key ]._id + } + } else if (field.nested || (_.isArray(field) && field[ 0 ].nested)) { + if (_.isArray(field)){ + toSave = this[ key ].map(e => { + if (e.beforeSave) + return e.beforeSave(null, {force: true}) + else { + console.warn(`The values at ${key} should be an ActiveRecord instance for diff purpose`); + return e; + } + }) + } else { + if (this[ key ].beforeSave){ + toSave = this[ key ].beforeSave(null, {force: true}) + } else { + toSave = this[ key ] + console.warn(`The value at ${key} should be an ActiveRecord instance for diff purpose`); + } + } + } else { + toSave = this[ key ] + } + sess[ key ] = _.cloneDeep(toSave) } }); + session.save(sess) return this; } clone () { var m = sl.getModel(name); - var ob = new m(this.$injector, this.rootUrl, this); + var ob = new m(this.rootUrl, this); delete ob._id; return ob; } @@ -96,21 +139,16 @@ export default function ActiveRecord (model, name) { return clone } - get $injector () { - return this._injector; - } - - set $injector ($injector) { - this._injector = $injector; - } - get $http () { return this.$injector.get('$http'); } + get $$pristine () { + return _.isEmpty(this.beforeSave()) + } + buildField (model, value) { return _.clone(value); - } archive () { @@ -129,7 +167,7 @@ export default function ActiveRecord (model, name) { return this.$http.delete(this.rootUrl + '/' + this._id); } - populate (field, query) { + populate (field, query, opts) { var $q = this.$injector.get('$q'); if (Array.isArray(field)) { @@ -157,7 +195,7 @@ export default function ActiveRecord (model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then((d)=> { + return dao.get(dao.query(query).select(grouped.string), opts).then((d)=> { /** To preserve order, we map the existing field, replacing only the populated values */ self[ field ] = self[ field ].map((f) => { if (typeof f === 'string') { @@ -184,7 +222,7 @@ export default function ActiveRecord (model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.getById(this[ field ]).then(function (sub) { + return dao.getById(this[ field ], query, opts).then(function (sub) { self[ field ] = sub; return self; }); @@ -197,39 +235,77 @@ export default function ActiveRecord (model, name) { } - beforeSave (obj) { - obj = obj || _.cloneDeep(this); + beforeSave (obj, opts = {}) { + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ + if (!obj) { + obj = {} + _.each(_.keys(model), k => {if (!_.isUndefined(this[ k ])) { obj[ k ] = this[ k ]}}) + } + /** Retrieve object saved in session to perform the diff */ + let old = session.retrieve(this._id) || {} + _.each(model, (field, key)=> { - if (obj[ key ] && (field.ref || (_.isArray(field) && field[ 0 ].ref))) { - - if (_.isArray(obj[ key ])) { - obj[ key ] = _.compact(obj[ key ].map((val) => { - if (typeof(val) === 'object') { - return field.nested ? val : val._id; - } else if (typeof(val) === 'string') { - return val; + if (!_.isUndefined(obj [ key ])) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || (_.isArray(field) && field[ 0 ].ref)) + /** Unless it is specifically marked as nested */ + && !(field.nested || (_.isArray(field) && field[ 0 ].nested )) + ) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[ key ])) { + obj[ key ] = _.compact(obj[ key ].map((val) => { + if (typeof(val) === 'object') { + return field.nested ? val : val._id; + } else if (typeof(val) === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[ key ] = _.get(obj, [ key , '_id']) || obj[ key ]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || (_.isArray(field) && field[ 0 ].nested )) { + if (_.isArray(field)) { + obj[ key ] = obj[ key ] + .filter(e => !_.isUndefined(e)) + .map(e => e.beforeSave ? e.beforeSave(null, { force: true }) : e) + } else { + if (obj[ key ] && obj[ key ] !== null) { + obj[ key ] = obj[ key ].beforeSave ? obj[ key ].beforeSave(null, { force: true }) : obj[ key ] } - })); - } else { - obj[ key ] = obj[ key ]._id || obj[ key ]; + } + } else if (_.isDate(obj[ key ])) { + /** Make sure the date is an ISOString */ + obj[ key ] = new Date(obj[ key ]).toISOString(); } - } else if (obj[ key ] && (field.type === Date || (_.isArray(field) && field[ 0 ].type === Date))) { - obj[ key ] = new Date(moment(obj[ key ])).toISOString(); + } + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ + if (!opts.force && !deep(old[ key ], obj[ key ])) { + delete obj[ key ] } }); - delete obj.rootUrl; - delete obj.$injector; return obj; - } - save (populate) { - var toSave = this.beforeSave(); + save (opts = {}) { + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(resolve => resolve({ data: this })) + } var callback; - if (populate) { + if (opts.populate) { var dao = sl.getDao(name); callback = ()=> { - return dao.getById(this._id, dao.query().populate(populate)); + return dao.getById(this._id, dao.query().populate(opts.populate)); } } else { callback = (data) => { @@ -239,9 +315,9 @@ export default function ActiveRecord (model, name) { } if (this._id) { - return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback) + return this.$http.put(this.rootUrl + '/' + this._id, toSave, opts).then(callback) } else { - return this.$http.post(this.rootUrl, toSave).then(callback); + return this.$http.post(this.rootUrl, toSave, opts).then(callback); } } @@ -262,7 +338,7 @@ export default function ActiveRecord (model, name) { } } }) - var $q = this._injector.get('$q') + var $q = this.$injector.get('$q') return $q.all(promises).then(() => { return this.save(populate) }) @@ -297,8 +373,8 @@ export default function ActiveRecord (model, name) { // Get from the model the fields that reference another object var pop = _.pick(model, function (v) { if (_.isArray(v)) - return v[ 0 ].ref; - return v.ref; + return v[ 0 ].ref && !v[ 0 ].nested; + return v.ref && !v.nested; }); if (populateArray === 'all') { @@ -317,7 +393,11 @@ export default function ActiveRecord (model, name) { return model; } + static getSession () { + return session; + } + }; - // sl.registerModel(name, ActiveRecord); + return ActiveRecord; } diff --git a/src/DaoHelper.js b/src/DaoHelper.js index d971d33..a5fab62 100644 --- a/src/DaoHelper.js +++ b/src/DaoHelper.js @@ -24,6 +24,14 @@ export default class DaoHelper{ this.dao.url = url; } + setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, (discriminator) => { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url + } + }) + } + $get(){ return this.dao; } @@ -35,7 +43,8 @@ export default class DaoHelper{ module .provider(name, DaoHelper.getProvider(dao)) .run([name, '$injector', function(service, $injector){ - service.setInjector($injector); + ServiceLocator.instance.registerInjector($injector) + //service.setInjector($injector); }]) } diff --git a/src/Discriminator.js b/src/Discriminator.js new file mode 100644 index 0000000..75837f7 --- /dev/null +++ b/src/Discriminator.js @@ -0,0 +1,15 @@ +//_ = require('lodash'); +import ServiceLocator from './ServiceLocator' +import SessionManager from './SessionManager' + + +export default function Discriminator (Model, type) { + + let Discriminator = class extends Model{ + static get type (){ + return type + } + }; + + return Discriminator; +} diff --git a/src/GenericDao.js b/src/GenericDao.js index 5310dce..1cd7640 100644 --- a/src/GenericDao.js +++ b/src/GenericDao.js @@ -1,23 +1,20 @@ import ServiceLocator from './ServiceLocator'; import QueryBuilder from './QueryBuilder'; -export default function GenericDao(model, qb){ +export default function GenericDao(model, qb, discriminators){ let sl = ServiceLocator.instance; sl.registerModel(model.getName(), model); let myClass = class { constructor(url){ - // this.$injector = $injector; - // this.$http = $injector.get('$http'); this.url = url; this.model = model + this.discriminators = discriminators } - - get $http(){ - return this.$injector.get('$http'); + get $injector () { + return sl.getInjector() } - - setInjector($injector){ - this.$injector = $injector; + get $http(){ + return sl.getInjector().get('$http'); } getModel(){ @@ -33,12 +30,12 @@ export default function GenericDao(model, qb){ return qb ? new qb(this, q) : new QueryBuilder(this, q); } - getHeaders(){ - + getHeaders(opts={}){ + return opts.headers } getOptions(opts={}){ - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } @@ -71,12 +68,28 @@ export default function GenericDao(model, qb){ if (Array.isArray(data)){ return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + if (this.discriminators && data.__t) { + let disc = _.find(this.discriminators, {type: data.__t}); + if (disc) { + return new disc(disc.discriminatorUrl, data); + } + } + return new model(this.url, data); } create(params){ - return new this.model(this.$injector, this.url, params); + if (this.discriminators && params.__t) { + let disc = _.find(this.discriminators,{type: params.__t}); + if (disc) { + return new disc(disc.discriminatorUrl, params); + } + } + return new this.model(this.url, params); } + + // get discriminators(){ + // return discriminators + // } }; @@ -94,13 +107,15 @@ export default function GenericDao(model, qb){ _.forIn(model.getModel(), function(value, key){ var v = Array.isArray(value) ? value[0] : value; if (key === '_id'){ - myClass.prototype['findById'] = myClass.prototype['getById'] = function(value, qb = this.query){ - return this.$http.get(this.url + '/' + value, {params: qb.opts}).then((data)=>{ - return new model(this.$injector, this.url, data.data); + myClass.prototype['findById'] = myClass.prototype['getById'] = function(value, qb = this.query(), opts = {}){ + opts = this.getOptions(opts) + return this.$http.get(this.url + '/' + value, _.merge(opts, {params: qb.opts})).then((data)=>{ + return new model(this.url, data.data); }) } } else{ - myClass.prototype['selectBy' + _.capitalize(key)] = function(toSelect, qb = this.query()){ + myClass.prototype['selectBy' + _.capitalize(key)] = function(toSelect, qb = this.query(), opts={}){ + opts = this.getOptions(opts) if (toSelect && toSelect.length){ if (value.ref){ toSelect = extractId(toSelect); @@ -115,14 +130,7 @@ export default function GenericDao(model, qb){ } qb.setQuery(obj); } - return this.$http.get(this.url, {params: qb.opts}).then((data)=>{ - if (!data.data) { - data.data = [] - } - return { - data: data.data.map(this.build, this), meta: {total: data.headers('X-Total-Count')} - }; - }) + return this.get(qb, opts) } } diff --git a/src/ServiceLocator.js b/src/ServiceLocator.js index 3a35a6b..f6796bc 100644 --- a/src/ServiceLocator.js +++ b/src/ServiceLocator.js @@ -34,6 +34,10 @@ class ServiceLocator { return this.daoRegistry[name]; } + getInjector(){ + return this.injector + } + registerDao(name, dao){ this.daoRegistry[name] = dao; } @@ -42,6 +46,10 @@ class ServiceLocator { this.modelRegistry[name] = model; } + registerInjector(injector){ + this.injector = injector + } + } export default ServiceLocator; \ No newline at end of file diff --git a/src/SessionManager.js b/src/SessionManager.js new file mode 100644 index 0000000..197b27f --- /dev/null +++ b/src/SessionManager.js @@ -0,0 +1,31 @@ +import ServiceLocator from './ServiceLocator' + + +export default function SessionManager(model){ + + let SessionManager = class { + + constructor(){ + this.storage = {} + } + + save(obj){ + if (!obj._id) return + this.storage[obj._id] = obj + } + + retrieve(id){ + return this.storage[id] + } + + clean(){ + this.storage = {} + } + + get model(){ + return model + } + + } + return SessionManager +} \ No newline at end of file diff --git a/tst/app.js b/tst/app.js index d40626a..1249e7e 100644 --- a/tst/app.js +++ b/tst/app.js @@ -3,10 +3,14 @@ import DaoHelper from './DaoHelper'; import ModelManager from './managers/tstManager1'; import ModelManager2 from './managers/tstManager2'; +import ModelManager3 from './managers/tstManager3'; +import ModelManager4 from './managers/tstManager4'; var module = angular .module('tstModule', []); DaoHelper.registerService(module, 'ModelManager', ModelManager); -DaoHelper.registerService(module, 'ModelManager2', ModelManager2); \ No newline at end of file +DaoHelper.registerService(module, 'ModelManager2', ModelManager2); +DaoHelper.registerService(module, 'ModelManager3', ModelManager3); +DaoHelper.registerService(module, 'ModelManager4', ModelManager4); \ No newline at end of file diff --git a/tst/managers/tstManager3.js b/tst/managers/tstManager3.js new file mode 100644 index 0000000..c2a063f --- /dev/null +++ b/tst/managers/tstManager3.js @@ -0,0 +1,10 @@ +'use strict'; + +import Model3 from './../models/tstModel3.js'; +import GenericDao from '../GenericDao'; +import QueryBuilder from '../QueryBuilder'; + +var DAO = GenericDao(Model3); + +export default class ModelManager3 extends DAO { +}; diff --git a/tst/managers/tstManager4.js b/tst/managers/tstManager4.js new file mode 100644 index 0000000..a94e810 --- /dev/null +++ b/tst/managers/tstManager4.js @@ -0,0 +1,14 @@ +'use strict'; + +import Model4 from './../models/tstModel4.js'; +import GenericDao from '../GenericDao'; +import Discriminator from '../Discriminator' + +var D1 = Discriminator(Model4, 'Type1') +var D2 = Discriminator(Model4, 'Type2') + + +var DAO = GenericDao(Model4, undefined, [D1, D2]); + +export default class ModelManager4 extends DAO { +}; diff --git a/tst/models/tstModel1.js b/tst/models/tstModel1.js index 1fbddcb..06bf35e 100644 --- a/tst/models/tstModel1.js +++ b/tst/models/tstModel1.js @@ -12,6 +12,8 @@ var model = { //private: true }, + when: Date, + label: String, num: Number, @@ -26,7 +28,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; diff --git a/tst/models/tstModel3.js b/tst/models/tstModel3.js new file mode 100644 index 0000000..83f45a7 --- /dev/null +++ b/tst/models/tstModel3.js @@ -0,0 +1,25 @@ +'use strict'; + + +import ActiveRecord from '../ActiveRecord' + + +var model = { + + _id: { + type : String, + unique: true, + //private: true + }, + + name: String, +}; + + +var AR = ActiveRecord(model, 'Model3'); + +export default class Model3 extends AR { + + +} + diff --git a/tst/models/tstModel4.js b/tst/models/tstModel4.js new file mode 100644 index 0000000..f0e0432 --- /dev/null +++ b/tst/models/tstModel4.js @@ -0,0 +1,24 @@ +'use strict'; + + +import ActiveRecord from '../ActiveRecord' + + +var model = { + + _id: { + type : String, + unique: true, + //private: true + }, +  + name: String, + + __t: String +}; + + +var AR = ActiveRecord(model, 'Model4'); + +export default class Model4 extends AR {} + diff --git a/tst/specs/angular-dao-base.specs.js b/tst/specs/angular-dao-base.specs.js new file mode 100644 index 0000000..247c307 --- /dev/null +++ b/tst/specs/angular-dao-base.specs.js @@ -0,0 +1,99 @@ +describe('Angular DAO Basics', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('should have a modelManager', function () { + expect(ModelManager).toBeDefined(); + }); + + it('should query for the list', function () { + httpBackend.expectGET('http://MOCKURL.com/model1').respond(); + ModelManager.get(); + }); + + it('should query for one element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); + ModelManager.getById('ididid'); + }); + + it('should deserialize numbers', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); + ModelManager.getById('ididid').then(function (data) { + expect(data.num).toBeDefined() + }) + httpBackend.flush(); + }); + + it('should provide with a query builder', function () { + var qb = ModelManager.query(); + expect(qb).toBeDefined(); + }); + + it('Should have created field specific methods', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); + ModelManager.selectByLabel('toto'); + httpBackend.flush(); + }); + + it('Should have created field specific methods and extract object', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([ { _id: "2" } ]); + httpBackend.flush(); + }); + it('Should have created field specific methods and extract object with key', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([ "2" ]); + httpBackend.flush(); + }); + + it('Should return an ActiveRecord element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ + _id: '1234656' + }); + ModelManager.getById('123').then(function (data) { + expect(data.save).toBeDefined(); + }); + httpBackend.flush(); + }); + + it('Should build an empty array when server return 204', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1')).respond(function(method, url, data) { + return [204, undefined, {'X-Total-Count': 0}] + }); + ModelManager.get().then(function (data) { + expect(data).toEqual({data: []}) + }); + httpBackend.flush(); + }) + + it ('Should not fail when a nested property is undefined', function(){ + var m = ModelManager.create({ + _id: "111", + models2: [{_id: "121212"}, undefined, {_id: null}, null], + model2: undefined + }) + }) + +}); diff --git a/tst/specs/angular-dao-clone.specs.js b/tst/specs/angular-dao-clone.specs.js new file mode 100644 index 0000000..4e4cb82 --- /dev/null +++ b/tst/specs/angular-dao-clone.specs.js @@ -0,0 +1,62 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should have a clone function', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + var clone = model.clone() + expect(clone._id).not.toBeDefined() + }); + + it('Should Clone Deep', function () { + var model = ModelManager.create({ + _id: '123456', + model2: { + _id: '1111', + name: 'toto' + }, + models2: [ + { + _id: '2222', + name: 'tutu' + }, + { + _id: '3333', + name: 'titi' + }, + '444' + ] + }) + var clone = model.cloneDeep() + expect(clone._id).not.toBeDefined() + expect(clone.model2._id).not.toBeDefined() + expect(clone.models2[0]._id).not.toBeDefined() + expect(clone.models2[1]._id).not.toBeDefined() + expect(clone.models2[2]).toEqual('444') + }) +}); diff --git a/tst/specs/angular-dao-discriminators.specs.js b/tst/specs/angular-dao-discriminators.specs.js new file mode 100644 index 0000000..d09eee6 --- /dev/null +++ b/tst/specs/angular-dao-discriminators.specs.js @@ -0,0 +1,59 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManager4Provider) { + ModelManager4Provider.setRootUrl('http://MOCKURL.com/model2'); + ModelManager4Provider.setDiscriminatorUrl('Type1', 'http://fakeurl/type1'); + ModelManager4Provider.setDiscriminatorUrl('Type2', 'http://fakeurl/type2'); + }); + + inject(function (_ModelManager4_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager4 = _ModelManager4_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }) + }); + + it ('should be possible to instantiate discriminators', function(){ + expect(ModelManager4.discriminators.length).toEqual(2) + expect(ModelManager4.discriminators[0].type).toEqual('Type1') + expect(ModelManager4.discriminators[1].type).toEqual('Type2') + expect(ModelManager4.discriminators[0].discriminatorUrl).toEqual('http://fakeurl/type1') + expect(ModelManager4.discriminators[1].discriminatorUrl).toEqual('http://fakeurl/type2') + }) + + it ('should make GET queries on parent url', function(){ + httpBackend.expectGET('http://MOCKURL.com/model2').respond(); + ModelManager4.get(); + httpBackend.flush(); + }) + + it ('should instanciate discriminators according to __t value', function(){ + // TODO : make a GET request, respond with two types, and check their urls + httpBackend.expectGET('http://MOCKURL.com/model2').respond([{_id: 123, name: 'Type1', __t: 'Type1'}, {_id: 321, name: 'Type2', __t: 'Type2'}]); + + ModelManager4.get().then(function (data) { + expect(data.data[0].rootUrl).toEqual('http://fakeurl/type1'); + expect(data.data[1].rootUrl).toEqual('http://fakeurl/type2'); + }); + httpBackend.flush(); + }) + + it ('should make POST queries on correct discriminator URL', function () { + var obj = ModelManager4.createModel({_id: 123456, name: '12345', __t: 'Type1'}) + obj.name = '23456' + httpBackend.expectPUT('http://fakeurl/type1/123456', {name: '23456'}).respond() + obj.save() + }) + + + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + +}); diff --git a/tst/specs/angular-dao-populate.specs.js b/tst/specs/angular-dao-populate.specs.js new file mode 100644 index 0000000..ec9d7b3 --- /dev/null +++ b/tst/specs/angular-dao-populate.specs.js @@ -0,0 +1,187 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + + it('Should make subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + model.populate('models2').then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777') + expect(model.models2[ 1 ]._id).toEqual('4444') + expect(model.models2[ 2 ]._id).toEqual('888') + }); + httpBackend.flush() + }); + + it('Should not make useless subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' } ] + }); + model.populate('models2').then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777') + expect(model.models2[ 1 ]._id).toEqual('888') + }); + }); + + it('Should make subPopulate queries on several fields', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: [ '77777', '4444' ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777'); + expect(model.models2[ 1 ]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + }); + + it('should populate again after save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [ '999' ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888', + models2: [ '999' ] + }) + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [ { _id: '999' } ] + }) + model.save({ force: true, populate: [ 'model2', 'models2' ] }).then(function (pop) { + expect(pop.models2[ 0 ]._id).toEqual('999') + }) + httpBackend.flush() + }) + + it('should not override populated values on save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888' + }) + model.save({ force: true }).then(function () { + expect(model.model2._id).toEqual('888') + }) + httpBackend.flush() + }) + + it('should override if populated value isnt the same', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '777' + }) + model.save({ force: true }).then(function () { + expect(model.model2).toEqual('777') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays on save', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '888' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2[ 0 ]._id).toEqual('888') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays values, but be able to add newly added values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '888', '999' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2) + expect(model.models2[ 0 ]._id).toEqual('888') + expect(model.models2[ 0 ].name).toEqual('tutu') + expect(model.models2[ 1 ]).toEqual('999') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays values, but detect deleted values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '999', '111' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2) + expect(model.models2[ 0 ]).toEqual('999') + expect(model.models2[ 1 ]._id).toEqual('111') + expect(model.models2[ 1 ].name).toEqual('toto') + }) + httpBackend.flush() + }) + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + + }) +}); diff --git a/tst/specs/angular-dao-queries.specs.js b/tst/specs/angular-dao-queries.specs.js new file mode 100644 index 0000000..7e9d530 --- /dev/null +++ b/tst/specs/angular-dao-queries.specs.js @@ -0,0 +1,151 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should query by Id if not key specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select('2')); + httpBackend.flush(); + }); + + it('Should query directly if array size === 1', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2' ])); + httpBackend.flush(); + }); + + it('Should query with $in operator', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ])); + httpBackend.flush(); + }); + + it('Should query on another field if specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two single queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two arrays queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4', '5' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, single then array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select([ '3', '4' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, array then single', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select('4', 'label')); + httpBackend.flush(); + }); + it('Should merge queries on the same field, array then single-array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4' ], 'label')); + httpBackend.flush(); + }); + + it('Should query for archives', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); + ModelManager.get(ModelManager.query().archived(true)); + httpBackend.flush(); + + }); + + it('Should query for deleted', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); + ModelManager.get(ModelManager.query().deleted(true)); + httpBackend.flush(); + + }); + + it('Should paginate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); + ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); + httpBackend.flush(); + }); + it('Should limit', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); + ModelManager.get(ModelManager.query().limit(10)); + httpBackend.flush(); + }); + + it('Should sort', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); + ModelManager.get(ModelManager.query().sort("toto")); + httpBackend.flush(); + }); + + it('Should select specific fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); + ModelManager.get(ModelManager.query().fields([ "toto", "tutu" ])); + httpBackend.flush(); + }); + + it('Should search', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto')); + httpBackend.flush(); + }); + + it('Should search on another field', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', 'label')); + httpBackend.flush(); + }); + + it('Should search on multiple fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', [ 'name', 'label' ])); + httpBackend.flush(); + }); + + it('Should count', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); + ModelManager.count() + httpBackend.flush() + }) + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + +}); diff --git a/tst/specs/angular-dao-save.specs.js b/tst/specs/angular-dao-save.specs.js new file mode 100644 index 0000000..92002d4 --- /dev/null +++ b/tst/specs/angular-dao-save.specs.js @@ -0,0 +1,261 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it ('should prepare to save refs and nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] + }); + model.model3 = ModelManager3.create({_id: '00002'}) + model.models3 = [ModelManager3.create({_id: '0001'})] + expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) + }) + + it ('should diff nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ], + model3: {_id: '00002'}, + models3:[{_id: '0001'}] + }); + expect(model.beforeSave()).toEqual({}) + }) + + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: [ '7777' ], + label: 'toto' + }) + model.models2.push('8888') + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: [ '7777', '8888' ] }).respond() + model.save() + httpBackend.flush() + }) + + it('Should save new objects', function () { + var model = ModelManager.createModel({ + model2: '77777' + }); + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + expect(model.$$pristine).toBeTruthy() + model.save(); + }); + + it('Should detect change when change is falsy', function () { + var model = ModelManager.createModel({ + _id: '123456', + label: 'AZE', + num: 10 + }) + model.label = '' + model.num = 0 + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: '', num: 0}).respond() + model.save(); + httpBackend.flush() + }) + + it('Should deep nested objects', function () { + var model = ModelManager.createModel({ + label: 'toto', + model2: { name: 'toto' } + }); + httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); + httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); + model.saveDeep(); + httpBackend.flush(); + expect(model.model2._id).toEqual('1111') + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + + }) + + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }) + model.save() + }) + + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }) + model.when = new Date(model.when) + model.save() + }) + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }) + model.when = new Date(203939) + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save() + }) + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: [ '77777', '4444' ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777'); + expect(model.models2[ 1 ]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.save() + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.model2 = { _id: '999' } + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save() + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush() + model.models2.push({ _id: '999' }) + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save() + }); + + it ('Should not save irrelevant fields', function(){ + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am' + model.save() + }) + + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + model.iShouldntBeThere = 'but I am' + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + + it('should allow to save a nested field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model3: { + _id: '23456', + name: 'toto' + } + }) + + model.model3 = null + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model3: null}).respond() + model.save() + httpBackend.flush() + }) + + it('should allow to save a populated field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: { + _id: '234567', + name: 'toto' + } + }) + + model.model2 = null + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model2: null }).respond() + model.save() + httpBackend.flush() + }) + +}); diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js deleted file mode 100644 index 929ee0c..0000000 --- a/tst/specs/angular-dao.specs.js +++ /dev/null @@ -1,423 +0,0 @@ -describe('Angular DAO', function () { - - var ModelManager, httpBackend, $rootScope, $timeout; - - beforeEach(function () { - - module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { - ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); - ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); - }); - - inject(function (_ModelManager_, $httpBackend, _$rootScope_, _$timeout_) { - ModelManager = _ModelManager_; - httpBackend = $httpBackend; - $rootScope = _$rootScope_; - $timeout = _$timeout_; - - }) - - }); - - afterEach(function () { - httpBackend.verifyNoOutstandingExpectation() - }); - - it('should have a modelManager', function () { - expect(ModelManager).toBeDefined(); - }); - - it('should query for the list', function () { - httpBackend.expectGET('http://MOCKURL.com/model1').respond(); - ModelManager.get(); - }); - - it('should query for one element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); - ModelManager.getById('ididid'); - }); - - it('should deserialize numbers', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); - ModelManager.getById('ididid').then(function (data) { - expect(data.num).toBeDefined() - }) - httpBackend.flush(); - }); - - it('should provide with a query builder', function () { - var qb = ModelManager.query(); - expect(qb).toBeDefined(); - }); - - it('Should have created field specific methods', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); - ModelManager.selectByLabel('toto'); - httpBackend.flush(); - }); - - it('Should have created field specific methods and extract object', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([ { _id: "2" } ]); - httpBackend.flush(); - }); - it('Should have created field specific methods and extract object with key', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([ "2" ]); - httpBackend.flush(); - }); - - it('Should return an ActiveRecord element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ - _id: '1234656' - }); - ModelManager.getById('123').then(function (data) { - expect(data.save).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('Should query by Id if not key specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select('2')); - httpBackend.flush(); - }); - - it('Should query directly if array size === 1', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2' ])); - httpBackend.flush(); - }); - it('Should query with $in operator', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ])); - httpBackend.flush(); - }); - - it('Should query on another field if specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two single queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two arrays queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4', '5' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, single then array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select([ '3', '4' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, array then single', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select('4', 'label')); - httpBackend.flush(); - }); - it('Should merge queries on the same field, array then single-array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4' ], 'label')); - httpBackend.flush(); - }); - - it('Should query for archives', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); - ModelManager.get(ModelManager.query().archived(true)); - httpBackend.flush(); - - }); - - it('Should query for deleted', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); - ModelManager.get(ModelManager.query().deleted(true)); - httpBackend.flush(); - - }); - - it('Should paginate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); - ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); - httpBackend.flush(); - }); - it('Should limit', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); - ModelManager.get(ModelManager.query().limit(10)); - httpBackend.flush(); - }); - - it('Should sort', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); - ModelManager.get(ModelManager.query().sort("toto")); - httpBackend.flush(); - }); - - it('Should select specific fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); - ModelManager.get(ModelManager.query().fields([ "toto", "tutu" ])); - httpBackend.flush(); - }); - - it('Should search', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto')); - httpBackend.flush(); - }); - - it('Should search on another field', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', 'label')); - httpBackend.flush(); - }); - - it('Should search on multiple fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', [ 'name', 'label' ])); - httpBackend.flush(); - }); - - it('Should count', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); - ModelManager.count() - httpBackend.flush() - }) - - it('Should make subPopulate queries', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); - model.populate('model2'); - httpBackend.flush(); - }); - - it('Should have a clone function', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - var clone = model.clone() - expect(clone._id).not.toBeDefined() - }); - - it('Should Clone Deep', function(){ - var model = ModelManager.create({ - _id: '123456', - model2: { - _id: '1111', - name: 'toto' - }, - models2: [ - { - _id: '2222', - name: 'tutu' - }, - { - _id: '3333', - name: 'titi' - }, - '444' - ] - }) - var clone = model.cloneDeep() - expect(clone._id).not.toBeDefined() - expect(clone.model2._id).not.toBeDefined() - expect(clone.models2[ 0 ]._id).not.toBeDefined() - expect(clone.models2[ 1 ]._id).not.toBeDefined() - expect(clone.models2[ 2 ]).toEqual('444') - }) - - it('Should make subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); - model.populate('models2').then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777') - expect(model.models2[ 1 ]._id).toEqual('4444') - expect(model.models2[ 2 ]._id).toEqual('888') - }); - httpBackend.flush() - }); - - it('Should not make useless subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' } ] - }); - model.populate('models2').then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777') - expect(model.models2[ 1 ]._id).toEqual('888') - }); - }); - - it('Should make subPopulate queries on several fields', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: [ '77777', '4444' ] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate([ 'models2', 'model2' ]).then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777'); - expect(model.models2[ 1 ]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush() - }); - - it('should populate again after save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [ '999' ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888', - models2: [ '999' ] - }) - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [ { _id: '999' } ] - }) - model.save([ 'model2', 'models2' ]).then(function (pop) { - expect(pop.models2[ 0 ]._id).toEqual('999') - }) - httpBackend.flush() - }) - - it('should not override populated values on save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888' - }) - model.save().then(function () { - expect(model.model2._id).toEqual('888') - }) - httpBackend.flush() - }) - - it('should override if populated value isnt the same', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '777' - }) - model.save().then(function () { - expect(model.model2).toEqual('777') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays on save', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '888' ] - }); - model.save().then(function () { - expect(model.models2[ 0 ]._id).toEqual('888') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays values, but be able to add newly added values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '888', '999' ] - }); - model.save().then(function () { - expect(model.models2.length).toEqual(2) - expect(model.models2[ 0 ]._id).toEqual('888') - expect(model.models2[ 0 ].name).toEqual('tutu') - expect(model.models2[ 1 ]).toEqual('999') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays values, but detect deleted values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '999', '111' ] - }); - model.save().then(function () { - expect(model.models2.length).toEqual(2) - expect(model.models2[ 0 ]).toEqual('999') - expect(model.models2[ 1 ]._id).toEqual('111') - expect(model.models2[ 1 ].name).toEqual('toto') - }) - httpBackend.flush() - }) - - it('Should save objects', function () { - var model = ModelManager.createModel({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656').respond(); - model.save(); - httpBackend.flush(); - }); - - it('Should deep nested objects', function () { - var model = ModelManager.createModel({ - label: 'toto', - model2: { name: 'toto'} - }); - httpBackend.expectPOST('http://MOCKURL.com/model2').respond({name: 'toto', _id: '1111'}); - httpBackend.expectPOST('http://MOCKURL.com/model1').respond({label: 'toto'}); - model.saveDeep(); - httpBackend.flush(); - expect(model.model2._id).toEqual('1111') - }); - - it('Should make a query with populate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); - ModelManager.get(ModelManager.query().populate('model2')); - httpBackend.flush(); - - }); - - it('Should build an empty array when server return 204', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1')).respond(function(method, url, data) { - return [204, undefined, {'X-Total-Count': 0}] - }); - ModelManager.get().then(function (data) { - expect(data).toEqual({data: []}) - }); - httpBackend.flush(); - - }) - -});