mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Fixed model events and transactions (#9524)
no issue - if multiple queries run in a transaction, the model events are triggered before the txn finished - if the txn rolls back, the events are anyway emitted - the events are triggered too early - solution: - `emitChange` needs to detect that a transaction is happening - it listens on a txn event to determine if events should be triggered
This commit is contained in:
parent
25cd7c7756
commit
fb79f24316
12 changed files with 201 additions and 85 deletions
|
@ -1,20 +1,21 @@
|
||||||
var ghostBookshelf = require('./base'),
|
'use strict';
|
||||||
Basetoken = require('./base/token'),
|
|
||||||
common = require('../lib/common'),
|
|
||||||
|
|
||||||
Accesstoken,
|
const ghostBookshelf = require('./base'),
|
||||||
|
Basetoken = require('./base/token');
|
||||||
|
|
||||||
|
let Accesstoken,
|
||||||
Accesstokens;
|
Accesstokens;
|
||||||
|
|
||||||
Accesstoken = Basetoken.extend({
|
Accesstoken = Basetoken.extend({
|
||||||
tableName: 'accesstokens',
|
tableName: 'accesstokens',
|
||||||
|
|
||||||
emitChange: function emitChange(event) {
|
emitChange: function emitChange(event, options) {
|
||||||
// Event named 'token' as access and refresh token will be merged in future, see #6626
|
const eventToTrigger = 'token' + '.' + event;
|
||||||
common.events.emit('token' + '.' + event, this);
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreated: function onCreated(model) {
|
onCreated: function onCreated(model, attrs, options) {
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ ghostBookshelf = bookshelf(db.knex);
|
||||||
// Load the Bookshelf registry plugin, which helps us avoid circular dependencies
|
// Load the Bookshelf registry plugin, which helps us avoid circular dependencies
|
||||||
ghostBookshelf.plugin('registry');
|
ghostBookshelf.plugin('registry');
|
||||||
|
|
||||||
|
// Add committed/rollback events.
|
||||||
|
ghostBookshelf.plugin(plugins.transactionEvents);
|
||||||
|
|
||||||
// Load the Ghost filter plugin, which handles applying a 'filter' to findPage requests
|
// Load the Ghost filter plugin, which handles applying a 'filter' to findPage requests
|
||||||
ghostBookshelf.plugin(plugins.filter);
|
ghostBookshelf.plugin(plugins.filter);
|
||||||
|
|
||||||
|
@ -98,6 +101,32 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
emitChange: function (model, event, options) {
|
||||||
|
if (!options.transacting) {
|
||||||
|
return common.events.emit(event, model, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model.ghostEvents) {
|
||||||
|
model.ghostEvents = [];
|
||||||
|
|
||||||
|
if (options.importing) {
|
||||||
|
options.transacting.setMaxListeners(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.transacting.once('committed', (committed) => {
|
||||||
|
if (!committed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(this.ghostEvents, (ghostEvent) => {
|
||||||
|
common.events.emit(ghostEvent, model, _.omit(options, 'transacting'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
model.ghostEvents.push(event);
|
||||||
|
},
|
||||||
|
|
||||||
// Bookshelf `initialize` - declare a constructor-like method for model creation
|
// Bookshelf `initialize` - declare a constructor-like method for model creation
|
||||||
initialize: function initialize() {
|
initialize: function initialize() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
|
@ -31,9 +31,9 @@ common.events.on('user.deactivated', function (userModel, options) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
models.Accesstoken.destroyByUser(_.omit(options, 'transacting'))
|
models.Accesstoken.destroyByUser(options)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return models.Refreshtoken.destroyByUser(_.omit(options, 'transacting'));
|
return models.Refreshtoken.destroyByUser(options);
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
common.logging.error(new common.errors.GhostError({
|
common.logging.error(new common.errors.GhostError({
|
||||||
|
|
|
@ -2,5 +2,6 @@ module.exports = {
|
||||||
filter: require('./filter'),
|
filter: require('./filter'),
|
||||||
includeCount: require('./include-count'),
|
includeCount: require('./include-count'),
|
||||||
pagination: require('./pagination'),
|
pagination: require('./pagination'),
|
||||||
collision: require('./collision')
|
collision: require('./collision'),
|
||||||
|
transactionEvents: require('./transaction-events')
|
||||||
};
|
};
|
||||||
|
|
28
core/server/models/plugins/transaction-events.js
Normal file
28
core/server/models/plugins/transaction-events.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a feature request in knex for 1.0.
|
||||||
|
* https://github.com/tgriesser/knex/issues/1641
|
||||||
|
*/
|
||||||
|
module.exports = function (bookshelf) {
|
||||||
|
const orig1 = bookshelf.transaction;
|
||||||
|
|
||||||
|
bookshelf.transaction = function (cb) {
|
||||||
|
return orig1.bind(bookshelf)(function (t) {
|
||||||
|
const orig2 = t.commit;
|
||||||
|
const orig3 = t.rollback;
|
||||||
|
|
||||||
|
t.commit = function () {
|
||||||
|
t.emit('committed', true);
|
||||||
|
return orig2.apply(t, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
t.rollback = function () {
|
||||||
|
t.emit('committed', false);
|
||||||
|
return orig3.apply(t, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
return cb(t);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -61,15 +61,19 @@ Post = ghostBookshelf.Model.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
emitChange: function emitChange(event, options) {
|
emitChange: function emitChange(event, options) {
|
||||||
options = options || {};
|
let eventToTrigger;
|
||||||
|
|
||||||
var resourceType = this.get('page') ? 'page' : 'post';
|
var resourceType = this.get('page') ? 'page' : 'post';
|
||||||
|
|
||||||
if (options.usePreviousResourceType) {
|
if (options.useUpdatedAttribute) {
|
||||||
resourceType = this.updated('page') ? 'page' : 'post';
|
resourceType = this.updated('page') ? 'page' : 'post';
|
||||||
|
} else if (options.usePreviousAttribute) {
|
||||||
|
resourceType = this.previous('page') ? 'page' : 'post';
|
||||||
}
|
}
|
||||||
|
|
||||||
common.events.emit(resourceType + '.' + event, this, options);
|
eventToTrigger = resourceType + '.' + event;
|
||||||
|
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,14 +92,14 @@ Post = ghostBookshelf.Model.extend({
|
||||||
|
|
||||||
var status = model.get('status');
|
var status = model.get('status');
|
||||||
|
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
|
|
||||||
if (['published', 'scheduled'].indexOf(status) !== -1) {
|
if (['published', 'scheduled'].indexOf(status) !== -1) {
|
||||||
model.emitChange(status, {importing: options.importing});
|
model.emitChange(status, options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onUpdated: function onUpdated(model) {
|
onUpdated: function onUpdated(model, attrs, options) {
|
||||||
model.statusChanging = model.get('status') !== model.updated('status');
|
model.statusChanging = model.get('status') !== model.updated('status');
|
||||||
model.isPublished = model.get('status') === 'published';
|
model.isPublished = model.get('status') === 'published';
|
||||||
model.isScheduled = model.get('status') === 'scheduled';
|
model.isScheduled = model.get('status') === 'scheduled';
|
||||||
|
@ -108,65 +112,65 @@ Post = ghostBookshelf.Model.extend({
|
||||||
// Handle added and deleted for post -> page or page -> post
|
// Handle added and deleted for post -> page or page -> post
|
||||||
if (model.resourceTypeChanging) {
|
if (model.resourceTypeChanging) {
|
||||||
if (model.wasPublished) {
|
if (model.wasPublished) {
|
||||||
model.emitChange('unpublished', {usePreviousResourceType: true});
|
model.emitChange('unpublished', Object.assign({useUpdatedAttribute: true}, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.wasScheduled) {
|
if (model.wasScheduled) {
|
||||||
model.emitChange('unscheduled', {usePreviousResourceType: true});
|
model.emitChange('unscheduled', Object.assign({useUpdatedAttribute: true}, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.emitChange('deleted', {usePreviousResourceType: true});
|
model.emitChange('deleted', Object.assign({useUpdatedAttribute: true}, options));
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
|
|
||||||
if (model.isPublished) {
|
if (model.isPublished) {
|
||||||
model.emitChange('published');
|
model.emitChange('published', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.isScheduled) {
|
if (model.isScheduled) {
|
||||||
model.emitChange('scheduled');
|
model.emitChange('scheduled', options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (model.statusChanging) {
|
if (model.statusChanging) {
|
||||||
// CASE: was published before and is now e.q. draft or scheduled
|
// CASE: was published before and is now e.q. draft or scheduled
|
||||||
if (model.wasPublished) {
|
if (model.wasPublished) {
|
||||||
model.emitChange('unpublished');
|
model.emitChange('unpublished', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE: was draft or scheduled before and is now e.q. published
|
// CASE: was draft or scheduled before and is now e.q. published
|
||||||
if (model.isPublished) {
|
if (model.isPublished) {
|
||||||
model.emitChange('published');
|
model.emitChange('published', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE: was draft or published before and is now e.q. scheduled
|
// CASE: was draft or published before and is now e.q. scheduled
|
||||||
if (model.isScheduled) {
|
if (model.isScheduled) {
|
||||||
model.emitChange('scheduled');
|
model.emitChange('scheduled', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE: from scheduled to something
|
// CASE: from scheduled to something
|
||||||
if (model.wasScheduled && !model.isScheduled && !model.isPublished) {
|
if (model.wasScheduled && !model.isScheduled && !model.isPublished) {
|
||||||
model.emitChange('unscheduled');
|
model.emitChange('unscheduled', options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (model.isPublished) {
|
if (model.isPublished) {
|
||||||
model.emitChange('published.edited');
|
model.emitChange('published.edited', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.needsReschedule) {
|
if (model.needsReschedule) {
|
||||||
model.emitChange('rescheduled');
|
model.emitChange('rescheduled', options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire edited if this wasn't a change between resourceType
|
// Fire edited if this wasn't a change between resourceType
|
||||||
model.emitChange('edited');
|
model.emitChange('edited', options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroying: function onDestroying(model) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
if (model.previous('status') === 'published') {
|
if (model.previous('status') === 'published') {
|
||||||
model.emitChange('unpublished');
|
model.emitChange('unpublished', Object.assign({usePreviousAttribute: true}, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.emitChange('deleted');
|
model.emitChange('deleted', Object.assign({usePreviousAttribute: true}, options));
|
||||||
},
|
},
|
||||||
|
|
||||||
onSaving: function onSaving(model, attr, options) {
|
onSaving: function onSaving(model, attr, options) {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
var Settings,
|
'use strict';
|
||||||
Promise = require('bluebird'),
|
|
||||||
|
const Promise = require('bluebird'),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
uuid = require('uuid'),
|
uuid = require('uuid'),
|
||||||
crypto = require('crypto'),
|
crypto = require('crypto'),
|
||||||
ghostBookshelf = require('./base'),
|
ghostBookshelf = require('./base'),
|
||||||
common = require('../lib/common'),
|
common = require('../lib/common'),
|
||||||
validation = require('../data/validation'),
|
validation = require('../data/validation'),
|
||||||
|
internalContext = {context: {internal: true}};
|
||||||
|
|
||||||
internalContext = {context: {internal: true}},
|
let Settings, defaultSettings;
|
||||||
|
|
||||||
defaultSettings;
|
|
||||||
|
|
||||||
// For neatness, the defaults file is split into categories.
|
// For neatness, the defaults file is split into categories.
|
||||||
// It's much easier for us to work with it as a single level
|
// It's much easier for us to work with it as a single level
|
||||||
|
@ -58,21 +58,22 @@ Settings = ghostBookshelf.Model.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
emitChange: function emitChange(event, options) {
|
emitChange: function emitChange(event, options) {
|
||||||
common.events.emit('settings' + '.' + event, this, options);
|
const eventToTrigger = 'settings' + '.' + event;
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroyed: function onDestroyed(model, response, options) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
model.emitChange('deleted');
|
model.emitChange('deleted', options);
|
||||||
model.emitChange(model.attributes.key + '.' + 'deleted', options);
|
model.emitChange(model._previousAttributes.key + '.' + 'deleted', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreated: function onCreated(model, response, options) {
|
onCreated: function onCreated(model, response, options) {
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
model.emitChange(model.attributes.key + '.' + 'added', options);
|
model.emitChange(model.attributes.key + '.' + 'added', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onUpdated: function onUpdated(model, response, options) {
|
onUpdated: function onUpdated(model, response, options) {
|
||||||
model.emitChange('edited');
|
model.emitChange('edited', options);
|
||||||
model.emitChange(model.attributes.key + '.' + 'edited', options);
|
model.emitChange(model.attributes.key + '.' + 'edited', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
var Promise = require('bluebird'),
|
'use strict';
|
||||||
|
|
||||||
|
const Promise = require('bluebird'),
|
||||||
ghostBookshelf = require('./base'),
|
ghostBookshelf = require('./base'),
|
||||||
common = require('../lib/common'),
|
common = require('../lib/common');
|
||||||
Subscriber,
|
|
||||||
|
let Subscriber,
|
||||||
Subscribers;
|
Subscribers;
|
||||||
|
|
||||||
Subscriber = ghostBookshelf.Model.extend({
|
Subscriber = ghostBookshelf.Model.extend({
|
||||||
tableName: 'subscribers',
|
tableName: 'subscribers',
|
||||||
|
|
||||||
emitChange: function emitChange(event, options) {
|
emitChange: function emitChange(event, options) {
|
||||||
options = options || {};
|
const eventToTrigger = 'subscriber' + '.' + event;
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
common.events.emit('subscriber' + '.' + event, this, options);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
defaults: function defaults() {
|
defaults: function defaults() {
|
||||||
|
@ -27,7 +29,7 @@ Subscriber = ghostBookshelf.Model.extend({
|
||||||
model.emitChange('edited', options);
|
model.emitChange('edited', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroyed: function onDestroyed(model, response, options) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
model.emitChange('deleted', options);
|
model.emitChange('deleted', options);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
var ghostBookshelf = require('./base'),
|
'use strict';
|
||||||
common = require('../lib/common'),
|
|
||||||
Tag,
|
const ghostBookshelf = require('./base');
|
||||||
Tags;
|
let Tag, Tags;
|
||||||
|
|
||||||
Tag = ghostBookshelf.Model.extend({
|
Tag = ghostBookshelf.Model.extend({
|
||||||
|
|
||||||
|
@ -13,20 +13,21 @@ Tag = ghostBookshelf.Model.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
emitChange: function emitChange(event) {
|
emitChange: function emitChange(event, options) {
|
||||||
common.events.emit('tag' + '.' + event, this);
|
const eventToTrigger = 'tag' + '.' + event;
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreated: function onCreated(model) {
|
onCreated: function onCreated(model, attrs, options) {
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onUpdated: function onUpdated(model) {
|
onUpdated: function onUpdated(model, attrs, options) {
|
||||||
model.emitChange('edited');
|
model.emitChange('edited', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroyed: function onDestroyed(model) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
model.emitChange('deleted');
|
model.emitChange('deleted', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSaving: function onSaving(newTag, attr, options) {
|
onSaving: function onSaving(newTag, attr, options) {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
var _ = require('lodash'),
|
'use strict';
|
||||||
|
|
||||||
|
const _ = require('lodash'),
|
||||||
Promise = require('bluebird'),
|
Promise = require('bluebird'),
|
||||||
validator = require('validator'),
|
validator = require('validator'),
|
||||||
ObjectId = require('bson-objectid'),
|
ObjectId = require('bson-objectid'),
|
||||||
|
@ -15,9 +17,9 @@ var _ = require('lodash'),
|
||||||
* locked user: imported users, they get a random passport
|
* locked user: imported users, they get a random passport
|
||||||
*/
|
*/
|
||||||
inactiveStates = ['inactive', 'locked'],
|
inactiveStates = ['inactive', 'locked'],
|
||||||
allStates = activeStates.concat(inactiveStates),
|
allStates = activeStates.concat(inactiveStates);
|
||||||
User,
|
|
||||||
Users;
|
let User, Users;
|
||||||
|
|
||||||
User = ghostBookshelf.Model.extend({
|
User = ghostBookshelf.Model.extend({
|
||||||
|
|
||||||
|
@ -30,23 +32,24 @@ User = ghostBookshelf.Model.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
emitChange: function emitChange(event, options) {
|
emitChange: function emitChange(event, options) {
|
||||||
common.events.emit('user' + '.' + event, this, options);
|
const eventToTrigger = 'user' + '.' + event;
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroyed: function onDestroyed(model, response, options) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
if (_.includes(activeStates, model.previous('status'))) {
|
if (_.includes(activeStates, model.previous('status'))) {
|
||||||
model.emitChange('deactivated', options);
|
model.emitChange('deactivated', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.emitChange('deleted');
|
model.emitChange('deleted', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreated: function onCreated(model) {
|
onCreated: function onCreated(model, attrs, options) {
|
||||||
model.emitChange('added');
|
model.emitChange('added', options);
|
||||||
|
|
||||||
// active is the default state, so if status isn't provided, this will be an active user
|
// active is the default state, so if status isn't provided, this will be an active user
|
||||||
if (!model.get('status') || _.includes(activeStates, model.get('status'))) {
|
if (!model.get('status') || _.includes(activeStates, model.get('status'))) {
|
||||||
model.emitChange('activated');
|
model.emitChange('activated', options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -58,11 +61,11 @@ User = ghostBookshelf.Model.extend({
|
||||||
model.emitChange(model.isActive ? 'activated' : 'deactivated', options);
|
model.emitChange(model.isActive ? 'activated' : 'deactivated', options);
|
||||||
} else {
|
} else {
|
||||||
if (model.isActive) {
|
if (model.isActive) {
|
||||||
model.emitChange('activated.edited');
|
model.emitChange('activated.edited', options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model.emitChange('edited');
|
model.emitChange('edited', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
isActive: function isActive() {
|
isActive: function isActive() {
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
var Promise = require('bluebird'),
|
'use strict';
|
||||||
ghostBookshelf = require('./base'),
|
|
||||||
common = require('../lib/common'),
|
const Promise = require('bluebird'),
|
||||||
Webhook,
|
ghostBookshelf = require('./base');
|
||||||
|
|
||||||
|
let Webhook,
|
||||||
Webhooks;
|
Webhooks;
|
||||||
|
|
||||||
Webhook = ghostBookshelf.Model.extend({
|
Webhook = ghostBookshelf.Model.extend({
|
||||||
tableName: 'webhooks',
|
tableName: 'webhooks',
|
||||||
|
|
||||||
emitChange: function emitChange(event, options) {
|
emitChange: function emitChange(event, options) {
|
||||||
options = options || {};
|
const eventToTrigger = 'webhook' + '.' + event;
|
||||||
|
ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
|
||||||
common.events.emit('webhook' + '.' + event, this, options);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreated: function onCreated(model, response, options) {
|
onCreated: function onCreated(model, response, options) {
|
||||||
|
@ -21,7 +22,7 @@ Webhook = ghostBookshelf.Model.extend({
|
||||||
model.emitChange('edited', options);
|
model.emitChange('edited', options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDestroyed: function onDestroyed(model, response, options) {
|
onDestroyed: function onDestroyed(model, options) {
|
||||||
model.emitChange('deleted', options);
|
model.emitChange('deleted', options);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -424,6 +424,48 @@ describe('Post Model', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('[failure] multiple edits in one transaction', function () {
|
||||||
|
const options = _.cloneDeep(context),
|
||||||
|
data = {
|
||||||
|
status: 'published'
|
||||||
|
};
|
||||||
|
|
||||||
|
return models.Base.transaction(function (txn) {
|
||||||
|
options.transacting = txn;
|
||||||
|
|
||||||
|
return models.Post.edit(data, _.merge({id: testUtils.DataGenerator.Content.posts[3].id}, options))
|
||||||
|
.then(function () {
|
||||||
|
return models.Post.edit(data, _.merge({id: testUtils.DataGenerator.Content.posts[5].id}, options));
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
// force rollback
|
||||||
|
throw new Error();
|
||||||
|
});
|
||||||
|
}).catch(function () {
|
||||||
|
// txn was rolled back
|
||||||
|
Object.keys(eventsTriggered).length.should.eql(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiple edits in one transaction', function () {
|
||||||
|
const options = _.cloneDeep(context),
|
||||||
|
data = {
|
||||||
|
status: 'published'
|
||||||
|
};
|
||||||
|
|
||||||
|
return models.Base.transaction(function (txn) {
|
||||||
|
options.transacting = txn;
|
||||||
|
|
||||||
|
return models.Post.edit(data, _.merge({id: testUtils.DataGenerator.Content.posts[3].id}, options))
|
||||||
|
.then(function () {
|
||||||
|
return models.Post.edit(data, _.merge({id: testUtils.DataGenerator.Content.posts[5].id}, options));
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
// txn was successful
|
||||||
|
Object.keys(eventsTriggered).length.should.eql(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('can change title', function (done) {
|
it('can change title', function (done) {
|
||||||
var postId = testUtils.DataGenerator.Content.posts[0].id;
|
var postId = testUtils.DataGenerator.Content.posts[0].id;
|
||||||
|
|
||||||
|
@ -932,7 +974,10 @@ describe('Post Model', function () {
|
||||||
post.status.should.equal('draft');
|
post.status.should.equal('draft');
|
||||||
|
|
||||||
// Test changing status and published_by at the same time
|
// Test changing status and published_by at the same time
|
||||||
return models.Post.edit({status: 'published', published_by: 4}, _.extend({}, context, {id: postId}));
|
return models.Post.edit({
|
||||||
|
status: 'published',
|
||||||
|
published_by: 4
|
||||||
|
}, _.extend({}, context, {id: postId}));
|
||||||
}).then(function (edited) {
|
}).then(function (edited) {
|
||||||
should.exist(edited);
|
should.exist(edited);
|
||||||
edited.attributes.status.should.equal('published');
|
edited.attributes.status.should.equal('published');
|
||||||
|
|
Loading…
Add table
Reference in a new issue