UNPKG

mongoose-to-object-project

Version:

Adds projection to toObject with predefined levels and level selector capabilitiy

681 lines (607 loc) 14.8 kB
'use strict'; const expect = require('expect.js'); const mongoose = require('mongoose'); const schema = mongoose.Schema({ username: String, name: String, password: String, email: String, deep: { _id: false, a: String, b: String, c: [{ _id: false, a: String, b: String }] }, notes: { title: String, content: String }, friend: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, systemField: { type: String, level: '-public -private -newLevel' // visible on levels other than 'public' and 'private' }, groups: { type: String, level: 'internal' // only visible on 'internal' level } }); let otherActive = true; schema.set('toObject', { transform: (doc, ret) => { if (otherActive) ret.otherTransform = true; } }); let options = { levels: { public: 'username -password -_id -deep otherTransform -friend', private: '-password -_id', system: '', internal: '', deepExclusion: '-deep.field -deep.arr.val -deep.deep.undefined', deepInclusion: 'deep.field deep.arr.val deep.deep.undefined' }, // optional default level as string or a synchronous level selector function returning level name as a string (recommended), function is passed all transform arguments level: 'public' }; schema.plugin(require('.'), options); const Model = mongoose.model('User', schema); describe('set method', function() { let user = new Model({ username: 'Jhon Doe', deep: { a: 'a string', b: 'b string', c: [{ a: 'hello', b: 'world' }] }, }); it('should not set', () => { user.set('usernames', 'world', { level: 'system' }); expect(user.get('usernames')).to.not.be('world'); }); it('should not set deep', () => { user.set('usernames.name', 'world', { level: 'system' }); expect(user.get('usernames.name')).to.not.be('world'); }); it('username should equal world', () => { user.set('username', 'world', { level: 'system' }); expect(user.get('username')).to.be('world'); }); it('deep.c.0.a should equal world', () => { user.set('deep.c.0.a', 'world', { level: 'system' }); expect(user.get('deep.c.0.a')).to.be('world'); }); it('deep.c.0.a should not equal word', () => { user.set('deep.c.0.a', 'word', { level: 'public' }); expect(user.get('deep.c.0.a')).to.not.be('word'); }); it('deep.c.b.a should not equal word', () => { user.set('deep.c.b.a', 'word', { level: 'system' }); expect(user.get('deep.c.b.a')).to.not.be('word'); }); it('deep.c.0.a should equal bird', () => { user.set('deep.c.0', { a: 'bird' }, { level: 'system' }); expect(user.get('deep.c.0.a')).to.eql('bird'); }); it('deep.c.0.a should equal word', () => { user.set({ deep: { c: [{ a: 'word' }] } }, undefined, { level: 'system' }); expect(user.get('deep.c.0.a')).to.be('word'); }); }); describe('levelProjectObject', function() { it('should return public', function() { debugger; expect(Model.levelProjectObject({ username: 'my username', password: 'my password', deep: { field: 'lel' } })).to.eql({ username: 'my username' }); }); it('should return public minimized', function() { expect(Model.levelProjectObject({ username: 'my username', password: 'my password' }, { level: 'public', minimize: true })).to.eql({ username: 'my username' }); }); it('should return deepExclusion', function() { expect(Model.levelProjectObject({ username: 'my username', deep: { field: 'lel', arr: [{ val: 'val', hej: 'hej' }], deep: undefined } }, { level: 'deepExclusion' })).to.eql({ username: 'my username', deep: { arr: [{ hej: 'hej' }], deep: undefined } }); }); it('should return deepInclusion', function() { expect(Model.levelProjectObject({ username: 'my username', deep: { field: 'lel', arr: [{ val: 'val', hej: 'hej' }], deep: undefined } }, { level: 'deepInclusion', projection: 'deep.arr.b' })).to.eql({ deep: { field: 'lel', arr: [{ val: 'val' }] } }); }); }); describe('getPathAsLevel', function() { it('username as public should equal username', function() { expect(Model.getPathAsLevel('public', 'username')).to.eql(schema.path('username')); }); it('usernamez as public should equal undefined', function() { expect(Model.getPathAsLevel('public', 'usernamez')).to.eql(undefined); }); it('password as public should equal undefined', function() { expect(Model.getPathAsLevel('public', 'password')).to.eql(undefined); }); it('password as system should equal password', function() { expect(Model.getPathAsLevel('system', 'password')).to.eql(schema.path('password')); }); }); describe('ToObject Project Single Level', function() { let friend = new Model({ username: 'Doe', name: 'John Doe', password: '654321', email: 'doe@email.com' }); let user = new Model({ username: 'John', name: 'John Jhonsson', password: '123456', email: 'private@email.com', friend: friend, systemField: 'sys', groups: 'admin user', notes: { title: undefined }, data: [] }); it('should respect existing toObject transform', () => { let data = user.toObject(); otherActive = false; expect(data.otherTransform).to.be.ok(); }); it('level selector function should select level', function() { let data = user.toObject({ user: user, // Set and override level selector. // A simple level selector based on user _id equality. // Overrides all (sub)schema defined level functions or defaults - generally not a good idea but should work anyhow level: (doc, ret, options) => doc._id.equals(options.user._id) ? 'private' : 'public' }); expect(data).to.eql({ username: 'John', name: 'John Jhonsson', email: 'private@email.com', friend: { username: 'Doe' }, deep: { c: [] } }); }); it('should be public by default', function() { let data = user.toObject(); expect(data).to.eql({ username: 'John' }); }); it('should not minimize', function() { let data = user.toObject({ minimize: false, level: 'internal' }); expect(data).to.have.key('notes'); }); it('should minimize', function() { let data = user.toObject({ minimize: true, level: 'internal' }); expect(data).to.not.have.key('notes'); }); it('should be private', function() { let data = user.toObject({ level: 'private', minimize: true }); expect(data).to.eql({ username: 'John', name: 'John Jhonsson', email: 'private@email.com', friend: { username: 'Doe', name: 'John Doe', email: 'doe@email.com', deep: { c: [] } }, deep: { c: [] } }); }); it('should be system', function() { let data = user.toObject({ level: 'system', minimize: true }); expect(data).to.eql({ _id: user._id, username: 'John', name: 'John Jhonsson', password: '123456', email: 'private@email.com', friend: { _id: user.friend._id, username: 'Doe', name: 'John Doe', password: '654321', email: 'doe@email.com', deep: { c: [] } }, deep: { c: [] }, systemField: 'sys' }); }); it('should be be modified private', function() { let data = user.toObject({ level: 'private', projection: '-username -friend -deep', minimize: true }); expect(data).to.eql({ name: 'John Jhonsson', email: 'private@email.com' }); }); it('public should not include password even if asked to', function() { let data = user.toObject({ level: 'public', projection: 'password', minimize: true }); expect(data).to.eql({ username: 'John' }); }); it('no level should equal public', function() { let data = user.toObject({ minimize: true }); expect(data).to.eql({ username: 'John' }); }); it('only username should exist', function() { let data = user.toObject({ projection: 'username' }); expect(data).to.eql({ username: 'John' }); }); it('systemField should not exist', function() { let data = user.toObject(); expect(data).to.not.have.key('systemField'); }); it('systemField should exist', function() { let data = user.toObject({ level: 'system', minimize: true }); expect(data).to.have.key('systemField'); }); it('groups should not exist', function() { let data = user.toObject(); expect(data).to.not.have.key('groups'); }); it('groups should exist', function() { let data = user.toObject({ level: 'internal', minimize: true }); expect(data).to.have.key('groups'); }); describe('toObjectOptionsExtend', function() { let data = Model.toObjectOptionsExtend({ name: 'Doe' }); it('name should equal to Doe', function() { expect(data.name).to.eql('Doe'); }); it('level should equal public', function() { expect(data.level).to.eql('public'); }); }); }); describe('ToObject Project MultiLevel', function() { let user = new Model({ username: 'John', name: 'John Jhonsson', password: '123456', email: 'private@email.com', deep: { a: 'a var', b: 'b var', c: [{ a: 'c.a var 0', b: 'c.b var 0' }, { a: 'c.a var 1', b: 'c.b var 1' }] } }); it('should be public by default', function() { let data = user.toObject(); expect(data).to.eql({ username: 'John' }); }); it('should be private', function() { let data = user.toObject({ level: 'private', minimize: true }); expect(data).to.eql({ username: 'John', name: 'John Jhonsson', email: 'private@email.com', deep: { a: 'a var', b: 'b var', c: [{ a: 'c.a var 0', b: 'c.b var 0' }, { a: 'c.a var 1', b: 'c.b var 1' }] } }); }); it('deep should not exist', function() { let data = user.toObject({ level: 'private', projection: '-deep', minimize: true }); expect(data).to.eql({ username: 'John', name: 'John Jhonsson', email: 'private@email.com' }); }); it('deep.c.a should be undefined', function() { let data = user.toObject({ level: 'private', projection: '-deep.c.a', minimize: true }); expect(data).to.eql({ username: 'John', name: 'John Jhonsson', email: 'private@email.com', deep: { a: 'a var', b: 'b var', c: [{ b: 'c.b var 0' }, { b: 'c.b var 1' }] } }); }); it('should get level schema', function() { let schema0 = Model.getLevelSchemaTree('public'); expect(schema0).to.be.ok(); expect(schema0).to.eql({ username: String }); }); }); describe('Child inclusion overrides parent exclusion', function() { let schema0 = mongoose.Schema({ parent: { child0: String, child1: { child0: String } } }); let options0 = { levels: { public: '-parent parent.child1' }, level: 'public' }; let error; schema0.plugin(require('.'), options0); let Model0 = mongoose.model(Date.now().toString(), schema0); it('should include parent.child1 but not parent.child0', function() { debugger; let data = new Model0({ parent: { child0: 'data0', child1: { child0: 'data1' } } }).toObject(); expect(data).to.have.property('parent'); expect(data.parent).to.not.have.property('child0'); expect(data.parent).to.have.property('child1'); }); }); describe('Errors', function() { describe('schema tree level defintion', function() { let schema0 = mongoose.Schema({ field: { type: String, level: 'public' } }); let schema1 = mongoose.Schema({ field: { type: String, level: 'public -private' } }); let options0 = {}; let options1 = { levels: { public: '', private: '' } }; let error0; let error1; try { schema0.plugin(require('.'), options0); } catch (err) { error0 = err; } try { schema1.plugin(require('.'), options1); } catch (err) { error1 = err; } let pathName = 'field'; let level = 'public'; it('no predefined level should trow error', function() { expect(error0).to.be.a(Error); expect(error0.message).to.be(`"${pathName}" contains undefined level "${level}". Level inclusions must be predefined in plugin options.`); }); it('mixing inclusion and exclusion should trow error', function() { expect(error1).to.be.a(Error); expect(error1.message).to.be(`"${pathName}" contains inclusions and exclusions, only one type can be used`); }); }); describe('options', function() { let schema0 = mongoose.Schema({ field: { type: String, } }); let options0 = { levels: { public: {} } }; let error; try { schema.plugin(require('.'), options0); } catch (err) { error = err; } it('non-string level should throw error', function() { expect(error).to.be.a(Error); }); }); describe('transform', function() { let schema0 = mongoose.Schema({ field: { type: String, } }); let options0 = { levels: { public: 'field' }, level: () => { } }; let error; schema0.plugin(require('.'), options0); let Model0 = mongoose.model(Date.now().toString(), schema0); try { new Model0({}).toObject(); } catch (err) { error = err; } it('no level should trow error', function() { expect(error).to.be.a(Error); expect(error.message).to.be('unable to determine level'); }); }); });