mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
✨ Added ability to send a newsletter to members with a certain label or product (#12932)
refs https://github.com/TryGhost/Team/issues/581 refs https://github.com/TryGhost/Team/issues/582 When publishing a post via the API it was possible to send it using `?email_recipient_filter=all/free/paid` which allowed you to send to members only based on their payment status which is quite limiting for some sites. This PR updates the `?email_recipient_filter` query param to support Ghost's `?filter` param syntax which enables more specific recipient lists, eg: `?email_recipient_filter=status:free` = free members only `?email_recipient_filter=status:paid` = paid members only `?email_recipient_filter=label:vip` = members that have the `vip` label attached `?email_recipient_filter=status:paid,label:vip` = paid members and members that have the `vip` label attached The older `free/paid` values are still supported by the API for backwards compatibility. - updates `Post` and `Email` models to transform legacy `free` and `paid` values to their NQL equivalents on read/write - lets us not worry about supporting legacy values elsewhere in the code - cleanup migration to transform all rows slated for 5.0 - removes schema and API `isIn` validations for recipient filters so allow free-form filters - updates posts API input serializers to transform `free` and `paid` values in the `?email_recipient_filter` param to their NQL equivalents for backwards compatibility - updates Post API controllers `edit` methods to run a query using the supplied filter to verify that it's valid - updates `mega` service to use the filter directly when selecting recipients
This commit is contained in:
parent
1ee97ccfbc
commit
322664a145
15 changed files with 351 additions and 27 deletions
|
@ -3,6 +3,7 @@ const i18n = require('../../../shared/i18n');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
const urlUtils = require('../../../shared/url-utils');
|
const urlUtils = require('../../../shared/url-utils');
|
||||||
const {mega} = require('../../services/mega');
|
const {mega} = require('../../services/mega');
|
||||||
|
const {BadRequestError} = require('@tryghost/errors');
|
||||||
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
||||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||||
|
|
||||||
|
@ -141,9 +142,6 @@ module.exports = {
|
||||||
source: {
|
source: {
|
||||||
values: ['html']
|
values: ['html']
|
||||||
},
|
},
|
||||||
email_recipient_filter: {
|
|
||||||
values: ['none', 'free', 'paid', 'all']
|
|
||||||
},
|
|
||||||
send_email_when_published: {
|
send_email_when_published: {
|
||||||
values: [true, false]
|
values: [true, false]
|
||||||
}
|
}
|
||||||
|
@ -183,7 +181,20 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Handle newsletter email */
|
/**Handle newsletter email */
|
||||||
if (model.get('email_recipient_filter') !== 'none') {
|
const emailRecipientFilter = model.get('email_recipient_filter');
|
||||||
|
if (emailRecipientFilter !== 'none') {
|
||||||
|
if (emailRecipientFilter !== 'all') {
|
||||||
|
// check filter is valid
|
||||||
|
try {
|
||||||
|
await models.Member.findPage({filter: `subscribed:true+${emailRecipientFilter}`, limit: 1});
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(new BadRequestError({
|
||||||
|
message: i18n.t('errors.api.posts.invalidEmailRecipientFilter'),
|
||||||
|
context: err.message
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const postPublished = model.wasChanged() && (model.get('status') === 'published') && (model.previous('status') !== 'published');
|
const postPublished = model.wasChanged() && (model.get('status') === 'published') && (model.previous('status') !== 'published');
|
||||||
if (postPublished) {
|
if (postPublished) {
|
||||||
let postEmail = model.relations.email;
|
let postEmail = model.relations.email;
|
||||||
|
|
|
@ -102,6 +102,15 @@ const forceStatusFilter = (frame) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const transformLegacyEmailRecipientFilters = (frame) => {
|
||||||
|
if (frame.options.email_recipient_filter === 'free') {
|
||||||
|
frame.options.email_recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (frame.options.email_recipient_filter === 'paid') {
|
||||||
|
frame.options.email_recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
browse(apiConfig, frame) {
|
browse(apiConfig, frame) {
|
||||||
debug('browse');
|
debug('browse');
|
||||||
|
@ -196,6 +205,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformLegacyEmailRecipientFilters(frame);
|
||||||
handlePostsMeta(frame);
|
handlePostsMeta(frame);
|
||||||
defaultFormat(frame);
|
defaultFormat(frame);
|
||||||
defaultRelations(frame);
|
defaultRelations(frame);
|
||||||
|
@ -205,6 +215,7 @@ module.exports = {
|
||||||
debug('edit');
|
debug('edit');
|
||||||
this.add(apiConfig, frame, {add: false});
|
this.add(apiConfig, frame, {add: false});
|
||||||
|
|
||||||
|
transformLegacyEmailRecipientFilters(frame);
|
||||||
handlePostsMeta(frame);
|
handlePostsMeta(frame);
|
||||||
forceStatusFilter(frame);
|
forceStatusFilter(frame);
|
||||||
forcePageFilter(frame);
|
forcePageFilter(frame);
|
||||||
|
|
|
@ -3,6 +3,7 @@ const i18n = require('../../../shared/i18n');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
const urlUtils = require('../../../shared/url-utils');
|
const urlUtils = require('../../../shared/url-utils');
|
||||||
const {mega} = require('../../services/mega');
|
const {mega} = require('../../services/mega');
|
||||||
|
const {BadRequestError} = require('@tryghost/errors');
|
||||||
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
||||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||||
|
|
||||||
|
@ -141,9 +142,6 @@ module.exports = {
|
||||||
source: {
|
source: {
|
||||||
values: ['html']
|
values: ['html']
|
||||||
},
|
},
|
||||||
email_recipient_filter: {
|
|
||||||
values: ['none', 'free', 'paid', 'all']
|
|
||||||
},
|
|
||||||
send_email_when_published: {
|
send_email_when_published: {
|
||||||
values: [true, false]
|
values: [true, false]
|
||||||
}
|
}
|
||||||
|
@ -183,7 +181,20 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Handle newsletter email */
|
/**Handle newsletter email */
|
||||||
if (model.get('email_recipient_filter') !== 'none') {
|
const emailRecipientFilter = model.get('email_recipient_filter');
|
||||||
|
if (emailRecipientFilter !== 'none') {
|
||||||
|
if (emailRecipientFilter !== 'all') {
|
||||||
|
// check filter is valid
|
||||||
|
try {
|
||||||
|
await models.Member.findPage({filter: `subscribed:true+${emailRecipientFilter}`, limit: 1});
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(new BadRequestError({
|
||||||
|
message: i18n.t('errors.api.posts.invalidEmailRecipientFilter'),
|
||||||
|
context: err.message
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const postPublished = model.wasChanged() && (model.get('status') === 'published') && (model.previous('status') !== 'published');
|
const postPublished = model.wasChanged() && (model.get('status') === 'published') && (model.previous('status') !== 'published');
|
||||||
if (postPublished) {
|
if (postPublished) {
|
||||||
let postEmail = model.relations.email;
|
let postEmail = model.relations.email;
|
||||||
|
|
|
@ -102,6 +102,15 @@ const forceStatusFilter = (frame) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const transformLegacyEmailRecipientFilters = (frame) => {
|
||||||
|
if (frame.options.email_recipient_filter === 'free') {
|
||||||
|
frame.options.email_recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (frame.options.email_recipient_filter === 'paid') {
|
||||||
|
frame.options.email_recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
browse(apiConfig, frame) {
|
browse(apiConfig, frame) {
|
||||||
debug('browse');
|
debug('browse');
|
||||||
|
@ -204,6 +213,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformLegacyEmailRecipientFilters(frame);
|
||||||
handlePostsMeta(frame);
|
handlePostsMeta(frame);
|
||||||
defaultFormat(frame);
|
defaultFormat(frame);
|
||||||
defaultRelations(frame);
|
defaultRelations(frame);
|
||||||
|
@ -213,6 +223,7 @@ module.exports = {
|
||||||
debug('edit');
|
debug('edit');
|
||||||
this.add(apiConfig, frame, {add: false});
|
this.add(apiConfig, frame, {add: false});
|
||||||
|
|
||||||
|
transformLegacyEmailRecipientFilters(frame);
|
||||||
handlePostsMeta(frame);
|
handlePostsMeta(frame);
|
||||||
forceStatusFilter(frame);
|
forceStatusFilter(frame);
|
||||||
forcePageFilter(frame);
|
forcePageFilter(frame);
|
||||||
|
|
|
@ -34,8 +34,7 @@ module.exports = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
maxlength: 50,
|
maxlength: 50,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultTo: 'none',
|
defaultTo: 'none'
|
||||||
validations: {isIn: [['none', 'all', 'free', 'paid']]}
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @deprecated: single authors was superceded by multiple authors in Ghost 1.22.0
|
* @deprecated: single authors was superceded by multiple authors in Ghost 1.22.0
|
||||||
|
@ -529,8 +528,7 @@ module.exports = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
maxlength: 50,
|
maxlength: 50,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultTo: 'paid',
|
defaultTo: 'status:-free'
|
||||||
validations: {isIn: [['all', 'free', 'paid']]}
|
|
||||||
},
|
},
|
||||||
error: {type: 'string', maxlength: 2000, nullable: true},
|
error: {type: 'string', maxlength: 2000, nullable: true},
|
||||||
error_data: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
error_data: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true},
|
||||||
|
|
|
@ -8,7 +8,7 @@ const Email = ghostBookshelf.Model.extend({
|
||||||
return {
|
return {
|
||||||
uuid: uuid.v4(),
|
uuid: uuid.v4(),
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
recipient_filter: 'paid',
|
recipient_filter: 'status:-free',
|
||||||
track_opens: false,
|
track_opens: false,
|
||||||
delivered_count: 0,
|
delivered_count: 0,
|
||||||
opened_count: 0,
|
opened_count: 0,
|
||||||
|
@ -16,12 +16,40 @@ const Email = ghostBookshelf.Model.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
parse() {
|
||||||
|
const attrs = ghostBookshelf.Model.prototype.parse.apply(this, arguments);
|
||||||
|
|
||||||
|
// update legacy recipient_filter values to proper NQL
|
||||||
|
if (attrs.recipient_filter === 'free') {
|
||||||
|
attrs.recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (attrs.recipient_filter === 'paid') {
|
||||||
|
attrs.recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
},
|
||||||
|
|
||||||
|
formatOnWrite(attrs) {
|
||||||
|
// update legacy recipient_filter values to proper NQL
|
||||||
|
if (attrs.recipient_filter === 'free') {
|
||||||
|
attrs.recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (attrs.recipient_filter === 'paid') {
|
||||||
|
attrs.recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
return this.belongsTo('Post', 'post_id');
|
return this.belongsTo('Post', 'post_id');
|
||||||
},
|
},
|
||||||
|
|
||||||
emailBatches() {
|
emailBatches() {
|
||||||
return this.hasMany('EmailBatch', 'email_id');
|
return this.hasMany('EmailBatch', 'email_id');
|
||||||
},
|
},
|
||||||
|
|
||||||
recipients() {
|
recipients() {
|
||||||
return this.hasMany('EmailRecipient', 'email_id');
|
return this.hasMany('EmailRecipient', 'email_id');
|
||||||
},
|
},
|
||||||
|
|
|
@ -101,6 +101,14 @@ Post = ghostBookshelf.Model.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update legacy email_recipient_filter values to proper NQL
|
||||||
|
if (attrs.email_recipient_filter === 'free') {
|
||||||
|
attrs.email_recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (attrs.email_recipient_filter === 'paid') {
|
||||||
|
attrs.email_recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
|
||||||
return attrs;
|
return attrs;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -139,6 +147,14 @@ Post = ghostBookshelf.Model.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update legacy email_recipient_filter values to proper NQL
|
||||||
|
if (attrs.email_recipient_filter === 'free') {
|
||||||
|
attrs.email_recipient_filter = 'status:free';
|
||||||
|
}
|
||||||
|
if (attrs.email_recipient_filter === 'paid') {
|
||||||
|
attrs.email_recipient_filter = 'status:-free';
|
||||||
|
}
|
||||||
|
|
||||||
return attrs;
|
return attrs;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -117,19 +117,17 @@ const addEmail = async (postModel, options) => {
|
||||||
const emailRecipientFilter = postModel.get('email_recipient_filter');
|
const emailRecipientFilter = postModel.get('email_recipient_filter');
|
||||||
|
|
||||||
switch (emailRecipientFilter) {
|
switch (emailRecipientFilter) {
|
||||||
|
// `paid` and `free` were swapped out for NQL filters in 4.5.0, we shouldn't see them here now
|
||||||
case 'paid':
|
case 'paid':
|
||||||
filterOptions.filter = 'subscribed:true+status:-free';
|
|
||||||
break;
|
|
||||||
case 'free':
|
case 'free':
|
||||||
filterOptions.filter = 'subscribed:true+status:free';
|
throw new Error(`Unexpected email_recipient_filter value "${emailRecipientFilter}", expected an NQL equivalent`);
|
||||||
break;
|
|
||||||
case 'all':
|
case 'all':
|
||||||
filterOptions.filter = 'subscribed:true';
|
filterOptions.filter = 'subscribed:true';
|
||||||
break;
|
break;
|
||||||
case 'none':
|
case 'none':
|
||||||
throw new Error('Cannot sent email to "none" email_recipient_filter');
|
throw new Error('Cannot sent email to "none" email_recipient_filter');
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown email_recipient_filter ${emailRecipientFilter}`);
|
filterOptions.filter = `subscribed:true+${emailRecipientFilter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startRetrieve = Date.now();
|
const startRetrieve = Date.now();
|
||||||
|
@ -314,24 +312,22 @@ async function sendEmailJob({emailModel, options}) {
|
||||||
// instantiations and associated processing and event loop blocking
|
// instantiations and associated processing and event loop blocking
|
||||||
async function getEmailMemberRows({emailModel, options}) {
|
async function getEmailMemberRows({emailModel, options}) {
|
||||||
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
||||||
|
|
||||||
// TODO: this will clobber a user-assigned filter if/when we allow emails to be sent to filtered member lists
|
|
||||||
const filterOptions = Object.assign({}, knexOptions);
|
const filterOptions = Object.assign({}, knexOptions);
|
||||||
|
|
||||||
const recipientFilter = emailModel.get('recipient_filter');
|
const recipientFilter = emailModel.get('recipient_filter');
|
||||||
|
|
||||||
switch (recipientFilter) {
|
switch (recipientFilter) {
|
||||||
|
// `paid` and `free` were swapped out for NQL filters in 4.5.0, we shouldn't see them here now
|
||||||
case 'paid':
|
case 'paid':
|
||||||
filterOptions.filter = 'subscribed:true+status:-free';
|
|
||||||
break;
|
|
||||||
case 'free':
|
case 'free':
|
||||||
filterOptions.filter = 'subscribed:true+status:free';
|
throw new Error(`Unexpected recipient_filter value "${recipientFilter}", expected an NQL equivalent`);
|
||||||
break;
|
|
||||||
case 'all':
|
case 'all':
|
||||||
filterOptions.filter = 'subscribed:true';
|
filterOptions.filter = 'subscribed:true';
|
||||||
break;
|
break;
|
||||||
|
case 'none':
|
||||||
|
throw new Error('Cannot sent email to "none" recipient_filter');
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown recipient_filter ${recipientFilter}`);
|
filterOptions.filter = `subscribed:true+${recipientFilter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const startRetrieve = Date.now();
|
const startRetrieve = Date.now();
|
||||||
|
|
|
@ -355,7 +355,8 @@
|
||||||
"notificationDoesNotExist": "Notification does not exist."
|
"notificationDoesNotExist": "Notification does not exist."
|
||||||
},
|
},
|
||||||
"posts": {
|
"posts": {
|
||||||
"postNotFound": "Post not found."
|
"postNotFound": "Post not found.",
|
||||||
|
"invalidEmailRecipientFilter": "Invalid filter in email_recipient_filter param."
|
||||||
},
|
},
|
||||||
"authors": {
|
"authors": {
|
||||||
"notFound": "Author not found."
|
"notFound": "Author not found."
|
||||||
|
|
|
@ -604,6 +604,40 @@ describe('Posts API (canary)', function () {
|
||||||
should.equal(res.body.posts[0].plaintext, undefined);
|
should.equal(res.body.posts[0].plaintext, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors with invalid email recipient filter', function () {
|
||||||
|
return request
|
||||||
|
.post(localUtils.API.getApiQuery('posts/'))
|
||||||
|
.set('Origin', config.get('url'))
|
||||||
|
.send({
|
||||||
|
posts: [{
|
||||||
|
title: 'Ready to be emailed',
|
||||||
|
status: 'draft'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
.expect(201)
|
||||||
|
.then((res) => {
|
||||||
|
return request
|
||||||
|
.put(`${localUtils.API.getApiQuery(`posts/${res.body.posts[0].id}/`)}?email_recipient_filter=not a filter`)
|
||||||
|
.set('Origin', config.get('url'))
|
||||||
|
.send({
|
||||||
|
posts: [{
|
||||||
|
title: res.body.posts[0].title,
|
||||||
|
mobilecdoc: res.body.posts[0].mobilecdoc,
|
||||||
|
updated_at: res.body.posts[0].updated_at,
|
||||||
|
status: 'published'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
.expect(400);
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
res.text.should.match(/invalid filter/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Destroy', function () {
|
describe('Destroy', function () {
|
||||||
|
|
|
@ -604,6 +604,40 @@ describe('Posts API (v3)', function () {
|
||||||
should.equal(res.body.posts[0].plaintext, undefined);
|
should.equal(res.body.posts[0].plaintext, undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors with invalid email recipient filter', function () {
|
||||||
|
return request
|
||||||
|
.post(localUtils.API.getApiQuery('posts/'))
|
||||||
|
.set('Origin', config.get('url'))
|
||||||
|
.send({
|
||||||
|
posts: [{
|
||||||
|
title: 'Ready to be emailed',
|
||||||
|
status: 'draft'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
.expect(201)
|
||||||
|
.then((res) => {
|
||||||
|
return request
|
||||||
|
.put(`${localUtils.API.getApiQuery(`posts/${res.body.posts[0].id}/`)}?email_recipient_filter=not a filter`)
|
||||||
|
.set('Origin', config.get('url'))
|
||||||
|
.send({
|
||||||
|
posts: [{
|
||||||
|
title: res.body.posts[0].title,
|
||||||
|
mobilecdoc: res.body.posts[0].mobilecdoc,
|
||||||
|
updated_at: res.body.posts[0].updated_at,
|
||||||
|
status: 'published'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
.expect(400);
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
res.text.should.match(/invalid filter/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Destroy', function () {
|
describe('Destroy', function () {
|
||||||
|
|
|
@ -60,6 +60,29 @@ describe('Post Model', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('findOne', function () {
|
||||||
|
it('transforms legacy email_recipient_filter values on read', function (done) {
|
||||||
|
const postId = testUtils.DataGenerator.Content.posts[0].id;
|
||||||
|
|
||||||
|
db.knex('posts').where({id: postId}).update({
|
||||||
|
email_recipient_filter: 'paid'
|
||||||
|
}).then(() => {
|
||||||
|
return db.knex('posts').where({id: postId});
|
||||||
|
}).then((knexResult) => {
|
||||||
|
const [knexPost] = knexResult;
|
||||||
|
knexPost.email_recipient_filter.should.equal('paid');
|
||||||
|
|
||||||
|
return models.Post.findOne({id: postId});
|
||||||
|
}).then((result) => {
|
||||||
|
should.exist(result);
|
||||||
|
const post = result.toJSON();
|
||||||
|
post.email_recipient_filter.should.equal('status:-free');
|
||||||
|
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('findPage', function () {
|
describe('findPage', function () {
|
||||||
// @TODO: this test case fails for mysql currently if you run all regression tests, the test does not fail if you run this as a single test
|
// @TODO: this test case fails for mysql currently if you run all regression tests, the test does not fail if you run this as a single test
|
||||||
describe.skip('with more posts/tags', function () {
|
describe.skip('with more posts/tags', function () {
|
||||||
|
@ -692,6 +715,24 @@ describe('Post Model', function () {
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('transforms legacy email_recipient_filter values on save', function (done) {
|
||||||
|
const postId = testUtils.DataGenerator.Content.posts[3].id;
|
||||||
|
|
||||||
|
models.Post.findOne({id: postId}).then(() => {
|
||||||
|
return models.Post.edit({
|
||||||
|
email_recipient_filter: 'free'
|
||||||
|
}, _.extend({}, context, {id: postId}));
|
||||||
|
}).then((edited) => {
|
||||||
|
edited.attributes.email_recipient_filter.should.equal('status:free');
|
||||||
|
return db.knex('posts').where({id: edited.id});
|
||||||
|
}).then((knexResult) => {
|
||||||
|
const [knexPost] = knexResult;
|
||||||
|
knexPost.email_recipient_filter.should.equal('status:free');
|
||||||
|
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('add', function () {
|
describe('add', function () {
|
||||||
|
|
|
@ -361,5 +361,71 @@ describe('Unit: canary/utils/serializers/input/posts', function () {
|
||||||
frame.data.posts[0].tags.should.eql([{name: 'name1'}, {name: 'name2'}]);
|
frame.data.posts[0].tags.should.eql([{name: 'name1'}, {name: 'name2'}]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('transforms legacy email recipient filter values', function () {
|
||||||
|
it('free becomes status:free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'free'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.edit({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:free');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('paid becomes status:-free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'paid'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.edit({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:-free');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('add', function () {
|
||||||
|
describe('transforms legacy email recipient filter values', function () {
|
||||||
|
it('free becomes status:free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'free'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.add({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:free');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('paid becomes status:-free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'paid'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.add({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:-free');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -361,5 +361,71 @@ describe('Unit: v3/utils/serializers/input/posts', function () {
|
||||||
frame.data.posts[0].tags.should.eql([{name: 'name1'}, {name: 'name2'}]);
|
frame.data.posts[0].tags.should.eql([{name: 'name1'}, {name: 'name2'}]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('transforms legacy email recipient filter values', function () {
|
||||||
|
it('free becomes status:free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'free'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.edit({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:free');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('paid becomes status:-free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'paid'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.edit({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:-free');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('add', function () {
|
||||||
|
describe('transforms legacy email recipient filter values', function () {
|
||||||
|
it('free becomes status:free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'free'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.add({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:free');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('paid becomes status:-free', function () {
|
||||||
|
const frame = {
|
||||||
|
options: {
|
||||||
|
email_recipient_filter: 'paid'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
posts: [{id: '1'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
serializers.input.posts.add({}, frame);
|
||||||
|
|
||||||
|
frame.options.email_recipient_filter.should.eql('status:-free');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,7 +32,7 @@ const defaultSettings = require('../../../../core/server/data/schema/default-set
|
||||||
*/
|
*/
|
||||||
describe('DB version integrity', function () {
|
describe('DB version integrity', function () {
|
||||||
// Only these variables should need updating
|
// Only these variables should need updating
|
||||||
const currentSchemaHash = 'b7bca80554f3946cd2f83e0e99ff3532';
|
const currentSchemaHash = 'bcb57235883ddb9765f9abf8ab878cd7';
|
||||||
const currentFixturesHash = '8671672598d2a62e53418c4b91aa79a3';
|
const currentFixturesHash = '8671672598d2a62e53418c4b91aa79a3';
|
||||||
const currentSettingsHash = 'c202cf5780aa77d8730a82680e2b142e';
|
const currentSettingsHash = 'c202cf5780aa77d8730a82680e2b142e';
|
||||||
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
|
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
|
||||||
|
|
Loading…
Add table
Reference in a new issue