mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Updated Admin API and Mega to use status flag (#12579)
no-issue * Removed support for paid param from v3 & canary API * Updated active subscription checks to use status flag * Updated MEGA to use status filter over paid flag * Removed support for paid option at model level * Installed @tryghost/members-api@1.0.0-rc.0 * Updated members fixtures
This commit is contained in:
parent
229295d671
commit
6af2706f10
14 changed files with 57 additions and 186 deletions
|
@ -133,9 +133,7 @@ function updateLocalTemplateOptions(req, res, next) {
|
||||||
firstname: req.member.name && req.member.name.split(' ')[0],
|
firstname: req.member.name && req.member.name.split(' ')[0],
|
||||||
avatar_image: req.member.avatar_image,
|
avatar_image: req.member.avatar_image,
|
||||||
subscriptions: req.member.stripe.subscriptions,
|
subscriptions: req.member.stripe.subscriptions,
|
||||||
paid: req.member.stripe.subscriptions.filter((subscription) => {
|
paid: req.member.status === 'paid'
|
||||||
return ['active', 'trialing', 'unpaid', 'past_due'].includes(subscription.status);
|
|
||||||
}).length !== 0
|
|
||||||
} : null;
|
} : null;
|
||||||
|
|
||||||
hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
|
hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
|
||||||
|
|
|
@ -34,8 +34,7 @@ module.exports = {
|
||||||
'order',
|
'order',
|
||||||
'debug',
|
'debug',
|
||||||
'page',
|
'page',
|
||||||
'search',
|
'search'
|
||||||
'paid'
|
|
||||||
],
|
],
|
||||||
permissions: true,
|
permissions: true,
|
||||||
validation: {},
|
validation: {},
|
||||||
|
@ -304,8 +303,7 @@ module.exports = {
|
||||||
options: [
|
options: [
|
||||||
'limit',
|
'limit',
|
||||||
'filter',
|
'filter',
|
||||||
'search',
|
'search'
|
||||||
'paid'
|
|
||||||
],
|
],
|
||||||
headers: {
|
headers: {
|
||||||
disposition: {
|
disposition: {
|
||||||
|
|
|
@ -34,8 +34,7 @@ module.exports = {
|
||||||
'order',
|
'order',
|
||||||
'debug',
|
'debug',
|
||||||
'page',
|
'page',
|
||||||
'search',
|
'search'
|
||||||
'paid'
|
|
||||||
],
|
],
|
||||||
permissions: true,
|
permissions: true,
|
||||||
validation: {},
|
validation: {},
|
||||||
|
@ -304,8 +303,7 @@ module.exports = {
|
||||||
options: [
|
options: [
|
||||||
'limit',
|
'limit',
|
||||||
'filter',
|
'filter',
|
||||||
'search',
|
'search'
|
||||||
'paid'
|
|
||||||
],
|
],
|
||||||
headers: {
|
headers: {
|
||||||
disposition: {
|
disposition: {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const ghostBookshelf = require('./base');
|
const ghostBookshelf = require('./base');
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const {sequence} = require('@tryghost/promise');
|
|
||||||
const config = require('../../shared/config');
|
const config = require('../../shared/config');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
@ -108,7 +107,6 @@ const Member = ghostBookshelf.Model.extend({
|
||||||
|
|
||||||
onSaving: function onSaving(model, attr, options) {
|
onSaving: function onSaving(model, attr, options) {
|
||||||
let labelsToSave = [];
|
let labelsToSave = [];
|
||||||
let ops = [];
|
|
||||||
|
|
||||||
// CASE: detect lowercase/uppercase label slugs
|
// CASE: detect lowercase/uppercase label slugs
|
||||||
if (!_.isUndefined(this.get('labels')) && !_.isNull(this.get('labels'))) {
|
if (!_.isUndefined(this.get('labels')) && !_.isNull(this.get('labels'))) {
|
||||||
|
@ -129,26 +127,23 @@ const Member = ghostBookshelf.Model.extend({
|
||||||
this.set('labels', labelsToSave);
|
this.set('labels', labelsToSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CASE: Detect existing labels with same case-insensitive name and replace
|
|
||||||
ops.push(function updateLabels() {
|
|
||||||
return ghostBookshelf.model('Label')
|
|
||||||
.findAll(Object.assign({
|
|
||||||
columns: ['id', 'name']
|
|
||||||
}, _.pick(options, 'transacting')))
|
|
||||||
.then((labels) => {
|
|
||||||
labelsToSave.forEach((label) => {
|
|
||||||
let existingLabel = labels.find((lab) => {
|
|
||||||
return label.name.toLowerCase() === lab.get('name').toLowerCase();
|
|
||||||
});
|
|
||||||
label.name = (existingLabel && existingLabel.get('name')) || label.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
model.set('labels', labelsToSave);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.handleAttachedModels(model);
|
this.handleAttachedModels(model);
|
||||||
return sequence(ops);
|
|
||||||
|
// CASE: Detect existing labels with same case-insensitive name and replace
|
||||||
|
return ghostBookshelf.model('Label')
|
||||||
|
.findAll(Object.assign({
|
||||||
|
columns: ['id', 'name']
|
||||||
|
}, _.pick(options, 'transacting')))
|
||||||
|
.then((labels) => {
|
||||||
|
labelsToSave.forEach((label) => {
|
||||||
|
let existingLabel = labels.find((lab) => {
|
||||||
|
return label.name.toLowerCase() === lab.get('name').toLowerCase();
|
||||||
|
});
|
||||||
|
label.name = (existingLabel && existingLabel.get('name')) || label.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
model.set('labels', labelsToSave);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleAttachedModels: function handleAttachedModels(model) {
|
handleAttachedModels: function handleAttachedModels(model) {
|
||||||
|
@ -209,40 +204,6 @@ const Member = ghostBookshelf.Model.extend({
|
||||||
queryBuilder.orWhere('members.email', 'like', `%${query}%`);
|
queryBuilder.orWhere('members.email', 'like', `%${query}%`);
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: hacky way to filter by members with an active subscription,
|
|
||||||
// replace with a proper way to do this via filter param.
|
|
||||||
// NOTE: assumes members will have a single subscription
|
|
||||||
customQuery: function customQuery(queryBuilder, options) {
|
|
||||||
if (options.paid === true) {
|
|
||||||
queryBuilder.innerJoin(
|
|
||||||
'members_stripe_customers',
|
|
||||||
'members.id',
|
|
||||||
'members_stripe_customers.member_id'
|
|
||||||
);
|
|
||||||
queryBuilder.innerJoin(
|
|
||||||
'members_stripe_customers_subscriptions',
|
|
||||||
function () {
|
|
||||||
this.on(
|
|
||||||
'members_stripe_customers.customer_id',
|
|
||||||
'members_stripe_customers_subscriptions.customer_id'
|
|
||||||
).onIn(
|
|
||||||
'members_stripe_customers_subscriptions.status',
|
|
||||||
['active', 'trialing', 'past_due', 'unpaid']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.paid === false) {
|
|
||||||
queryBuilder.leftJoin(
|
|
||||||
'members_stripe_customers',
|
|
||||||
'members.id',
|
|
||||||
'members_stripe_customers.member_id'
|
|
||||||
);
|
|
||||||
queryBuilder.whereNull('members_stripe_customers.member_id');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
orderRawQuery(field, direction) {
|
orderRawQuery(field, direction) {
|
||||||
if (field === 'email_open_rate') {
|
if (field === 'email_open_rate') {
|
||||||
return {
|
return {
|
||||||
|
@ -276,8 +237,7 @@ const Member = ghostBookshelf.Model.extend({
|
||||||
let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);
|
let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);
|
||||||
|
|
||||||
if (['findPage', 'findAll'].includes(methodName)) {
|
if (['findPage', 'findAll'].includes(methodName)) {
|
||||||
// TODO: remove 'paid' once it's possible to use in a filter
|
options = options.concat(['search']);
|
||||||
options = options.concat(['search', 'paid']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
|
|
|
@ -92,18 +92,19 @@ const sendTestEmail = async (postModel, toEmails) => {
|
||||||
|
|
||||||
const addEmail = async (postModel, options) => {
|
const addEmail = async (postModel, options) => {
|
||||||
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
|
||||||
const filterOptions = Object.assign({}, knexOptions, {filter: 'subscribed:true', limit: 1});
|
const filterOptions = Object.assign({}, knexOptions, {limit: 1});
|
||||||
|
|
||||||
const emailRecipientFilter = postModel.get('email_recipient_filter');
|
const emailRecipientFilter = postModel.get('email_recipient_filter');
|
||||||
|
|
||||||
switch (emailRecipientFilter) {
|
switch (emailRecipientFilter) {
|
||||||
case 'paid':
|
case 'paid':
|
||||||
filterOptions.paid = true;
|
filterOptions.filter = 'subscribed:true+status:paid';
|
||||||
break;
|
break;
|
||||||
case 'free':
|
case 'free':
|
||||||
filterOptions.paid = false;
|
filterOptions.filter = 'subscribed:true+status:free';
|
||||||
break;
|
break;
|
||||||
case 'all':
|
case 'all':
|
||||||
|
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');
|
||||||
|
@ -288,18 +289,19 @@ 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
|
// 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, {filter: 'subscribed:true'});
|
const filterOptions = Object.assign({}, knexOptions);
|
||||||
|
|
||||||
const recipientFilter = emailModel.get('recipient_filter');
|
const recipientFilter = emailModel.get('recipient_filter');
|
||||||
|
|
||||||
switch (recipientFilter) {
|
switch (recipientFilter) {
|
||||||
case 'paid':
|
case 'paid':
|
||||||
filterOptions.paid = true;
|
filterOptions.filter = 'subscribed:true+status:paid';
|
||||||
break;
|
break;
|
||||||
case 'free':
|
case 'free':
|
||||||
filterOptions.paid = false;
|
filterOptions.filter = 'subscribed:true+status:free';
|
||||||
break;
|
break;
|
||||||
case 'all':
|
case 'all':
|
||||||
|
filterOptions.filter = 'subscribed:true';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown recipient_filter ${recipientFilter}`);
|
throw new Error(`Unknown recipient_filter ${recipientFilter}`);
|
||||||
|
|
|
@ -23,12 +23,7 @@ function checkPostAccess(post, member) {
|
||||||
return PERMIT_ACCESS;
|
return PERMIT_ACCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeSubscriptions = member.stripe && member.stripe.subscriptions && member.stripe.subscriptions.filter((subscription) => {
|
if (post.visibility === 'paid' && member.status === 'paid') {
|
||||||
return ['active', 'trialing', 'unpaid', 'past_due'].includes(subscription.status);
|
|
||||||
});
|
|
||||||
const memberHasPlan = activeSubscriptions && activeSubscriptions.length;
|
|
||||||
|
|
||||||
if (post.visibility === 'paid' && memberHasPlan) {
|
|
||||||
return PERMIT_ACCESS;
|
return PERMIT_ACCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
|
||||||
avatar_image: member.avatar_image,
|
avatar_image: member.avatar_image,
|
||||||
subscribed: !!member.subscribed,
|
subscribed: !!member.subscribed,
|
||||||
subscriptions: member.stripe ? member.stripe.subscriptions : [],
|
subscriptions: member.stripe ? member.stripe.subscriptions : [],
|
||||||
paid: member.stripe && member.stripe.subscriptions && member.stripe.subscriptions.filter((subscription) => {
|
paid: member.status === 'paid'
|
||||||
return ['active', 'trialing', 'unpaid', 'past_due'].includes(subscription.status);
|
|
||||||
}).length !== 0
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
"@tryghost/kg-mobiledoc-html-renderer": "3.0.1",
|
"@tryghost/kg-mobiledoc-html-renderer": "3.0.1",
|
||||||
"@tryghost/magic-link": "0.6.4",
|
"@tryghost/magic-link": "0.6.4",
|
||||||
"@tryghost/maintenance": "0.1.0",
|
"@tryghost/maintenance": "0.1.0",
|
||||||
"@tryghost/members-api": "0.37.7",
|
"@tryghost/members-api": "1.0.0-rc.0",
|
||||||
"@tryghost/members-csv": "0.4.2",
|
"@tryghost/members-csv": "0.4.2",
|
||||||
"@tryghost/members-ssr": "0.8.8",
|
"@tryghost/members-ssr": "0.8.8",
|
||||||
"@tryghost/mw-session-from-token": "0.1.14",
|
"@tryghost/mw-session-from-token": "0.1.14",
|
||||||
|
|
|
@ -87,9 +87,9 @@ describe('Members API', function () {
|
||||||
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can browse with paid', async function () {
|
it('Can filter by paid status', async function () {
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(localUtils.API.getApiQuery('members/?paid=true'))
|
.get(localUtils.API.getApiQuery('members/?filter=status:paid'))
|
||||||
.set('Origin', config.get('url'))
|
.set('Origin', config.get('url'))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||||
|
|
|
@ -210,20 +210,6 @@ describe('Member Model', function run() {
|
||||||
describe('findAll', function () {
|
describe('findAll', function () {
|
||||||
beforeEach(testUtils.setup('members'));
|
beforeEach(testUtils.setup('members'));
|
||||||
|
|
||||||
it('can use custom query', function (done) {
|
|
||||||
Member.findAll().then(function (allResult) {
|
|
||||||
allResult.length.should.equal(4);
|
|
||||||
|
|
||||||
return Member.findAll({paid: true});
|
|
||||||
}).then(function (queryResult) {
|
|
||||||
queryResult.length.should.equal(2);
|
|
||||||
queryResult.models[0].get('email').should.equal('paid@test.com');
|
|
||||||
queryResult.models[1].get('email').should.equal('trialing@test.com');
|
|
||||||
|
|
||||||
done();
|
|
||||||
}).catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can use search query', function (done) {
|
it('can use search query', function (done) {
|
||||||
Member.findAll({search: 'egg'}).then(function (queryResult) {
|
Member.findAll({search: 'egg'}).then(function (queryResult) {
|
||||||
queryResult.length.should.equal(1);
|
queryResult.length.should.equal(1);
|
||||||
|
|
|
@ -85,7 +85,7 @@ describe('Unit: canary/utils/serializers/output/utils/post-gating', function ()
|
||||||
attrs.html.should.eql('<p>What\'s the matter?</p>');
|
attrs.html.should.eql('<p>What\'s the matter?</p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide content attributes when visibility is "paid" and member has no subscription', function () {
|
it('should hide content attributes when visibility is "paid" and member has status of "free"', function () {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
visibility: 'paid',
|
visibility: 'paid',
|
||||||
plaintext: 'I see dead people',
|
plaintext: 'I see dead people',
|
||||||
|
@ -97,9 +97,7 @@ describe('Unit: canary/utils/serializers/output/utils/post-gating', function ()
|
||||||
original: {
|
original: {
|
||||||
context: {
|
context: {
|
||||||
member: {
|
member: {
|
||||||
stripe: {
|
status: 'free'
|
||||||
subscriptions: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,35 +109,7 @@ describe('Unit: canary/utils/serializers/output/utils/post-gating', function ()
|
||||||
attrs.html.should.eql('');
|
attrs.html.should.eql('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide content attributes when visibility is "paid" and member has cancelled subscription', function () {
|
it('should NOT hide content attributes when visibility is "paid" and member has status of "paid"', function () {
|
||||||
const attrs = {
|
|
||||||
visibility: 'paid',
|
|
||||||
plaintext: 'I see dead people',
|
|
||||||
html: '<p>What\'s the matter?</p>'
|
|
||||||
};
|
|
||||||
|
|
||||||
const frame = {
|
|
||||||
options: {},
|
|
||||||
original: {
|
|
||||||
context: {
|
|
||||||
member: {
|
|
||||||
stripe: {
|
|
||||||
subscriptions: [{
|
|
||||||
status: 'canceled'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
gating.forPost(attrs, frame);
|
|
||||||
|
|
||||||
attrs.plaintext.should.eql('');
|
|
||||||
attrs.html.should.eql('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT hide content attributes when visibility is "paid" and member has a subscription', function () {
|
|
||||||
const attrs = {
|
const attrs = {
|
||||||
visibility: 'paid',
|
visibility: 'paid',
|
||||||
plaintext: 'Secret paid content',
|
plaintext: 'Secret paid content',
|
||||||
|
@ -151,11 +121,7 @@ describe('Unit: canary/utils/serializers/output/utils/post-gating', function ()
|
||||||
original: {
|
original: {
|
||||||
context: {
|
context: {
|
||||||
member: {
|
member: {
|
||||||
stripe: {
|
status: 'paid'
|
||||||
subscriptions: [{
|
|
||||||
status: 'active'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ describe('Unit: v2/utils/serializers/output/utils/post-gating', function () {
|
||||||
attrs.html.should.eql('<p>What\'s the matter?</p>');
|
attrs.html.should.eql('<p>What\'s the matter?</p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide content attributes when visibility is "paid" and member has no subscription', function () {
|
it('should hide content attributes when visibility is "paid" and member has status of "free"', function () {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
visibility: 'paid',
|
visibility: 'paid',
|
||||||
plaintext: 'I see dead people',
|
plaintext: 'I see dead people',
|
||||||
|
@ -93,9 +93,7 @@ describe('Unit: v2/utils/serializers/output/utils/post-gating', function () {
|
||||||
original: {
|
original: {
|
||||||
context: {
|
context: {
|
||||||
member: {
|
member: {
|
||||||
stripe: {
|
status: 'free'
|
||||||
subscriptions: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,35 +105,7 @@ describe('Unit: v2/utils/serializers/output/utils/post-gating', function () {
|
||||||
attrs.html.should.eql('');
|
attrs.html.should.eql('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide content attributes when visibility is "paid" and member has cancelled subscription', function () {
|
it('should NOT hide content attributes when visibility is "paid" and member has status of "paid"', function () {
|
||||||
const attrs = {
|
|
||||||
visibility: 'paid',
|
|
||||||
plaintext: 'I see dead people',
|
|
||||||
html: '<p>What\'s the matter?</p>'
|
|
||||||
};
|
|
||||||
|
|
||||||
const frame = {
|
|
||||||
options: {},
|
|
||||||
original: {
|
|
||||||
context: {
|
|
||||||
member: {
|
|
||||||
stripe: {
|
|
||||||
subscriptions: [{
|
|
||||||
status: 'canceled'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
gating.forPost(attrs, frame);
|
|
||||||
|
|
||||||
attrs.plaintext.should.eql('');
|
|
||||||
attrs.html.should.eql('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should NOT hide content attributes when visibility is "paid" and member has a subscription', function () {
|
|
||||||
const attrs = {
|
const attrs = {
|
||||||
visibility: 'paid',
|
visibility: 'paid',
|
||||||
plaintext: 'Secret paid content',
|
plaintext: 'Secret paid content',
|
||||||
|
@ -147,11 +117,7 @@ describe('Unit: v2/utils/serializers/output/utils/post-gating', function () {
|
||||||
original: {
|
original: {
|
||||||
context: {
|
context: {
|
||||||
member: {
|
member: {
|
||||||
stripe: {
|
status: 'paid'
|
||||||
subscriptions: [{
|
|
||||||
status: 'active'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,26 +311,30 @@ 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'
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b340',
|
||||||
|
status: 'free'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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'
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b341',
|
||||||
|
status: 'free'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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'
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b342',
|
||||||
|
status: 'paid'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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'
|
uuid: 'f6f91461-d7d8-4a3f-aa5d-8e582c40b343',
|
||||||
|
status: 'paid'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -553,17 +553,17 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
ghost-ignition "^4.4.3"
|
ghost-ignition "^4.4.3"
|
||||||
|
|
||||||
"@tryghost/members-api@0.37.7":
|
"@tryghost/members-api@1.0.0-rc.0":
|
||||||
version "0.37.7"
|
version "1.0.0-rc.0"
|
||||||
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.37.7.tgz#95102f775a0647898cf26919eb543a5bd0aa7778"
|
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-1.0.0-rc.0.tgz#e7769b8e886ea32c975e507268a1677b1af890a2"
|
||||||
integrity sha512-bE/5MO2OPak3OhJK4piz+U9ocnYRTMM/7zBYIOtTDCvGBmCsEUoChUEHNqHHrcE9Iw/cYcdPPqgtlcGkXbCgug==
|
integrity sha512-gHLwgyXl5wXlvNvtRYA6N4Nqod+oWEIa7g62lpZQCUQFeJkSZT1UKwTCCTs6Rd3CPjxo0QVk5dMb3y1vxLc/mg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tryghost/magic-link" "^0.6.6"
|
"@tryghost/magic-link" "^0.6.5"
|
||||||
bluebird "^3.5.4"
|
bluebird "^3.5.4"
|
||||||
body-parser "^1.19.0"
|
body-parser "^1.19.0"
|
||||||
cookies "^0.8.0"
|
cookies "^0.8.0"
|
||||||
express "^4.16.4"
|
express "^4.16.4"
|
||||||
ghost-ignition "4.4.3"
|
ghost-ignition "4.4.2"
|
||||||
got "^9.6.0"
|
got "^9.6.0"
|
||||||
jsonwebtoken "^8.5.1"
|
jsonwebtoken "^8.5.1"
|
||||||
leaky-bucket "2.2.0"
|
leaky-bucket "2.2.0"
|
||||||
|
|
Loading…
Add table
Reference in a new issue