0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Merged v5.12.3 into main

v5.12.3
This commit is contained in:
Daniel Lockyer 2022-09-01 15:36:46 +01:00
commit df99e1aec3
No known key found for this signature in database
GPG key ID: D21186F0B47295AD
4 changed files with 226 additions and 8 deletions

View file

@ -1,6 +1,6 @@
{
"name": "ghost-admin",
"version": "5.12.2",
"version": "5.12.3",
"description": "Ember.js admin client for Ghost",
"author": "Ghost Foundation",
"homepage": "http://ghost.org",

View file

@ -1,6 +1,6 @@
{
"name": "ghost",
"version": "5.12.2",
"version": "5.12.3",
"description": "The professional publishing platform",
"author": "Ghost Foundation",
"homepage": "https://ghost.org",

View file

@ -407,7 +407,7 @@ module.exports = class MemberRepository {
productsToAdd = _.differenceWith(incomingProductIds, existingProductIds);
productsToRemove = _.differenceWith(existingProductIds, incomingProductIds);
const productsToModify = productsToAdd.concat(productsToRemove);
if (productsToModify.length !== 0) {
// Load active subscriptions information
await initialMember.load(
@ -417,9 +417,9 @@ module.exports = class MemberRepository {
'stripeSubscriptions.stripePrice.stripeProduct',
'stripeSubscriptions.stripePrice.stripeProduct.product'
], sharedOptions);
const exisitingSubscriptions = initialMember.related('stripeSubscriptions')?.models ?? [];
if (productsToRemove.length > 0) {
// Only allow to delete comped products without a subscription attached to them
// Other products should be removed by canceling them via the related stripe subscription
@ -445,7 +445,7 @@ module.exports = class MemberRepository {
const existingActiveSubscriptions = exisitingSubscriptions.filter((subscription) => {
return this.isActiveSubscriptionStatus(subscription.get('status'));
});
if (existingActiveSubscriptions.length) {
throw new errors.BadRequestError({message: tpl(messages.addProductWithActiveSubscription)});
}
@ -964,8 +964,11 @@ module.exports = class MemberRepository {
...eventData
}, options);
const context = options?.context || {};
const source = this._resolveContextSource(context);
// Notify paid member subscription start
if (this._labsService.isSet('emailAlerts')) {
if (this._labsService.isSet('emailAlerts') && ['member', 'api'].includes(source)) {
await this.staffService.notifyPaidSubscriptionStart({
member: member.toJSON(),
offer: offer ? this._offerRepository.toJSON(offer) : null,

View file

@ -1,4 +1,5 @@
const assert = require('assert');
const sinon = require('sinon');
const MemberRepository = require('../../../../lib/repositories/member');
describe('MemberRepository', function () {
@ -33,7 +34,7 @@ describe('MemberRepository', function () {
api_key: true
});
assert.equal(source, 'api');
source = repo._resolveContextSource({
api_key: true
});
@ -49,4 +50,218 @@ describe('MemberRepository', function () {
assert.equal(source, 'member');
});
});
describe('linkSubscription', function (){
let Member;
let staffService;
let notifySpy;
let MemberPaidSubscriptionEvent;
let StripeCustomerSubscription;
let MemberProductEvent;
let stripeAPIService;
let productRepository;
let labsService;
let subscriptionData;
beforeEach(async function () {
notifySpy = sinon.spy();
subscriptionData = {
id: 'sub_123',
customer: 'cus_123',
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
};
Member = {
findOne: sinon.stub().resolves({
related: () => {
return {
query: sinon.stub().returns({
fetchOne: sinon.stub().resolves({})
}),
toJSON: sinon.stub().returns([]),
fetch: sinon.stub().resolves({
toJSON: sinon.stub().returns({})
})
};
},
toJSON: sinon.stub().returns({})
}),
edit: sinon.stub().resolves({
attributes: {},
_previousAttributes: {}
})
};
staffService = {
notifyPaidSubscriptionStart: notifySpy
};
MemberPaidSubscriptionEvent = {
add: sinon.stub().resolves()
};
StripeCustomerSubscription = {
add: sinon.stub().resolves({
get: sinon.stub().returns()
})
};
MemberProductEvent = {
add: sinon.stub().resolves({})
};
stripeAPIService = {
configured: true,
getSubscription: sinon.stub().resolves(subscriptionData)
};
productRepository = {
get: sinon.stub().resolves({
get: sinon.stub().returns(),
toJSON: sinon.stub().returns({})
}),
update: sinon.stub().resolves({})
};
labsService = {
isSet: sinon.stub().returns(true)
};
});
it('triggers email alert for member context', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
staffService,
productRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
await repo.linkSubscription({
subscription: subscriptionData
}, {
transacting: true,
context: {}
});
notifySpy.calledOnce.should.be.true();
});
it('triggers email alert for api context', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
staffService,
productRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
await repo.linkSubscription({
subscription: subscriptionData
}, {
transacting: true,
context: {api_key: 'abc'}
});
notifySpy.calledOnce.should.be.true();
});
it('does not trigger email alert for importer context', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
staffService,
productRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
await repo.linkSubscription({
subscription: subscriptionData
}, {
transacting: true,
context: {importer: true}
});
notifySpy.calledOnce.should.be.false();
});
it('does not trigger email alert for admin context', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
staffService,
productRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
await repo.linkSubscription({
subscription: subscriptionData
}, {
transacting: true,
context: {user: {}}
});
notifySpy.calledOnce.should.be.false();
});
it('does not trigger email alert for internal context', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
staffService,
productRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
await repo.linkSubscription({
subscription: subscriptionData
}, {
transacting: true,
context: {internal: true}
});
notifySpy.calledOnce.should.be.false();
});
afterEach(function () {
sinon.restore();
});
});
});