mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
Added acceptance test for /member/:id/?include=email_recipients
(#12477)
refs c1d66f0b01
- fixed base model allowing '@@INDEXES@@' as a permitted attribute/order
- fixed base model automatically setting `@@INDEXES@@` to null on the model when creating
- added `doAuth('members:emails')`
- creates an `email_batch` record attached to the first email in the fixtures
- creates an `email_recipients` record for each member
- runs analytics aggregation so the email and member counts are as expected
- added acceptance test for `/member/:id/?include=email_recipients`
This commit is contained in:
parent
90adc9ed98
commit
8aa55feaf8
@ -172,13 +172,15 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||||||
|
|
||||||
// Ghost option handling - get permitted attributes from server/data/schema.js, where the DB schema is defined
|
// Ghost option handling - get permitted attributes from server/data/schema.js, where the DB schema is defined
|
||||||
permittedAttributes: function permittedAttributes() {
|
permittedAttributes: function permittedAttributes() {
|
||||||
return _.keys(schema.tables[this.tableName]);
|
return _.keys(schema.tables[this.tableName])
|
||||||
|
.filter(key => key.indexOf('@@') === -1);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Ghost ordering handling, allows to order by permitted attributes by default and can be overriden on specific model level
|
// Ghost ordering handling, allows to order by permitted attributes by default and can be overriden on specific model level
|
||||||
orderAttributes: function orderAttributes() {
|
orderAttributes: function orderAttributes() {
|
||||||
return Object.keys(schema.tables[this.tableName])
|
return Object.keys(schema.tables[this.tableName])
|
||||||
.map(key => `${this.tableName}.${key}`);
|
.map(key => `${this.tableName}.${key}`)
|
||||||
|
.filter(key => key.indexOf('@@') === -1);
|
||||||
},
|
},
|
||||||
|
|
||||||
// When loading an instance, subclasses can specify default to fetch
|
// When loading an instance, subclasses can specify default to fetch
|
||||||
@ -354,7 +356,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||||||
*
|
*
|
||||||
* Happens after validation to ensure we don't set fields which are not nullable on db level.
|
* Happens after validation to ensure we don't set fields which are not nullable on db level.
|
||||||
*/
|
*/
|
||||||
_.each(Object.keys(schema.tables[this.tableName]), (columnKey) => {
|
_.each(Object.keys(schema.tables[this.tableName]).filter(key => key.indexOf('@@') === -1), (columnKey) => {
|
||||||
if (model.get(columnKey) === undefined) {
|
if (model.get(columnKey) === undefined) {
|
||||||
model.set(columnKey, null);
|
model.set(columnKey, null);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ describe('Members API', function () {
|
|||||||
before(async function () {
|
before(async function () {
|
||||||
await testUtils.startGhost();
|
await testUtils.startGhost();
|
||||||
request = supertest.agent(config.get('url'));
|
request = supertest.agent(config.get('url'));
|
||||||
await localUtils.doAuth(request, 'members');
|
await localUtils.doAuth(request, 'members', 'members:emails');
|
||||||
sinon.stub(labs, 'isSet').withArgs('members').returns(true);
|
sinon.stub(labs, 'isSet').withArgs('members').returns(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,6 +123,25 @@ describe('Members API', function () {
|
|||||||
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can read and include email_recipients', async function () {
|
||||||
|
const res = await request
|
||||||
|
.get(localUtils.API.getApiQuery(`members/${testUtils.DataGenerator.Content.members[0].id}/?include=email_recipients`))
|
||||||
|
.set('Origin', config.get('url'))
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
|
const jsonResponse = res.body;
|
||||||
|
should.exist(jsonResponse);
|
||||||
|
should.exist(jsonResponse.members);
|
||||||
|
jsonResponse.members.should.have.length(1);
|
||||||
|
localUtils.API.checkResponse(jsonResponse.members[0], 'member', ['stripe', 'email_recipients']);
|
||||||
|
jsonResponse.members[0].email_recipients.length.should.equal(1);
|
||||||
|
localUtils.API.checkResponse(jsonResponse.members[0].email_recipients[0], 'email_recipient', ['email']);
|
||||||
|
localUtils.API.checkResponse(jsonResponse.members[0].email_recipients[0].email, 'email');
|
||||||
|
});
|
||||||
|
|
||||||
it('Can add', async function () {
|
it('Can add', async function () {
|
||||||
const member = {
|
const member = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
@ -115,6 +115,9 @@ const expectedProperties = {
|
|||||||
email: _(schema.emails)
|
email: _(schema.emails)
|
||||||
.keys(),
|
.keys(),
|
||||||
email_preview: ['html', 'subject', 'plaintext'],
|
email_preview: ['html', 'subject', 'plaintext'],
|
||||||
|
email_recipient: _(schema.email_recipients)
|
||||||
|
.keys()
|
||||||
|
.filter(key => key.indexOf('@@') === -1),
|
||||||
snippet: _(schema.snippets).keys()
|
snippet: _(schema.snippets).keys()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -310,23 +310,27 @@ DataGenerator.Content = {
|
|||||||
{
|
{
|
||||||
id: ObjectId.generate(),
|
id: ObjectId.generate(),
|
||||||
email: 'member1@test.com',
|
email: 'member1@test.com',
|
||||||
name: 'Mr Egg'
|
name: 'Mr Egg',
|
||||||
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b340'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ObjectId.generate(),
|
id: ObjectId.generate(),
|
||||||
email: 'member2@test.com',
|
email: 'member2@test.com',
|
||||||
email_open_rate: 50
|
email_open_rate: 50,
|
||||||
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b341'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ObjectId.generate(),
|
id: ObjectId.generate(),
|
||||||
email: 'paid@test.com',
|
email: 'paid@test.com',
|
||||||
name: 'Egon Spengler',
|
name: 'Egon Spengler',
|
||||||
email_open_rate: 80
|
email_open_rate: 80,
|
||||||
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b342'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: ObjectId.generate(),
|
id: ObjectId.generate(),
|
||||||
email: 'trialing@test.com',
|
email: 'trialing@test.com',
|
||||||
name: 'Ray Stantz'
|
name: 'Ray Stantz',
|
||||||
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b343'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -446,9 +450,11 @@ DataGenerator.Content = {
|
|||||||
uuid: '6b6afda6-4b5e-4893-bff6-f16859e8349a',
|
uuid: '6b6afda6-4b5e-4893-bff6-f16859e8349a',
|
||||||
status: 'submitted',
|
status: 'submitted',
|
||||||
email_count: 2,
|
email_count: 2,
|
||||||
|
recipient_filter: 'all',
|
||||||
subject: 'You got mailed!',
|
subject: 'You got mailed!',
|
||||||
html: '<p>Look! I\'m an email</p>',
|
html: '<p>Look! I\'m an email</p>',
|
||||||
plaintext: 'Waba-daba-dab-da',
|
plaintext: 'Waba-daba-dab-da',
|
||||||
|
track_opens: false,
|
||||||
submitted_at: moment().toDate()
|
submitted_at: moment().toDate()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -456,15 +462,72 @@ DataGenerator.Content = {
|
|||||||
uuid: '365daa11-4bf0-4614-ad43-6346387ffa00',
|
uuid: '365daa11-4bf0-4614-ad43-6346387ffa00',
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
error: 'Everything went south',
|
error: 'Everything went south',
|
||||||
stats: '',
|
|
||||||
email_count: 3,
|
email_count: 3,
|
||||||
subject: 'You got mailed! Again!',
|
subject: 'You got mailed! Again!',
|
||||||
html: '<p>What\'s that? Another email!</p>',
|
html: '<p>What\'s that? Another email!</p>',
|
||||||
plaintext: 'yes this is an email',
|
plaintext: 'yes this is an email',
|
||||||
|
track_opens: false,
|
||||||
submitted_at: moment().toDate()
|
submitted_at: moment().toDate()
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
email_batches: [
|
||||||
|
{
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
email_id: null, // emails[0] relation added later
|
||||||
|
// TODO: cleanup <> in provider_id
|
||||||
|
provider_id: '<email1@testing.mailgun.net>',
|
||||||
|
status: 'submitted'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
email_recipients: [
|
||||||
|
{
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
email_id: null, // emails[0] relation added later
|
||||||
|
member_id: null, // members[0] relation added later
|
||||||
|
batch_id: null, // email_batches[0] relation added later
|
||||||
|
processed_at: moment().toDate(),
|
||||||
|
failed_at: null,
|
||||||
|
member_uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b340',
|
||||||
|
member_email: 'member1@test.com',
|
||||||
|
member_name: 'Mr Egg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
email_id: null, // emails[0] relation added later
|
||||||
|
member_id: null, // members[1] relation added later
|
||||||
|
batch_id: null, // email_batches[0] relation added later
|
||||||
|
processed_at: moment().toDate(),
|
||||||
|
failed_at: null,
|
||||||
|
member_uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b341',
|
||||||
|
member_email: 'member2@test.com',
|
||||||
|
member_name: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
email_id: null, // emails[0] relation added later
|
||||||
|
member_id: null, // members[2] relation added later
|
||||||
|
batch_id: null, // email_batches[0] relation added later
|
||||||
|
processed_at: moment().toDate(),
|
||||||
|
failed_at: null,
|
||||||
|
member_uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b342',
|
||||||
|
member_email: 'member1@test.com',
|
||||||
|
member_name: 'Mr Egg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
email_id: null, // emails[0] relation added later
|
||||||
|
member_id: null, // members[3] relation added later
|
||||||
|
batch_id: null, // email_batches[0] relation added later
|
||||||
|
processed_at: moment().toDate(),
|
||||||
|
failed_at: null,
|
||||||
|
member_uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b343',
|
||||||
|
member_email: 'member1@test.com',
|
||||||
|
member_name: 'Mr Egg'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
snippets: [
|
snippets: [
|
||||||
{
|
{
|
||||||
id: ObjectId.generate(),
|
id: ObjectId.generate(),
|
||||||
@ -480,6 +543,19 @@ DataGenerator.Content.api_keys[0].integration_id = DataGenerator.Content.integra
|
|||||||
DataGenerator.Content.api_keys[1].integration_id = DataGenerator.Content.integrations[0].id;
|
DataGenerator.Content.api_keys[1].integration_id = DataGenerator.Content.integrations[0].id;
|
||||||
DataGenerator.Content.emails[0].post_id = DataGenerator.Content.posts[0].id;
|
DataGenerator.Content.emails[0].post_id = DataGenerator.Content.posts[0].id;
|
||||||
DataGenerator.Content.emails[1].post_id = DataGenerator.Content.posts[1].id;
|
DataGenerator.Content.emails[1].post_id = DataGenerator.Content.posts[1].id;
|
||||||
|
DataGenerator.Content.email_batches[0].email_id = DataGenerator.Content.emails[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[0].batch_id = DataGenerator.Content.email_batches[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[0].email_id = DataGenerator.Content.email_batches[0].email_id;
|
||||||
|
DataGenerator.Content.email_recipients[0].member_id = DataGenerator.Content.members[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[1].batch_id = DataGenerator.Content.email_batches[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[1].email_id = DataGenerator.Content.email_batches[0].email_id;
|
||||||
|
DataGenerator.Content.email_recipients[1].member_id = DataGenerator.Content.members[1].id;
|
||||||
|
DataGenerator.Content.email_recipients[2].batch_id = DataGenerator.Content.email_batches[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[2].email_id = DataGenerator.Content.email_batches[0].email_id;
|
||||||
|
DataGenerator.Content.email_recipients[2].member_id = DataGenerator.Content.members[2].id;
|
||||||
|
DataGenerator.Content.email_recipients[3].batch_id = DataGenerator.Content.email_batches[0].id;
|
||||||
|
DataGenerator.Content.email_recipients[3].email_id = DataGenerator.Content.email_batches[0].email_id;
|
||||||
|
DataGenerator.Content.email_recipients[3].member_id = DataGenerator.Content.members[3].id;
|
||||||
DataGenerator.Content.members_stripe_customers[0].member_id = DataGenerator.Content.members[2].id;
|
DataGenerator.Content.members_stripe_customers[0].member_id = DataGenerator.Content.members[2].id;
|
||||||
DataGenerator.Content.members_stripe_customers[1].member_id = DataGenerator.Content.members[3].id;
|
DataGenerator.Content.members_stripe_customers[1].member_id = DataGenerator.Content.members[3].id;
|
||||||
|
|
||||||
@ -758,6 +834,22 @@ DataGenerator.forKnex = (function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createEmailBatch(overrides) {
|
||||||
|
const newObj = _.cloneDeep(overrides);
|
||||||
|
return _.defaults(newObj, {
|
||||||
|
id: ObjectId.generate(),
|
||||||
|
created_at: new Date(),
|
||||||
|
updated_at: new Date()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEmailRecipient(overrides) {
|
||||||
|
const newObj = _.cloneDeep(overrides);
|
||||||
|
return _.defaults(newObj, {
|
||||||
|
id: ObjectId.generate()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const posts = [
|
const posts = [
|
||||||
createPost(DataGenerator.Content.posts[0]),
|
createPost(DataGenerator.Content.posts[0]),
|
||||||
createPost(DataGenerator.Content.posts[1]),
|
createPost(DataGenerator.Content.posts[1]),
|
||||||
@ -965,6 +1057,17 @@ DataGenerator.forKnex = (function () {
|
|||||||
createEmail(DataGenerator.Content.emails[1])
|
createEmail(DataGenerator.Content.emails[1])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const email_batches = [
|
||||||
|
createEmailBatch(DataGenerator.Content.email_batches[0])
|
||||||
|
];
|
||||||
|
|
||||||
|
const email_recipients = [
|
||||||
|
createEmailRecipient(DataGenerator.Content.email_recipients[0]),
|
||||||
|
createEmailRecipient(DataGenerator.Content.email_recipients[1]),
|
||||||
|
createEmailRecipient(DataGenerator.Content.email_recipients[2]),
|
||||||
|
createEmailRecipient(DataGenerator.Content.email_recipients[3])
|
||||||
|
];
|
||||||
|
|
||||||
const members = [
|
const members = [
|
||||||
createMember(DataGenerator.Content.members[0]),
|
createMember(DataGenerator.Content.members[0]),
|
||||||
createMember(DataGenerator.Content.members[1]),
|
createMember(DataGenerator.Content.members[1]),
|
||||||
@ -1035,6 +1138,8 @@ DataGenerator.forKnex = (function () {
|
|||||||
integrations,
|
integrations,
|
||||||
api_keys,
|
api_keys,
|
||||||
emails,
|
emails,
|
||||||
|
email_batches,
|
||||||
|
email_recipients,
|
||||||
labels,
|
labels,
|
||||||
members,
|
members,
|
||||||
members_labels,
|
members_labels,
|
||||||
|
@ -22,6 +22,7 @@ const routingService = require('../../core/frontend/services/routing');
|
|||||||
const settingsService = require('../../core/server/services/settings');
|
const settingsService = require('../../core/server/services/settings');
|
||||||
const frontendSettingsService = require('../../core/frontend/services/settings');
|
const frontendSettingsService = require('../../core/frontend/services/settings');
|
||||||
const settingsCache = require('../../core/server/services/settings/cache');
|
const settingsCache = require('../../core/server/services/settings/cache');
|
||||||
|
const emailAnalyticsService = require('../../core/server/services/email-analytics');
|
||||||
const imageLib = require('../../core/server/lib/image');
|
const imageLib = require('../../core/server/lib/image');
|
||||||
const web = require('../../core/server/web');
|
const web = require('../../core/server/web');
|
||||||
const permissions = require('../../core/server/services/permissions');
|
const permissions = require('../../core/server/services/permissions');
|
||||||
@ -501,6 +502,27 @@ fixtures = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
insertEmailsAndRecipients: function insertEmailsAndRecipients() {
|
||||||
|
return Promise.each(_.cloneDeep(DataGenerator.forKnex.emails), function (email) {
|
||||||
|
return models.Email.add(email, module.exports.context.internal);
|
||||||
|
}).then(function () {
|
||||||
|
return Promise.each(_.cloneDeep(DataGenerator.forKnex.email_batches), function (emailBatch) {
|
||||||
|
return models.EmailBatch.add(emailBatch, module.exports.context.internal);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
return Promise.each(_.cloneDeep(DataGenerator.forKnex.email_recipients), (emailRecipient) => {
|
||||||
|
return models.EmailRecipient.add(emailRecipient, module.exports.context.internal);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
const toAggregate = {
|
||||||
|
emailIds: DataGenerator.forKnex.emails.map(email => email.id),
|
||||||
|
memberIds: DataGenerator.forKnex.members.map(member => member.id)
|
||||||
|
};
|
||||||
|
|
||||||
|
return emailAnalyticsService.aggregateStats(toAggregate);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
insertSnippets: function insertSnippets() {
|
insertSnippets: function insertSnippets() {
|
||||||
return Promise.map(DataGenerator.forKnex.snippets, function (snippet) {
|
return Promise.map(DataGenerator.forKnex.snippets, function (snippet) {
|
||||||
return models.Snippet.add(snippet, module.exports.context.internal);
|
return models.Snippet.add(snippet, module.exports.context.internal);
|
||||||
@ -576,6 +598,9 @@ toDoList = {
|
|||||||
members: function insertMembersAndLabels() {
|
members: function insertMembersAndLabels() {
|
||||||
return fixtures.insertMembersAndLabels();
|
return fixtures.insertMembersAndLabels();
|
||||||
},
|
},
|
||||||
|
'members:emails': function insertEmailsAndRecipients() {
|
||||||
|
return fixtures.insertEmailsAndRecipients();
|
||||||
|
},
|
||||||
posts: function insertPostsAndTags() {
|
posts: function insertPostsAndTags() {
|
||||||
return fixtures.insertPostsAndTags();
|
return fixtures.insertPostsAndTags();
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user