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

Updated members API to return subscriptions' offers from the offer_id (#14515)

refs https://github.com/TryGhost/Team/issues/1520

**Changes in members repo**

Bumped to `5.12.0`, with the following changes:

- Compare differences via https://github.com/TryGhost/Members/compare/%40tryghost/members-api%405.11.1...%40tryghost/members-api%405.12.0
- Instead of doing the matching of the offers and subscriptions by looking at the offer redemptions, we can now look at the offer_id from subscriptions.
- This also fixes an issue where we don't attach the offer object to subscriptions in the members' browse method
- Updated browse behaviour to match the read behaviour of members (product relation needs to get loaded because it is missing in member.products if the subscription is expired).

**Tests**
- Includes test to see if the API correctly returns the offer object when fetching one or multiple members
- Check if the return format is the same for the read, edit and browse members admin API endpoints (offer was missing in subscriptions)
- Snapshot files have been updated because now the offer is returned in subscriptions (content length increased)
This commit is contained in:
Simon Backx 2022-04-21 12:06:06 +02:00 committed by GitHub
parent 9def4e6edc
commit b9646cceb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 11 deletions

View file

@ -83,7 +83,7 @@
"@tryghost/logging": "2.1.5",
"@tryghost/magic-link": "1.0.21",
"@tryghost/member-events": "0.4.1",
"@tryghost/members-api": "5.11.1",
"@tryghost/members-api": "5.12.0",
"@tryghost/members-events-service": "0.3.3",
"@tryghost/members-importer": "0.5.8",
"@tryghost/members-offers": "0.11.1",

View file

@ -537,7 +537,7 @@ exports[`Members API Can browse 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "8299",
"content-length": "8426",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
@ -1126,7 +1126,7 @@ exports[`Members API Can filter by paid status 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "6764",
"content-length": "6891",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
@ -1304,7 +1304,7 @@ exports[`Members API Can ignore any unknown includes 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "6764",
"content-length": "6891",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
@ -1967,7 +1967,7 @@ exports[`Members API Search for paid members retrieves member with email paid@te
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1682",
"content-length": "1720",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
@ -2409,7 +2409,7 @@ exports[`Members API: with multiple newsletters Can browse 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "8435",
"content-length": "8562",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",

View file

@ -847,6 +847,8 @@ describe('Members API', function () {
});
});
let memberWithPaidSubscription;
it('Can create a member with an existing paid subscription', async function () {
const fakePrice = {
id: 'price_1',
@ -934,6 +936,7 @@ describe('Members API', function () {
});
const newMember = body.members[0];
assert.equal(newMember.status, 'paid', 'The created member should have the paid status');
assert.equal(newMember.subscriptions.length, 1, 'The member should have a single subscription');
assert.equal(newMember.subscriptions[0].id, fakeSubscription.id, 'The returned subscription should have an ID assigned');
@ -980,6 +983,30 @@ describe('Members API', function () {
plan_currency: 'usd',
mrr: 100
});
// Save this member for the next test
memberWithPaidSubscription = newMember;
});
it('Returns an identical member format for read, edit and browse', async function () {
if (!memberWithPaidSubscription) {
// Previous test failed
this.skip();
}
// Check status has been updated to 'free' after cancelling
const {body: readBody} = await agent.get('/members/' + memberWithPaidSubscription.id + '/');
assert.equal(readBody.members.length, 1, 'The member was not found in read');
const readMember = readBody.members[0];
// Note that we explicitly need to ask to include products while browsing
const {body: browseBody} = await agent.get(`/members/?search=${memberWithPaidSubscription.email}&include=products`);
assert.equal(browseBody.members.length, 1, 'The member was not found in browse');
const browseMember = browseBody.members[0];
// Check for this member with a paid subscription that the body results for the patch, get and browse endpoints are 100% identical
should.deepEqual(browseMember, readMember, 'Browsing a member returns a different format than reading a member');
should.deepEqual(memberWithPaidSubscription, readMember, 'Editing a member returns a different format than reading a member');
});
it('Can edit by id', async function () {

View file

@ -1203,6 +1203,13 @@ describe('Members API', function () {
offer_id: offer_id
});
// Check whether the offer attribute is passed correctly in the response when fetching a single member
member.subscriptions[0].should.match({
offer: {
id: offer_id
}
});
await assertMemberEvents({
eventType: 'MemberPaidSubscriptionEvent',
memberId: member.id,
@ -1245,7 +1252,10 @@ describe('Members API', function () {
assert.equal(updatedMember.products.length, 0, 'The member should have no products');
should(updatedMember.subscriptions).match([
{
status: 'canceled'
status: 'canceled',
offer: {
id: offer_id
}
}
]);
@ -1592,6 +1602,11 @@ describe('Members API', function () {
offer_id: null
});
// Check whether the offer attribute is passed correctly in the response when fetching a single member
member.subscriptions[0].should.match({
offer: null
});
await assertMemberEvents({
eventType: 'MemberPaidSubscriptionEvent',
memberId: member.id,
@ -1642,6 +1657,13 @@ describe('Members API', function () {
offer_id: offer.id
});
// Check whether the offer attribute is passed correctly in the response when fetching a single member
updatedMember.subscriptions[0].should.match({
offer: {
id: offer.id
}
});
await assertMemberEvents({
eventType: 'MemberPaidSubscriptionEvent',
memberId: updatedMember.id,
@ -1776,6 +1798,11 @@ describe('Members API', function () {
offer_id: null
});
// Check whether the offer attribute is passed correctly in the response when fetching a single member
member.subscriptions[0].should.match({
offer: null
});
await assertMemberEvents({
eventType: 'MemberPaidSubscriptionEvent',
memberId: member.id,

View file

@ -2009,10 +2009,10 @@
"@tryghost/domain-events" "^0.1.9"
"@tryghost/member-events" "^0.4.1"
"@tryghost/members-api@5.11.1":
version "5.11.1"
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-5.11.1.tgz#5b32fe806d5ce6f5d6743525fda74ea6667ffff4"
integrity sha512-yeaC6WpqNA+4qXqpLN5+Yo5ms3R1diMmQe6JnsMcjI6Nuby8ltPJi4uazFd09uFI2b+Bj/yucEzStbkT5lUS4g==
"@tryghost/members-api@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-5.12.0.tgz#b26389fef5e66602768e953cdc37da6714bd693f"
integrity sha512-lczUcc5Vd3WKUaBu4x2aP5y9CqFqqHEFOS5Rh/KvYD6Nu8UDdjxtb6mok9IngH48UG2RS8y3WoWkJFNtSNBfpQ==
dependencies:
"@nexes/nql" "^0.6.0"
"@tryghost/debug" "^0.1.2"