diff --git a/core/server/api/canary/members.js b/core/server/api/canary/members.js index fd8d9a501e..ad2236bc5d 100644 --- a/core/server/api/canary/members.js +++ b/core/server/api/canary/members.js @@ -13,6 +13,7 @@ const {i18n} = require('../../lib/common'); const db = require('../../data/db'); const ghostMailer = new GhostMailer(); +const allowedIncludes = ['email_recipients']; module.exports = { docName: 'members', @@ -51,15 +52,35 @@ module.exports = { }, read: { + options: [ + 'include' + ], headers: {}, data: [ 'id', 'email' ], - validation: {}, + validation: { + options: { + include: { + values: allowedIncludes + } + } + }, permissions: true, async query(frame) { - frame.options.withRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer']; + const defaultWithRelated = ['labels', 'stripeSubscriptions', 'stripeSubscriptions.customer']; + + if (!frame.options.withRelated) { + frame.options.withRelated = defaultWithRelated; + } else { + frame.options.withRelated = frame.options.withRelated.concat(defaultWithRelated); + } + + if (frame.options.withRelated.includes('email_recipients')) { + frame.options.withRelated.push('email_recipients.email'); + } + let model = await membersService.api.members.get(frame.data, frame.options); if (!model) { diff --git a/core/server/api/canary/utils/serializers/output/members.js b/core/server/api/canary/utils/serializers/output/members.js index fc1ae89011..f64c363ee3 100644 --- a/core/server/api/canary/utils/serializers/output/members.js +++ b/core/server/api/canary/utils/serializers/output/members.js @@ -103,7 +103,8 @@ function serializeMember(member, options) { comped: comped, email_count: json.email_count, email_opened_count: json.email_opened_count, - email_open_rate: json.email_open_rate + email_open_rate: json.email_open_rate, + email_recipients: json.email_recipients }; } @@ -150,6 +151,7 @@ function createSerializer(debugString, serialize) { * @prop {number} email_count * @prop {number} email_opened_count * @prop {number} email_open_rate + * @prop {null|SerializedEmailRecipient[]} email_recipients */ /** @@ -180,6 +182,48 @@ function createSerializer(debugString, serialize) { * @prop {string} plan.currency_symbol */ +/** + * @typedef {Object} SerializedEmailRecipient + * + * @prop {string} id + * @prop {string} email_id + * @prop {string} batch_id + * @prop {string} processed_at + * @prop {string} delivered_at + * @prop {string} opened_at + * @prop {string} failed_at + * @prop {string} member_uuid + * @prop {string} member_email + * @prop {string} member_name + * @prop {SerializedEmail[]} email + */ + +/** + * @typedef {Object} SerializedEmail + * + * @prop {string} id + * @prop {string} post_id + * @prop {string} uuid + * @prop {string} status + * @prop {string} recipient_filter + * @prop {null|string} error + * @prop {string} error_data + * @prop {number} email_count + * @prop {number} delivered_count + * @prop {number} opened_count + * @prop {number} failed_count + * @prop {string} subject + * @prop {string} from + * @prop {string} reply_to + * @prop {string} html + * @prop {string} plaintext + * @prop {boolean} track_opens + * @prop {string} created_at + * @prop {string} created_by + * @prop {string} updated_at + * @prop {string} updated_by + */ + /** * @typedef {Object} APIConfig * @prop {string} docName diff --git a/core/server/models/email-recipient.js b/core/server/models/email-recipient.js index 894094923e..c64ee79db7 100644 --- a/core/server/models/email-recipient.js +++ b/core/server/models/email-recipient.js @@ -4,6 +4,12 @@ const EmailRecipient = ghostBookshelf.Model.extend({ tableName: 'email_recipients', hasTimestamps: false, + relationships: ['email'], + + relationshipBelongsTo: { + email: 'emails' + }, + email() { return this.belongsTo('Email', 'email_id'); }, diff --git a/core/server/models/member.js b/core/server/models/member.js index c208c95199..3b273d65f2 100644 --- a/core/server/models/member.js +++ b/core/server/models/member.js @@ -17,11 +17,12 @@ const Member = ghostBookshelf.Model.extend({ }; }, - relationships: ['labels', 'stripeCustomers'], + relationships: ['labels', 'stripeCustomers', 'email_recipients'], relationshipBelongsTo: { labels: 'labels', - stripeCustomers: 'members_stripe_customers' + stripeCustomers: 'members_stripe_customers', + email_recipients: 'email_recipients' }, labels: function labels() { @@ -50,6 +51,10 @@ const Member = ghostBookshelf.Model.extend({ ); }, + email_recipients() { + return this.hasMany('EmailRecipient', 'member_id', 'id'); + }, + serialize(options) { const defaultSerializedObject = ghostBookshelf.Model.prototype.serialize.call(this, options);