mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-15 03:01:37 -05:00
Created Session Model (#9909)
refs #9865 - Created session model for session table - Added model relations - Added unit test coverage
This commit is contained in:
parent
1d17f2aa91
commit
4d9414b5d2
4 changed files with 323 additions and 0 deletions
core
|
@ -26,6 +26,7 @@ models = [
|
|||
'refreshtoken',
|
||||
'role',
|
||||
'settings',
|
||||
'session',
|
||||
'subscriber',
|
||||
'tag',
|
||||
'user',
|
||||
|
|
81
core/server/models/session.js
Normal file
81
core/server/models/session.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
const ghostBookshelf = require('./base');
|
||||
|
||||
const Session = ghostBookshelf.Model.extend({
|
||||
tableName: 'sessions',
|
||||
|
||||
parse(attrs) {
|
||||
attrs.session_data = JSON.parse(attrs.session_data);
|
||||
return attrs;
|
||||
},
|
||||
|
||||
format(attrs) {
|
||||
// CASE: format will be called when formatting all data for the DB
|
||||
// including for SELECTs meaning that if we call findOne without
|
||||
// a session_data property we'll get unintended JSON.stringify(undefined) calls
|
||||
if (attrs.session_data) {
|
||||
attrs.session_data = JSON.stringify(attrs.session_data);
|
||||
}
|
||||
return attrs;
|
||||
},
|
||||
|
||||
user() {
|
||||
return this.belongsTo('User');
|
||||
}
|
||||
}, {
|
||||
|
||||
permittedOptions(methodName) {
|
||||
const permittedOptions = ghostBookshelf.Model.permittedOptions.call(this, methodName);
|
||||
if (methodName === 'upsert') {
|
||||
return permittedOptions.concat('session_id');
|
||||
}
|
||||
if (methodName === 'destroy') {
|
||||
return permittedOptions.concat('session_id');
|
||||
}
|
||||
return permittedOptions;
|
||||
},
|
||||
|
||||
destroy(unfilteredOptions) {
|
||||
if (unfilteredOptions.id) {
|
||||
return ghostBookshelf.Model.destroy.call(this, unfilteredOptions);
|
||||
}
|
||||
const options = this.filterOptions(unfilteredOptions, 'destroy');
|
||||
|
||||
// Fetch the object before destroying it, so that the changed data is available to events
|
||||
return this.forge({session_id: options.session_id})
|
||||
.fetch(options)
|
||||
.then((obj) => {
|
||||
return obj.destroy(options);
|
||||
});
|
||||
},
|
||||
|
||||
upsert(data, unfilteredOptions) {
|
||||
const options = this.filterOptions(unfilteredOptions, 'upsert');
|
||||
const sessionId = options.session_id;
|
||||
const sessionData = data.session_data;
|
||||
const userId = sessionData.user_id;
|
||||
return this.findOne({session_id: sessionId, user_id: userId}, options)
|
||||
.then((model) => {
|
||||
if (model) {
|
||||
return this.edit({
|
||||
session_data: sessionData
|
||||
}, Object.assign(options, {
|
||||
id: model.id
|
||||
}));
|
||||
}
|
||||
return this.add({
|
||||
session_id: sessionId,
|
||||
session_data: sessionData,
|
||||
user_id: userId
|
||||
}, options);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const Sessions = ghostBookshelf.Collection.extend({
|
||||
model: Session
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Session: ghostBookshelf.model('Session', Session),
|
||||
Sessions: ghostBookshelf.collection('Sessions', Sessions)
|
||||
};
|
|
@ -249,6 +249,10 @@ User = ghostBookshelf.Model.extend({
|
|||
return this.hasMany('Posts', 'created_by');
|
||||
},
|
||||
|
||||
sessions: function sessions() {
|
||||
return this.hasMany('Sessions');
|
||||
},
|
||||
|
||||
roles: function roles() {
|
||||
return this.belongsToMany('Role');
|
||||
},
|
||||
|
|
237
core/test/unit/models/session_spec.js
Normal file
237
core/test/unit/models/session_spec.js
Normal file
|
@ -0,0 +1,237 @@
|
|||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
const models = require('../../../server/models');
|
||||
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Unit: models/session', function () {
|
||||
let sandbox;
|
||||
|
||||
before(function () {
|
||||
models.init();
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('parse', function () {
|
||||
const parse = function parse(attrs) {
|
||||
return new models.Session().parse(attrs);
|
||||
};
|
||||
|
||||
it('converts session_data to an object', function () {
|
||||
const attrs = {
|
||||
id: 'something',
|
||||
session_data: JSON.stringify({
|
||||
some: 'data'
|
||||
})
|
||||
};
|
||||
const parsed = parse(attrs);
|
||||
should.equal(typeof parsed.session_data, 'object');
|
||||
should.equal(parsed.session_data.some, 'data');
|
||||
});
|
||||
});
|
||||
|
||||
describe('format', function () {
|
||||
const format = function format(attrs) {
|
||||
return new models.Session().format(attrs);
|
||||
};
|
||||
|
||||
it('converts session_data to a string', function () {
|
||||
const attrs = {
|
||||
id: 'something',
|
||||
session_data: {
|
||||
some: 'data'
|
||||
}
|
||||
};
|
||||
const formatted = format(attrs);
|
||||
should.equal(typeof formatted.session_data, 'string');
|
||||
should.equal(formatted.session_data, JSON.stringify({
|
||||
some: 'data'
|
||||
}));
|
||||
});
|
||||
|
||||
it('does not add session_data key if missing', function () {
|
||||
const attrs = {
|
||||
id: 'something'
|
||||
};
|
||||
const formatted = format(attrs);
|
||||
should.equal(formatted.session_data, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('user', function () {
|
||||
it('sets up the relation to the "User" model', function () {
|
||||
const model = models.Session.forge({});
|
||||
const belongsToSpy = sandbox.spy(model, 'belongsTo');
|
||||
model.user();
|
||||
|
||||
should.equal(belongsToSpy.args[0][0], 'User');
|
||||
});
|
||||
});
|
||||
|
||||
describe('permittedOptions', function () {
|
||||
let basePermittedOptionsReturnVal;
|
||||
let basePermittedOptionsStub;
|
||||
|
||||
beforeEach(function () {
|
||||
basePermittedOptionsReturnVal = ['super', 'doopa'];
|
||||
basePermittedOptionsStub = sandbox.stub(models.Base.Model, 'permittedOptions')
|
||||
.returns(basePermittedOptionsReturnVal);
|
||||
});
|
||||
|
||||
it('passes the methodName and the context to the base permittedOptions method', function () {
|
||||
const methodName = 'methodName';
|
||||
models.Session.permittedOptions(methodName);
|
||||
|
||||
should.equal(basePermittedOptionsStub.args[0][0], methodName);
|
||||
should.equal(basePermittedOptionsStub.thisValues[0], models.Session);
|
||||
});
|
||||
|
||||
it('returns the base permittedOptions result', function () {
|
||||
const returnedOptions = models.Session.permittedOptions();
|
||||
|
||||
should.deepEqual(returnedOptions, basePermittedOptionsReturnVal);
|
||||
});
|
||||
|
||||
it('returns the base permittedOptions result plus "session_id" when methodName is upsert', function () {
|
||||
const returnedOptions = models.Session.permittedOptions('upsert');
|
||||
|
||||
should.deepEqual(returnedOptions, basePermittedOptionsReturnVal.concat('session_id'));
|
||||
});
|
||||
|
||||
it('returns the base permittedOptions result plus "session_id" when methodName is destroy', function () {
|
||||
const returnedOptions = models.Session.permittedOptions('destroy');
|
||||
|
||||
should.deepEqual(returnedOptions, basePermittedOptionsReturnVal.concat('session_id'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroy', function () {
|
||||
it('calls and returns the Base Model destroy if an id is passed', function () {
|
||||
const baseDestroyReturnVal = {};
|
||||
const baseDestroyStub = sandbox.stub(models.Base.Model, 'destroy')
|
||||
.returns(baseDestroyReturnVal);
|
||||
|
||||
const options = {id: 1};
|
||||
const returnVal = models.Session.destroy(options);
|
||||
|
||||
should.equal(baseDestroyStub.args[0][0], options);
|
||||
should.equal(returnVal, baseDestroyReturnVal);
|
||||
});
|
||||
|
||||
it('calls forge with the session_id, fetchs with the filtered options and then destroys with the options', function (done) {
|
||||
const model = models.Session.forge({});
|
||||
const session_id = 23;
|
||||
const unfilteredOptions = {session_id};
|
||||
const filteredOptions = {session_id};
|
||||
|
||||
const filterOptionsStub = sandbox.stub(models.Session, 'filterOptions')
|
||||
.returns(filteredOptions);
|
||||
const forgeStub = sandbox.stub(models.Session, 'forge')
|
||||
.returns(model);
|
||||
const fetchStub = sandbox.stub(model, 'fetch')
|
||||
.resolves(model);
|
||||
const destroyStub = sandbox.stub(model, 'destroy')
|
||||
.resolves();
|
||||
|
||||
models.Session.destroy(unfilteredOptions).then(() => {
|
||||
should.equal(filterOptionsStub.args[0][0], unfilteredOptions);
|
||||
should.equal(filterOptionsStub.args[0][1], 'destroy');
|
||||
|
||||
should.deepEqual(forgeStub.args[0][0], {session_id});
|
||||
|
||||
should.equal(fetchStub.args[0][0], filteredOptions);
|
||||
should.equal(destroyStub.args[0][0], filteredOptions);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('upsert', function () {
|
||||
it('calls findOne and then add if findOne results in nothing', function (done) {
|
||||
const session_id = 314;
|
||||
const unfilteredOptions = {session_id};
|
||||
const filteredOptions = {session_id};
|
||||
const data = {
|
||||
session_data: {
|
||||
user_id: 96
|
||||
}
|
||||
};
|
||||
|
||||
const filterOptionsStub = sandbox.stub(models.Session, 'filterOptions')
|
||||
.returns(filteredOptions);
|
||||
|
||||
const findOneStub = sandbox.stub(models.Session, 'findOne')
|
||||
.resolves();
|
||||
|
||||
const addStub = sandbox.stub(models.Session, 'add');
|
||||
|
||||
models.Session.upsert(data, unfilteredOptions).then(() => {
|
||||
should.equal(filterOptionsStub.args[0][0], unfilteredOptions);
|
||||
should.equal(filterOptionsStub.args[0][1], 'upsert');
|
||||
|
||||
should.deepEqual(findOneStub.args[0][0], {
|
||||
session_id,
|
||||
user_id: data.session_data.user_id
|
||||
});
|
||||
should.equal(findOneStub.args[0][1], filteredOptions);
|
||||
|
||||
should.deepEqual(addStub.args[0][0], {
|
||||
session_id: filteredOptions.session_id,
|
||||
session_data: data.session_data,
|
||||
user_id: data.session_data.user_id
|
||||
});
|
||||
|
||||
should.equal(addStub.args[0][1], filteredOptions);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls findOne and then edit if findOne results in nothing', function (done) {
|
||||
const model = models.Session.forge({id: 2});
|
||||
const session_id = 314;
|
||||
const unfilteredOptions = {session_id};
|
||||
const filteredOptions = {session_id};
|
||||
const data = {
|
||||
session_data: {
|
||||
user_id: 96
|
||||
}
|
||||
};
|
||||
|
||||
const filterOptionsStub = sandbox.stub(models.Session, 'filterOptions')
|
||||
.returns(filteredOptions);
|
||||
|
||||
const findOneStub = sandbox.stub(models.Session, 'findOne')
|
||||
.resolves(model);
|
||||
|
||||
const editStub = sandbox.stub(models.Session, 'edit');
|
||||
|
||||
models.Session.upsert(data, unfilteredOptions).then(() => {
|
||||
should.equal(filterOptionsStub.args[0][0], unfilteredOptions);
|
||||
should.equal(filterOptionsStub.args[0][1], 'upsert');
|
||||
|
||||
should.deepEqual(findOneStub.args[0][0], {
|
||||
session_id,
|
||||
user_id: data.session_data.user_id
|
||||
});
|
||||
should.equal(findOneStub.args[0][1], filteredOptions);
|
||||
|
||||
should.deepEqual(editStub.args[0][0], {
|
||||
session_data: data.session_data,
|
||||
});
|
||||
|
||||
should.deepEqual(editStub.args[0][1], {
|
||||
session_id,
|
||||
id: model.id
|
||||
});
|
||||
|
||||
should.equal(editStub.args[0][1], filteredOptions);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue