From 5aadf1cf00404e00da7dcc8071e06c1cabe15797 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Wed, 2 Sep 2020 00:34:26 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20members=20with=20Str?= =?UTF-8?q?ipe=20trials=20not=20receiving=20paid-members=20newsletters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no issue - members who have trial subscriptions added directly via Stripe will have a status of `"trialed"` in their Ghost subscription - the `paid: true` filter was not taking that into account meaning trial users were not receiving newsletters sent to paid members even though they have a "paid" subscription --- core/server/models/member.js | 4 +- test/api-acceptance/admin/members_spec.js | 11 +++--- .../api/canary/admin/members_spec.js | 6 +-- test/regression/models/model_members_spec.js | 5 ++- test/utils/fixtures/data-generator.js | 37 +++++++++++++++++-- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/core/server/models/member.js b/core/server/models/member.js index 6650c16f9f..223e49cbfe 100644 --- a/core/server/models/member.js +++ b/core/server/models/member.js @@ -209,9 +209,9 @@ const Member = ghostBookshelf.Model.extend({ this.on( 'members_stripe_customers.customer_id', 'members_stripe_customers_subscriptions.customer_id' - ).andOn( + ).onIn( 'members_stripe_customers_subscriptions.status', - ghostBookshelf.knex.raw('?', ['active']) + ['active', 'trialed'] ); } ); diff --git a/test/api-acceptance/admin/members_spec.js b/test/api-acceptance/admin/members_spec.js index 76d0edb8ea..b6788097b0 100644 --- a/test/api-acceptance/admin/members_spec.js +++ b/test/api-acceptance/admin/members_spec.js @@ -42,7 +42,7 @@ describe('Members API', function () { const jsonResponse = res.body; should.exist(jsonResponse); should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(3); + jsonResponse.members.should.have.length(4); localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe'); testUtils.API.isISO8601(jsonResponse.members[0].created_at).should.be.true(); @@ -51,7 +51,7 @@ describe('Members API', function () { jsonResponse.meta.pagination.should.have.property('page', 1); jsonResponse.meta.pagination.should.have.property('limit', 15); jsonResponse.meta.pagination.should.have.property('pages', 1); - jsonResponse.meta.pagination.should.have.property('total', 3); + jsonResponse.meta.pagination.should.have.property('total', 4); jsonResponse.meta.pagination.should.have.property('next', null); jsonResponse.meta.pagination.should.have.property('prev', null); }); @@ -108,8 +108,9 @@ describe('Members API', function () { const jsonResponse = res.body; should.exist(jsonResponse); should.exist(jsonResponse.members); - jsonResponse.members.should.have.length(1); + jsonResponse.members.should.have.length(2); jsonResponse.members[0].email.should.equal('paid@test.com'); + jsonResponse.members[1].email.should.equal('trialed@test.com'); localUtils.API.checkResponse(jsonResponse, 'members'); localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe'); localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); @@ -412,8 +413,8 @@ describe('Members API', function () { should.exist(jsonResponse.total_on_date); should.exist(jsonResponse.new_today); - // 3 from fixtures, 2 from above posts, 2 from above import - jsonResponse.total.should.equal(7); + // 4 from fixtures, 2 from above posts, 2 from above import + jsonResponse.total.should.equal(8); }); }); }); diff --git a/test/regression/api/canary/admin/members_spec.js b/test/regression/api/canary/admin/members_spec.js index 0671d4a690..781baecefa 100644 --- a/test/regression/api/canary/admin/members_spec.js +++ b/test/regression/api/canary/admin/members_spec.js @@ -542,7 +542,7 @@ describe('Members API', function () { should.exist(jsonResponse.new_today); // 3 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(9); + jsonResponse.total.should.equal(10); }); }); @@ -566,7 +566,7 @@ describe('Members API', function () { should.exist(jsonResponse.new_today); // 3 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(9); + jsonResponse.total.should.equal(10); }); }); @@ -590,7 +590,7 @@ describe('Members API', function () { should.exist(jsonResponse.new_today); // 3 from fixtures and 6 imported in previous tests - jsonResponse.total.should.equal(9); + jsonResponse.total.should.equal(10); }); }); diff --git a/test/regression/models/model_members_spec.js b/test/regression/models/model_members_spec.js index 63423a4085..41fb7943d8 100644 --- a/test/regression/models/model_members_spec.js +++ b/test/regression/models/model_members_spec.js @@ -212,12 +212,13 @@ describe('Member Model', function run() { it('can use custom query', function (done) { Member.findAll().then(function (allResult) { - allResult.length.should.equal(3); + allResult.length.should.equal(4); return Member.findAll({paid: true}); }).then(function (queryResult) { - queryResult.length.should.equal(1); + queryResult.length.should.equal(2); queryResult.models[0].get('email').should.equal('paid@test.com'); + queryResult.models[1].get('email').should.equal('trialed@test.com'); done(); }).catch(done); diff --git a/test/utils/fixtures/data-generator.js b/test/utils/fixtures/data-generator.js index 5c8f7da3ec..8afd3c8224 100644 --- a/test/utils/fixtures/data-generator.js +++ b/test/utils/fixtures/data-generator.js @@ -321,6 +321,11 @@ DataGenerator.Content = { id: ObjectId.generate(), email: 'paid@test.com', name: 'Egon Spengler' + }, + { + id: ObjectId.generate(), + email: 'trialed@test.com', + name: 'Ray Stantz' } ], @@ -344,6 +349,13 @@ DataGenerator.Content = { customer_id: 'cus_HR3tBmNhx4QsZY', name: 'Egon Spengler', email: 'paid@test.com' + }, + { + id: ObjectId.generate(), + member_id: null, // relation added later + customer_id: 'cus_HR3tBmNhx4QsZZ', + name: 'Ray Stantz', + email: 'trialed@test.com' } ], @@ -362,6 +374,21 @@ DataGenerator.Content = { plan_interval: 'month', plan_amount: '1000', plan_currency: 'usd' + }, + { + id: ObjectId.generate(), + customer_id: 'cus_HR3tBmNhx4QsZZ', + subscription_id: 'sub_HR3tLNgGAHsa7c', + plan_id: '173e16a1fffa7d232b398e4a9b08d266a456ae8f3d23e5f11cc608ced6730bb9', + status: 'trialed', + cancel_at_period_end: true, + current_period_end: '2025-07-09 19:01:20', + start_date: '2020-06-09 19:01:20', + default_payment_card_last4: '4242', + plan_nickname: 'Monthly', + plan_interval: 'month', + plan_amount: '1000', + plan_currency: 'usd' } ], @@ -445,6 +472,7 @@ DataGenerator.Content.api_keys[1].integration_id = DataGenerator.Content.integra DataGenerator.Content.emails[0].post_id = DataGenerator.Content.posts[0].id; DataGenerator.Content.emails[1].post_id = DataGenerator.Content.posts[1].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.forKnex = (function () { function createBasic(overrides) { @@ -910,7 +938,8 @@ DataGenerator.forKnex = (function () { const members = [ createMember(DataGenerator.Content.members[0]), createMember(DataGenerator.Content.members[1]), - createMember(DataGenerator.Content.members[2]) + createMember(DataGenerator.Content.members[2]), + createMember(DataGenerator.Content.members[3]) ]; const labels = [ @@ -925,11 +954,13 @@ DataGenerator.forKnex = (function () { ]; const members_stripe_customers = [ - createBasic(DataGenerator.Content.members_stripe_customers[0]) + createBasic(DataGenerator.Content.members_stripe_customers[0]), + createBasic(DataGenerator.Content.members_stripe_customers[1]) ]; const stripe_customer_subscriptions = [ - createBasic(DataGenerator.Content.members_stripe_customers_subscriptions[0]) + createBasic(DataGenerator.Content.members_stripe_customers_subscriptions[0]), + createBasic(DataGenerator.Content.members_stripe_customers_subscriptions[1]) ]; return { From 7a2b7582f1556f8d14e322bf2ae544c4ece55b76 Mon Sep 17 00:00:00 2001 From: Rish Date: Wed, 2 Sep 2020 12:02:34 +0530 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20missing=20member=20e?= =?UTF-8?q?mail=20on=20unsubscribe=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no refs - The `update` method in members-api package was edited to return Model object instead of JSON directly - TryGhost/Members@a28bcc5 - This unsubscribe handler was returning the raw member object returned from `update` method, which is now a model object and not able to access `member.email` - Fix updates the unsubscribe request handler to return the member JSON again --- core/server/services/mega/mega.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/server/services/mega/mega.js b/core/server/services/mega/mega.js index ba6b10a621..edaaa61194 100644 --- a/core/server/services/mega/mega.js +++ b/core/server/services/mega/mega.js @@ -177,7 +177,8 @@ async function handleUnsubscribeRequest(req) { } try { - return await membersService.api.members.update({subscribed: false}, {id: member.id}); + const memberModel = await membersService.api.members.update({subscribed: false}, {id: member.id}); + return memberModel.toJSON(); } catch (err) { throw new errors.InternalServerError({ message: 'Failed to unsubscribe member' From 1b01f166f06ebf8f742517d4597441bcd8cff429 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 2 Sep 2020 12:27:08 +0100 Subject: [PATCH 3/4] Updated Ghost-Admin to v3.31.4 --- core/client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/client b/core/client index 750264da7c..a9804ebd8a 160000 --- a/core/client +++ b/core/client @@ -1 +1 @@ -Subproject commit 750264da7c17938b65fad363d74c16560baa20df +Subproject commit a9804ebd8a651b74d08aeaca0ac348ddf1bf6202 From 4316bc5c74701b6509e1bee40c7d0821e4dc4f4e Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 2 Sep 2020 12:27:09 +0100 Subject: [PATCH 4/4] v3.31.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6b1ab80b0..3322489c89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "3.31.3", + "version": "3.31.4", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org",