mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Removed GA feature flags (#14915)
refs https://github.com/TryGhost/Team/issues/1616 - Removed all GA feature flags - Removed `tweetGridCard` alpha flag - Changes to `members-api` and `members-importer` packages: https://github.com/TryGhost/Members/compare/%40tryghost/members-api%408.1.1...%40tryghost/members-api%408.1.2
This commit is contained in:
parent
939496487d
commit
ad349bb3a5
15 changed files with 667 additions and 2080 deletions
|
@ -3,12 +3,11 @@
|
||||||
//
|
//
|
||||||
// Returns a string of the tiers with access to the post.
|
// Returns a string of the tiers with access to the post.
|
||||||
// By default, tiers are separated by commas.
|
// By default, tiers are separated by commas.
|
||||||
const {labs} = require('../services/proxy');
|
|
||||||
const {SafeString, escapeExpression} = require('../services/handlebars');
|
const {SafeString, escapeExpression} = require('../services/handlebars');
|
||||||
|
|
||||||
const isString = require('lodash/isString');
|
const isString = require('lodash/isString');
|
||||||
|
|
||||||
function tiers(options = {}) {
|
module.exports = function tiers(options = {}) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.hash = options.hash || {};
|
options.hash = options.hash || {};
|
||||||
|
|
||||||
|
@ -42,18 +41,4 @@ function tiers(options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SafeString(output);
|
return new SafeString(output);
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function productsLabsWrapper() {
|
|
||||||
let self = this;
|
|
||||||
let args = arguments;
|
|
||||||
|
|
||||||
return labs.enabledHelper({
|
|
||||||
flagKey: 'multipleProducts',
|
|
||||||
flagName: 'Tiers',
|
|
||||||
helperName: 'tiers',
|
|
||||||
helpUrl: 'https://ghost.org/docs/themes/'
|
|
||||||
}, () => {
|
|
||||||
return tiers.apply(self, args);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
const debug = require('@tryghost/debug')('services:routing:controllers:unsubscribe');
|
const debug = require('@tryghost/debug')('services:routing:controllers:unsubscribe');
|
||||||
const path = require('path');
|
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
|
||||||
const urlUtils = require('../../../../shared/url-utils');
|
const urlUtils = require('../../../../shared/url-utils');
|
||||||
const megaService = require('../../../../server/services/mega');
|
|
||||||
const renderer = require('../../rendering');
|
|
||||||
const labs = require('../../../../shared/labs');
|
|
||||||
|
|
||||||
module.exports = async function unsubscribeController(req, res) {
|
module.exports = async function unsubscribeController(req, res) {
|
||||||
debug('unsubscribeController');
|
debug('unsubscribeController');
|
||||||
|
|
||||||
if (labs.isSet('multipleNewslettersUI')) {
|
|
||||||
const {query} = url.parse(req.url, true);
|
const {query} = url.parse(req.url, true);
|
||||||
|
|
||||||
if (!query || !query.uuid) {
|
if (!query || !query.uuid) {
|
||||||
|
@ -26,23 +21,4 @@ module.exports = async function unsubscribeController(req, res) {
|
||||||
redirectUrl.searchParams.append('action', 'unsubscribe');
|
redirectUrl.searchParams.append('action', 'unsubscribe');
|
||||||
|
|
||||||
return res.redirect(302, redirectUrl.href);
|
return res.redirect(302, redirectUrl.href);
|
||||||
}
|
|
||||||
|
|
||||||
let data = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
data.member = await megaService.mega.handleUnsubscribeRequest(req);
|
|
||||||
} catch (err) {
|
|
||||||
data.error = err.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
const templateName = 'unsubscribe';
|
|
||||||
|
|
||||||
res.routerOptions = {
|
|
||||||
type: 'custom',
|
|
||||||
templates: templateName,
|
|
||||||
defaultTemplate: path.resolve(__dirname, '../../../views/', templateName)
|
|
||||||
};
|
|
||||||
|
|
||||||
return renderer.renderer(req, res, data);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,6 @@ const url = require('../utils/url');
|
||||||
const utils = require('../../../index');
|
const utils = require('../../../index');
|
||||||
|
|
||||||
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
||||||
const labsService = require('../../../../../../../shared/labs');
|
|
||||||
|
|
||||||
const getPostServiceInstance = require('../../../../../../services/posts/posts-service');
|
const getPostServiceInstance = require('../../../../../../services/posts/posts-service');
|
||||||
const postsService = getPostServiceInstance();
|
const postsService = getPostServiceInstance();
|
||||||
|
@ -35,9 +34,7 @@ module.exports = async (model, frame, options = {}) => {
|
||||||
extraAttrs.forPost(frame, model, jsonModel);
|
extraAttrs.forPost(frame, model, jsonModel);
|
||||||
|
|
||||||
// Attach tiers to custom nql visibility filter
|
// Attach tiers to custom nql visibility filter
|
||||||
if (labsService.isSet('multipleProducts')
|
if (jsonModel.visibility) {
|
||||||
&& jsonModel.visibility
|
|
||||||
) {
|
|
||||||
if (['members', 'public'].includes(jsonModel.visibility) && jsonModel.tiers) {
|
if (['members', 'public'].includes(jsonModel.visibility) && jsonModel.tiers) {
|
||||||
jsonModel.tiers = tiersData || [];
|
jsonModel.tiers = tiersData || [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//@ts-check
|
//@ts-check
|
||||||
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:members');
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:members');
|
||||||
const {unparse} = require('@tryghost/members-csv');
|
const {unparse} = require('@tryghost/members-csv');
|
||||||
const labsService = require('../../../../../../shared/labs');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
browse: createSerializer('browse', paginatedMembers),
|
browse: createSerializer('browse', paginatedMembers),
|
||||||
|
@ -142,7 +141,6 @@ function serializeMember(member, options) {
|
||||||
delete subscription.price.product;
|
delete subscription.price.product;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labsService.isSet('multipleNewsletters')) {
|
|
||||||
if (json.newsletters) {
|
if (json.newsletters) {
|
||||||
serialized.newsletters = json.newsletters
|
serialized.newsletters = json.newsletters
|
||||||
.filter(newsletter => newsletter.status === 'active')
|
.filter(newsletter => newsletter.status === 'active')
|
||||||
|
@ -155,7 +153,6 @@ function serializeMember(member, options) {
|
||||||
if (Array.isArray(serialized.newsletters) && serialized.newsletters.length > 0) {
|
if (Array.isArray(serialized.newsletters) && serialized.newsletters.length > 0) {
|
||||||
serialized.subscribed = true;
|
serialized.subscribed = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return serialized;
|
return serialized;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ const _ = require('lodash');
|
||||||
const Promise = require('bluebird');
|
const Promise = require('bluebird');
|
||||||
const debug = require('@tryghost/debug')('mega');
|
const debug = require('@tryghost/debug')('mega');
|
||||||
const tpl = require('@tryghost/tpl');
|
const tpl = require('@tryghost/tpl');
|
||||||
const url = require('url');
|
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const ObjectID = require('bson-objectid');
|
const ObjectID = require('bson-objectid');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
|
@ -15,7 +14,6 @@ const jobsService = require('../jobs');
|
||||||
const db = require('../../data/db');
|
const db = require('../../data/db');
|
||||||
const models = require('../../models');
|
const models = require('../../models');
|
||||||
const postEmailSerializer = require('./post-email-serializer');
|
const postEmailSerializer = require('./post-email-serializer');
|
||||||
const labs = require('../../../shared/labs');
|
|
||||||
const {getSegmentsFromHtml} = require('./segment-parser');
|
const {getSegmentsFromHtml} = require('./segment-parser');
|
||||||
|
|
||||||
// Used to listen to email.added and email.edited model events originally, I think to offload this - ideally would just use jobs now if possible
|
// Used to listen to email.added and email.edited model events originally, I think to offload this - ideally would just use jobs now if possible
|
||||||
|
@ -267,58 +265,6 @@ const retryFailedEmail = async (emailModel) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* handleUnsubscribeRequest
|
|
||||||
*
|
|
||||||
* Takes a request/response pair and reads the `unsubscribe` query parameter,
|
|
||||||
* using the content to update the members service to set the `subscribed` flag
|
|
||||||
* to false on the member
|
|
||||||
*
|
|
||||||
* If any operation fails, or the request is invalid the function will error - so using
|
|
||||||
* as middleware should consider wrapping with `try/catch`
|
|
||||||
*
|
|
||||||
* @param {Request} req
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async function handleUnsubscribeRequest(req) {
|
|
||||||
if (!req.url) {
|
|
||||||
throw new errors.BadRequestError({
|
|
||||||
message: 'Email address not found.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const {query} = url.parse(req.url, true);
|
|
||||||
if (!query || !query.uuid) {
|
|
||||||
throw new errors.BadRequestError({
|
|
||||||
message: (query.preview ? 'Unsubscribe preview' : 'Email address not found.')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await membersService.api.members.get({
|
|
||||||
uuid: query.uuid
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!member) {
|
|
||||||
throw new errors.BadRequestError({
|
|
||||||
message: 'Email address not found.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let memberData = {subscribed: false};
|
|
||||||
if (labs.isSet('multipleNewsletters')) {
|
|
||||||
memberData.newsletters = [];
|
|
||||||
}
|
|
||||||
const memberModel = await membersService.api.members.update(memberData, {id: member.id});
|
|
||||||
return memberModel.toJSON();
|
|
||||||
} catch (err) {
|
|
||||||
throw new errors.InternalServerError({
|
|
||||||
err,
|
|
||||||
message: 'Failed to unsubscribe this email address'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function pendingEmailHandler(emailModel, options) {
|
async function pendingEmailHandler(emailModel, options) {
|
||||||
// CASE: do not send email if we import a database
|
// CASE: do not send email if we import a database
|
||||||
// TODO: refactor post.published events to never fire on importing
|
// TODO: refactor post.published events to never fire on importing
|
||||||
|
@ -590,7 +536,6 @@ module.exports = {
|
||||||
addEmail,
|
addEmail,
|
||||||
retryFailedEmail,
|
retryFailedEmail,
|
||||||
sendTestEmail,
|
sendTestEmail,
|
||||||
handleUnsubscribeRequest,
|
|
||||||
// NOTE: below are only exposed for testing purposes
|
// NOTE: below are only exposed for testing purposes
|
||||||
_transformEmailRecipientFilter: transformEmailRecipientFilter,
|
_transformEmailRecipientFilter: transformEmailRecipientFilter,
|
||||||
_partitionMembersBySegment: partitionMembersBySegment,
|
_partitionMembersBySegment: partitionMembersBySegment,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const labsService = require('../../../shared/labs');
|
|
||||||
|
|
||||||
function formatNewsletterResponse(newsletters) {
|
function formatNewsletterResponse(newsletters) {
|
||||||
return newsletters.map(({id, name, description, sort_order: sortOrder}) => {
|
return newsletters.map(({id, name, description, sort_order: sortOrder}) => {
|
||||||
return {id, name, description, sort_order: sortOrder};
|
return {id, name, description, sort_order: sortOrder};
|
||||||
|
@ -20,7 +18,7 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
|
||||||
subscriptions: member.subscriptions || [],
|
subscriptions: member.subscriptions || [],
|
||||||
paid: member.status !== 'free'
|
paid: member.status !== 'free'
|
||||||
};
|
};
|
||||||
if (member.newsletters && labsService.isSet('multipleNewsletters')) {
|
if (member.newsletters) {
|
||||||
data.newsletters = formatNewsletterResponse(member.newsletters);
|
data.newsletters = formatNewsletterResponse(member.newsletters);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -14,17 +14,7 @@ const messages = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// flags in this list always return `true`, allows quick global enable prior to full flag removal
|
// flags in this list always return `true`, allows quick global enable prior to full flag removal
|
||||||
const GA_FEATURES = [
|
const GA_FEATURES = [];
|
||||||
'multipleProducts',
|
|
||||||
'tierWelcomePages',
|
|
||||||
'tierName',
|
|
||||||
'selectablePortalLinks',
|
|
||||||
'membersTableStatus',
|
|
||||||
'multipleNewsletters',
|
|
||||||
'multipleNewslettersUI',
|
|
||||||
'membersActivityFeed',
|
|
||||||
'dashboardV5'
|
|
||||||
];
|
|
||||||
|
|
||||||
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
||||||
// input for the "labs" setting value
|
// input for the "labs" setting value
|
||||||
|
@ -34,8 +24,7 @@ const BETA_FEATURES = [
|
||||||
|
|
||||||
const ALPHA_FEATURES = [
|
const ALPHA_FEATURES = [
|
||||||
'urlCache',
|
'urlCache',
|
||||||
'beforeAfterCard',
|
'beforeAfterCard'
|
||||||
'tweetGridCard'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports.GA_KEYS = [...GA_FEATURES];
|
module.exports.GA_KEYS = [...GA_FEATURES];
|
||||||
|
|
|
@ -85,9 +85,9 @@
|
||||||
"@tryghost/logging": "2.1.8",
|
"@tryghost/logging": "2.1.8",
|
||||||
"@tryghost/magic-link": "1.0.26",
|
"@tryghost/magic-link": "1.0.26",
|
||||||
"@tryghost/member-events": "0.4.6",
|
"@tryghost/member-events": "0.4.6",
|
||||||
"@tryghost/members-api": "8.1.1",
|
"@tryghost/members-api": "8.1.2",
|
||||||
"@tryghost/members-events-service": "0.4.3",
|
"@tryghost/members-events-service": "0.4.3",
|
||||||
"@tryghost/members-importer": "0.5.15",
|
"@tryghost/members-importer": "0.5.16",
|
||||||
"@tryghost/members-offers": "0.11.6",
|
"@tryghost/members-offers": "0.11.6",
|
||||||
"@tryghost/members-ssr": "1.0.28",
|
"@tryghost/members-ssr": "1.0.28",
|
||||||
"@tryghost/members-stripe-service": "0.10.5",
|
"@tryghost/members-stripe-service": "0.10.5",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,7 +44,6 @@ describe('Members API', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockManager.mockLabsDisabled('dashboardV5');
|
|
||||||
mockManager.mockMail();
|
mockManager.mockMail();
|
||||||
mockManager.mockStripe();
|
mockManager.mockStripe();
|
||||||
});
|
});
|
||||||
|
@ -190,11 +189,6 @@ describe('Members API', function () {
|
||||||
.expectStatus(200);
|
.expectStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Handling cancelled subscriptions', function () {
|
|
||||||
describe('With the dashboardV5 flag', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
mockManager.mockLabsEnabled('dashboardV5');
|
|
||||||
});
|
|
||||||
it('Handles cancellation of paid subscriptions correctly', async function () {
|
it('Handles cancellation of paid subscriptions correctly', async function () {
|
||||||
const customer_id = createStripeID('cust');
|
const customer_id = createStripeID('cust');
|
||||||
const subscription_id = createStripeID('sub');
|
const subscription_id = createStripeID('sub');
|
||||||
|
@ -315,131 +309,6 @@ describe('Members API', function () {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('Without the dashboardV5 flag', function () {
|
|
||||||
it('Handles cancellation of paid subscriptions correctly', async function () {
|
|
||||||
const customer_id = createStripeID('cust');
|
|
||||||
const subscription_id = createStripeID('sub');
|
|
||||||
|
|
||||||
// Create a new subscription in Stripe
|
|
||||||
set(subscription, {
|
|
||||||
id: subscription_id,
|
|
||||||
customer: customer_id,
|
|
||||||
status: 'active',
|
|
||||||
items: {
|
|
||||||
type: 'list',
|
|
||||||
data: [{
|
|
||||||
id: 'item_123',
|
|
||||||
price: {
|
|
||||||
id: 'price_123',
|
|
||||||
product: 'product_123',
|
|
||||||
active: true,
|
|
||||||
nickname: 'Monthly',
|
|
||||||
currency: 'USD',
|
|
||||||
recurring: {
|
|
||||||
interval: 'month'
|
|
||||||
},
|
|
||||||
unit_amount: 500,
|
|
||||||
type: 'recurring'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
start_date: Date.now() / 1000,
|
|
||||||
current_period_end: Date.now() / 1000 + (60 * 60 * 24 * 31),
|
|
||||||
cancel_at_period_end: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new customer in Stripe
|
|
||||||
set(customer, {
|
|
||||||
id: customer_id,
|
|
||||||
name: 'Test Member',
|
|
||||||
email: 'cancelled-paid-test-no-flag@email.com',
|
|
||||||
subscriptions: {
|
|
||||||
type: 'list',
|
|
||||||
data: [subscription]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure this customer has a corresponding member in the database
|
|
||||||
// And all the subscriptions are setup correctly
|
|
||||||
const initialMember = await createMemberFromStripe();
|
|
||||||
assert.equal(initialMember.status, 'paid', 'The member initial status should be paid');
|
|
||||||
assert.equal(initialMember.tiers.length, 1, 'The member should have one tier');
|
|
||||||
should(initialMember.subscriptions).match([
|
|
||||||
{
|
|
||||||
status: 'active'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Cancel the previously created subscription in Stripe
|
|
||||||
set(subscription, {
|
|
||||||
...subscription,
|
|
||||||
cancel_at_period_end: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send the webhook call to anounce the cancelation
|
|
||||||
const webhookPayload = JSON.stringify({
|
|
||||||
type: 'customer.subscription.updated',
|
|
||||||
data: {
|
|
||||||
object: subscription
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const webhookSignature = stripe.webhooks.generateTestHeaderString({
|
|
||||||
payload: webhookPayload,
|
|
||||||
secret: process.env.WEBHOOK_SECRET
|
|
||||||
});
|
|
||||||
|
|
||||||
await membersAgent.post('/webhooks/stripe/')
|
|
||||||
.body(webhookPayload)
|
|
||||||
.header('stripe-signature', webhookSignature)
|
|
||||||
.expectStatus(200);
|
|
||||||
|
|
||||||
// Check status has been updated to 'free' after cancelling
|
|
||||||
const {body: body2} = await adminAgent.get('/members/' + initialMember.id + '/');
|
|
||||||
assert.equal(body2.members.length, 1, 'The member does not exist');
|
|
||||||
const updatedMember = body2.members[0];
|
|
||||||
assert.equal(updatedMember.status, 'paid');
|
|
||||||
assert.equal(updatedMember.tiers.length, 1, 'The member should have products');
|
|
||||||
should(updatedMember.subscriptions).match([
|
|
||||||
{
|
|
||||||
cancel_at_period_end: true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Check the status events for this newly created member (should be NULL -> paid only)
|
|
||||||
await assertMemberEvents({
|
|
||||||
eventType: 'MemberStatusEvent',
|
|
||||||
memberId: updatedMember.id,
|
|
||||||
asserts: [
|
|
||||||
{
|
|
||||||
from_status: null,
|
|
||||||
to_status: 'free'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
from_status: 'free',
|
|
||||||
to_status: 'paid'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
await assertMemberEvents({
|
|
||||||
eventType: 'MemberPaidSubscriptionEvent',
|
|
||||||
memberId: updatedMember.id,
|
|
||||||
asserts: [
|
|
||||||
{
|
|
||||||
type: 'created',
|
|
||||||
mrr_delta: 500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'canceled',
|
|
||||||
mrr_delta: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Handling the end of subscriptions', function () {
|
describe('Handling the end of subscriptions', function () {
|
||||||
let canceledPaidMember;
|
let canceledPaidMember;
|
||||||
|
@ -1281,12 +1150,9 @@ describe('Members API', function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('With the dashboardV5 flag', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
mockManager.mockLabsEnabled('dashboardV5');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Correctly includes monthly forever percentage discounts in MRR', async function () {
|
it('Correctly includes monthly forever percentage discounts in MRR', async function () {
|
||||||
|
// Do you get a offer_id is null failed test here
|
||||||
|
// -> check if members-api and members-offers package versions are in sync in yarn.lock or if both are linked in dev
|
||||||
const discount = {
|
const discount = {
|
||||||
id: 'di_1Knkn7HUEDadPGIBPOQgmzIX',
|
id: 'di_1Knkn7HUEDadPGIBPOQgmzIX',
|
||||||
object: 'discount',
|
object: 'discount',
|
||||||
|
@ -1808,84 +1674,5 @@ describe('Members API', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Without the dashboardV5 flag', function () {
|
|
||||||
it('Does not include forever percentage discounts in MRR', async function () {
|
|
||||||
const discount = {
|
|
||||||
id: 'di_1Knkn7HUEDadPGIBPOQgmzIX',
|
|
||||||
object: 'discount',
|
|
||||||
checkout_session: null,
|
|
||||||
coupon: {
|
|
||||||
id: couponId,
|
|
||||||
object: 'coupon',
|
|
||||||
amount_off: null,
|
|
||||||
created: 1649774041,
|
|
||||||
currency: 'eur',
|
|
||||||
duration: 'forever',
|
|
||||||
duration_in_months: null,
|
|
||||||
livemode: false,
|
|
||||||
max_redemptions: null,
|
|
||||||
metadata: {},
|
|
||||||
name: '50% off',
|
|
||||||
percent_off: 50,
|
|
||||||
redeem_by: null,
|
|
||||||
times_redeemed: 0,
|
|
||||||
valid: true
|
|
||||||
},
|
|
||||||
end: null,
|
|
||||||
invoice: null,
|
|
||||||
invoice_item: null,
|
|
||||||
promotion_code: null,
|
|
||||||
start: beforeNow / 1000,
|
|
||||||
subscription: null
|
|
||||||
};
|
|
||||||
await testDiscount({
|
|
||||||
discount,
|
|
||||||
unit_amount: 500,
|
|
||||||
interval: 'month',
|
|
||||||
assert_mrr: 500,
|
|
||||||
offer_id: offer.id
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Does not include forever amount off discounts in MRR', async function () {
|
|
||||||
const discount = {
|
|
||||||
id: 'di_1Knkn7HUEDadPGIBPOQgmzIX',
|
|
||||||
object: 'discount',
|
|
||||||
checkout_session: null,
|
|
||||||
coupon: {
|
|
||||||
id: couponId,
|
|
||||||
object: 'coupon',
|
|
||||||
amount_off: 1,
|
|
||||||
created: 1649774041,
|
|
||||||
currency: 'eur',
|
|
||||||
duration: 'forever',
|
|
||||||
duration_in_months: null,
|
|
||||||
livemode: false,
|
|
||||||
max_redemptions: null,
|
|
||||||
metadata: {},
|
|
||||||
name: '1 cent off',
|
|
||||||
percent_off: null,
|
|
||||||
redeem_by: null,
|
|
||||||
times_redeemed: 0,
|
|
||||||
valid: true
|
|
||||||
},
|
|
||||||
end: null,
|
|
||||||
invoice: null,
|
|
||||||
invoice_item: null,
|
|
||||||
promotion_code: null,
|
|
||||||
start: beforeNow / 1000,
|
|
||||||
subscription: null
|
|
||||||
};
|
|
||||||
await testDiscount({
|
|
||||||
discount,
|
|
||||||
unit_amount: 500,
|
|
||||||
interval: 'month',
|
|
||||||
assert_mrr: 500,
|
|
||||||
offer_id: offer.id
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -192,10 +192,6 @@ describe('Front-end members behaviour', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Unsubscribe', function () {
|
describe('Unsubscribe', function () {
|
||||||
beforeEach(function () {
|
|
||||||
mockManager.mockLabsEnabled('multipleNewslettersUI');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
mockManager.restore();
|
mockManager.restore();
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('Unit: canary/utils/serializers/output/members', function () {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('browse: includes newsletter data when flag is enabled', function () {
|
it('browse: includes newsletter data', function () {
|
||||||
const apiConfig = {docName: 'members'};
|
const apiConfig = {docName: 'members'};
|
||||||
const frame = {
|
const frame = {
|
||||||
options: {
|
options: {
|
||||||
|
@ -51,24 +51,7 @@ describe('Unit: canary/utils/serializers/output/members', function () {
|
||||||
should.exist(frame.response.members[0].tiers);
|
should.exist(frame.response.members[0].tiers);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('browse: removes newsletter data when flag is disabled', function () {
|
it('read: includes newsletter data', function () {
|
||||||
labsStub.returns(false);
|
|
||||||
const apiConfig = {docName: 'members'};
|
|
||||||
const frame = {
|
|
||||||
options: {
|
|
||||||
context: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ctrlResponse = memberModel(testUtils.DataGenerator.forKnex.createMemberWithNewsletter());
|
|
||||||
memberSerializer.browse({
|
|
||||||
data: [ctrlResponse],
|
|
||||||
meta: null
|
|
||||||
}, apiConfig, frame);
|
|
||||||
should.not.exist(frame.response.members[0].newsletters);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('read: includes newsletter data when flag is enabled', function () {
|
|
||||||
const apiConfig = {docName: 'members'};
|
const apiConfig = {docName: 'members'};
|
||||||
const frame = {
|
const frame = {
|
||||||
options: {
|
options: {
|
||||||
|
@ -81,20 +64,6 @@ describe('Unit: canary/utils/serializers/output/members', function () {
|
||||||
should.exist(frame.response.members[0].newsletters);
|
should.exist(frame.response.members[0].newsletters);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('read: removes newsletter data when flag is disabled', function () {
|
|
||||||
labsStub.returns(false);
|
|
||||||
const apiConfig = {docName: 'members'};
|
|
||||||
const frame = {
|
|
||||||
options: {
|
|
||||||
context: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ctrlResponse = memberModel(testUtils.DataGenerator.forKnex.createMemberWithNewsletter());
|
|
||||||
memberSerializer.read(ctrlResponse, apiConfig, frame);
|
|
||||||
should.not.exist(frame.response.members[0].newsletters);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('read: includes tiers data', function () {
|
it('read: includes tiers data', function () {
|
||||||
const apiConfig = {docName: 'members'};
|
const apiConfig = {docName: 'members'};
|
||||||
const frame = {
|
const frame = {
|
||||||
|
|
|
@ -3,7 +3,7 @@ const sinon = require('sinon');
|
||||||
const errors = require('@tryghost/errors');
|
const errors = require('@tryghost/errors');
|
||||||
const labs = require('../../../../../core/shared/labs');
|
const labs = require('../../../../../core/shared/labs');
|
||||||
|
|
||||||
const {addEmail, _partitionMembersBySegment, _getEmailMemberRows, _transformEmailRecipientFilter, handleUnsubscribeRequest, _getFromAddress, _getReplyToAddress} = require('../../../../../core/server/services/mega/mega');
|
const {addEmail, _partitionMembersBySegment, _getEmailMemberRows, _transformEmailRecipientFilter, _getFromAddress, _getReplyToAddress} = require('../../../../../core/server/services/mega/mega');
|
||||||
const membersService = require('../../../../../core/server/services/members');
|
const membersService = require('../../../../../core/server/services/members');
|
||||||
|
|
||||||
describe('MEGA', function () {
|
describe('MEGA', function () {
|
||||||
|
@ -100,44 +100,6 @@ describe('MEGA', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handleUnsubscribeRequest', function () {
|
|
||||||
const updateStub = sinon.stub();
|
|
||||||
beforeEach(function () {
|
|
||||||
updateStub.returns({
|
|
||||||
toJSON: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
sinon.stub(membersService, 'api').get(() => {
|
|
||||||
return {
|
|
||||||
members: {
|
|
||||||
get: () => {
|
|
||||||
return {
|
|
||||||
id: 'id-1',
|
|
||||||
name: 'Jamie'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
update: updateStub
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unsubscribes from all newsletters', async function () {
|
|
||||||
sinon.stub(labs, 'isSet').withArgs('multipleNewsletters').returns(true);
|
|
||||||
const req = {
|
|
||||||
url: 'https://example.com?uuid=abc'
|
|
||||||
};
|
|
||||||
await handleUnsubscribeRequest(req);
|
|
||||||
updateStub.calledWith({
|
|
||||||
subscribed: false,
|
|
||||||
newsletters: []
|
|
||||||
}, {
|
|
||||||
id: 'id-1'
|
|
||||||
}).should.be.true();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getEmailMemberRows', function () {
|
describe('getEmailMemberRows', function () {
|
||||||
it('getEmailMemberRows throws when "none" is used as a recipient_filter', async function () {
|
it('getEmailMemberRows throws when "none" is used as a recipient_filter', async function () {
|
||||||
const newsletterGetter = sinon.stub();
|
const newsletterGetter = sinon.stub();
|
||||||
|
|
|
@ -71,36 +71,5 @@ describe('Members Service - utils', function () {
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes newsletter data if flag is disabled', async function () {
|
|
||||||
labsStub.returns(false);
|
|
||||||
const member1 = formattedMemberResponse({
|
|
||||||
uuid: 'uuid-1',
|
|
||||||
email: 'jamie+1@example.com',
|
|
||||||
name: 'Jamie Larson',
|
|
||||||
avatar_image: 'https://gravatar.com/avatar/7d8efd2c2a781111599a8cae293cf704?s=250&d=blank',
|
|
||||||
subscribed: true,
|
|
||||||
status: 'paid',
|
|
||||||
extra: 'property',
|
|
||||||
newsletters: [{
|
|
||||||
id: 'newsletter-1',
|
|
||||||
name: 'Daily brief',
|
|
||||||
description: 'One email daily',
|
|
||||||
sender_name: 'Jamie',
|
|
||||||
sender_email: 'jamie@example.com',
|
|
||||||
sort_order: 0
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
should(member1).deepEqual({
|
|
||||||
uuid: 'uuid-1',
|
|
||||||
email: 'jamie+1@example.com',
|
|
||||||
name: 'Jamie Larson',
|
|
||||||
firstname: 'Jamie',
|
|
||||||
avatar_image: 'https://gravatar.com/avatar/7d8efd2c2a781111599a8cae293cf704?s=250&d=blank',
|
|
||||||
subscribed: true,
|
|
||||||
subscriptions: [],
|
|
||||||
paid: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -1862,10 +1862,10 @@
|
||||||
"@tryghost/domain-events" "^0.1.14"
|
"@tryghost/domain-events" "^0.1.14"
|
||||||
"@tryghost/member-events" "^0.4.6"
|
"@tryghost/member-events" "^0.4.6"
|
||||||
|
|
||||||
"@tryghost/members-api@8.1.1":
|
"@tryghost/members-api@8.1.2":
|
||||||
version "8.1.1"
|
version "8.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-8.1.1.tgz#9078ea61717a0c4dab1cff1ef0a0ae505389731c"
|
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-8.1.2.tgz#dd4191ad7cbf0e6687c69153b06b8b5f9ef4709e"
|
||||||
integrity sha512-gnNmK9Bw8xpdWm/KsXmYKXdE6h52pzI+QOYMTrCHvKkdOk643OBYwkDmssXfSZAxO31offkE0j/q3xTIstlTUw==
|
integrity sha512-cD1NrGgPJQfaZBkW0GfTJRq5pcOABu13Tf/BFa4koFi03JUbK3QM92mZPmboks2cpDzkku6EzwFS4D7nbgcS5Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nexes/nql" "^0.6.0"
|
"@nexes/nql" "^0.6.0"
|
||||||
"@tryghost/debug" "^0.1.2"
|
"@tryghost/debug" "^0.1.2"
|
||||||
|
@ -1910,10 +1910,10 @@
|
||||||
"@tryghost/member-events" "^0.4.6"
|
"@tryghost/member-events" "^0.4.6"
|
||||||
moment-timezone "^0.5.34"
|
moment-timezone "^0.5.34"
|
||||||
|
|
||||||
"@tryghost/members-importer@0.5.15":
|
"@tryghost/members-importer@0.5.16":
|
||||||
version "0.5.15"
|
version "0.5.16"
|
||||||
resolved "https://registry.yarnpkg.com/@tryghost/members-importer/-/members-importer-0.5.15.tgz#a28d4c14d0f43608b9363892faf442b4afb49e5f"
|
resolved "https://registry.yarnpkg.com/@tryghost/members-importer/-/members-importer-0.5.16.tgz#d39f25d88515c9c386b3b9c574034b533aa21d96"
|
||||||
integrity sha512-rHig0CUBRAtInn/iXHaiR4QTGvnOMvRh6UmZ9oGn4oxUMuYo8wRhA1xyKOhgxOx/rFFIT2i27d21/m0QzXX1zQ==
|
integrity sha512-1D+9s9lWg72gc40p4IrxxcNpWjU5dClqnv9P7CJwKk5WUJV28WlyBwZuuqn0r0S+igjVyX3eKLKkp8W1tinulw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@tryghost/errors" "^1.0.0"
|
"@tryghost/errors" "^1.0.0"
|
||||||
"@tryghost/members-csv" "^1.2.16"
|
"@tryghost/members-csv" "^1.2.16"
|
||||||
|
|
Loading…
Add table
Reference in a new issue