From 773f514ddb9cf1df7fe4b6570bd3b365449f4ef4 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Wed, 18 Sep 2024 16:47:56 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Added=20a=20maximum=20limit=20of?= =?UTF-8?q?=20100=20for=20GET=20members=20admin=20endpoint=20(#20643)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref: https://linear.app/tryghost/issue/SLO-188/set-a-maximum-limit-for-get-members-api Endpoint: ghost/api/admin/members/?limit=all Change Overview: We are updating the GET ghost/api/admin/members/ endpoint to remove support for the limit=all parameter. Previously, a request like GET ghost/api/admin/members/?limit=all would return a list of all members. Going forward, any request with limit=all or a limit greater than 100 will only return up to 100 members per request. This change aims to improve the performance and scalability of the API. What changes for users? - They will have to implement pagination to retrieve the list of all members. --- .../admin/__snapshots__/members.test.js.snap | 668 ++++++++++++++++++ ghost/core/test/e2e-api/admin/members.test.js | 61 ++ .../lib/services/MemberBREADService.js | 4 + 3 files changed, 733 insertions(+) diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap index b25ae54dc7..c0df1e7786 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap @@ -2314,6 +2314,674 @@ Object { } `; +exports[`Members API Can browse with limit 1: [body] 1`] = ` +Object { + "members": Array [ + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "with-product@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Dana Barrett", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip-paid@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Peter Venkman", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Winston Zeddemore", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + ], + "meta": Object { + "pagination": Object { + "limit": 3, + "next": 2, + "page": 1, + "pages": 3, + "prev": null, + "total": 8, + }, + }, +} +`; + +exports[`Members API Can browse with limit 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": "2954", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Members API Can browse with limit=all 1: [body] 1`] = ` +Object { + "members": Array [ + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "with-product@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Dana Barrett", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip-paid@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Peter Venkman", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Winston Zeddemore", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "comped@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Vinz Clortho", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "trialing@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Ray Stantz", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "paid@test.com", + "email_count": 0, + "email_open_rate": 80, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Egon Spengler", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "member2@test.com", + "email_count": 0, + "email_open_rate": 50, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": null, + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "member1@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Mr Egg", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + ], + "meta": Object { + "pagination": Object { + "limit": 100, + "next": null, + "page": 1, + "pages": 1, + "prev": null, + "total": 8, + }, + }, +} +`; + +exports[`Members API Can browse with limit=all 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": "10101", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Members API Can browse with more than maximum allowed limit 1: [body] 1`] = ` +Object { + "members": Array [ + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "with-product@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Dana Barrett", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip-paid@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Peter Venkman", + "newsletters": Array [], + "note": null, + "status": "paid", + "subscribed": false, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "vip@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Winston Zeddemore", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "comped@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Vinz Clortho", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "trialing@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Ray Stantz", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Weekly newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "paid@test.com", + "email_count": 0, + "email_open_rate": 80, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Egon Spengler", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "paid", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "member2@test.com", + "email_count": 0, + "email_open_rate": 50, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": null, + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + Object { + "avatar_image": null, + "comped": false, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "email": "member1@test.com", + "email_count": 0, + "email_open_rate": null, + "email_opened_count": 0, + "email_suppression": Object { + "info": null, + "suppressed": false, + }, + "geolocation": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "labels": Any, + "last_seen_at": null, + "name": "Mr Egg", + "newsletters": Array [ + Object { + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "name": "Daily newsletter", + "status": "active", + }, + ], + "note": null, + "status": "free", + "subscribed": true, + "subscriptions": Any, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + }, + ], + "meta": Object { + "pagination": Object { + "limit": 100, + "next": null, + "page": 1, + "pages": 1, + "prev": null, + "total": 8, + }, + }, +} +`; + +exports[`Members API Can browse with more than maximum allowed limit 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": "10101", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + exports[`Members API Can browse with search 1: [body] 1`] = ` Object { "members": Array [ diff --git a/ghost/core/test/e2e-api/admin/members.test.js b/ghost/core/test/e2e-api/admin/members.test.js index fb7f6bdfdb..aa4d04ab75 100644 --- a/ghost/core/test/e2e-api/admin/members.test.js +++ b/ghost/core/test/e2e-api/admin/members.test.js @@ -524,6 +524,67 @@ describe('Members API', function () { }); }); + it('Can browse with limit', async function () { + await agent + .get('/members/?limit=3') + .expectStatus(200) + .matchBodySnapshot({ + members: [ + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1) + ] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + }); + + it('Can browse with more than maximum allowed limit', async function () { + await agent + .get('/members/?limit=300') + .expectStatus(200) + .matchBodySnapshot({ + members: [ + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 2), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1) + ] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + }); + + it('Can browse with limit=all', async function () { + await agent + .get('/members/?limit=all') + .expectStatus(200) + .matchBodySnapshot({ + members: [ + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 0), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 2), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1), + buildMemberMatcherShallowIncludesWithTiers(undefined, 1) + ] + }) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + }); + it('Can browse with filter', async function () { await agent .get('/members/?filter=label:label-1') diff --git a/ghost/members-api/lib/services/MemberBREADService.js b/ghost/members-api/lib/services/MemberBREADService.js index 66faef1a94..fea18bb6b7 100644 --- a/ghost/members-api/lib/services/MemberBREADService.js +++ b/ghost/members-api/lib/services/MemberBREADService.js @@ -381,6 +381,10 @@ module.exports = class MemberBREADService { 'newsletters' ]; + if (options.limit === 'all' || options.limit > 100) { + options.limit = 100; + } + const originalWithRelated = options.withRelated || []; const withRelated = new Set((originalWithRelated).concat(defaultWithRelated));