From f4066067e4769423125bfd03a7be3d1915c73c71 Mon Sep 17 00:00:00 2001 From: Rishabh Garg Date: Thu, 12 May 2022 19:54:45 +0530 Subject: [PATCH] Extended public settings to include portal settings (#14801) refs https://github.com/TryGhost/Team/issues/1599 - adds `portal_*` settings to public settings endpoint - adds calculated `firstpromoter_account` setting for public settings endpoint - also adds Ghost `version` information --- core/server/api/canary/settings-public.js | 4 ++- .../services/settings/settings-service.js | 8 ++++++ core/shared/settings-cache/public.js | 9 +++++- .../admin/__snapshots__/settings.test.js.snap | 11 +++++++- test/e2e-api/admin/settings.test.js | 2 +- .../__snapshots__/settings.test.js.snap | 12 +++++++- test/e2e-api/content/settings.test.js | 8 +++++- .../shared/__snapshots__/version.test.js.snap | 28 ++++++++++++++++--- test/e2e-api/shared/version.test.js | 12 ++++++-- test/regression/api/admin/settings.test.js | 22 +++++++-------- 10 files changed, 92 insertions(+), 24 deletions(-) diff --git a/core/server/api/canary/settings-public.js b/core/server/api/canary/settings-public.js index a8e2854446..74c0bbf5f4 100644 --- a/core/server/api/canary/settings-public.js +++ b/core/server/api/canary/settings-public.js @@ -1,5 +1,6 @@ const settingsCache = require('../../../shared/settings-cache'); const urlUtils = require('../../../shared/url-utils'); +const ghostVersion = require('@tryghost/version'); module.exports = { docName: 'settings', @@ -10,7 +11,8 @@ module.exports = { // @TODO: decouple settings cache from API knowledge // The controller fetches models (or cached models) and the API frame for the target API version formats the response. return Object.assign({}, settingsCache.getPublic(), { - url: urlUtils.urlFor('home', true) + url: urlUtils.urlFor('home', true), + version: ghostVersion.safe }); } } diff --git a/core/server/services/settings/settings-service.js b/core/server/services/settings/settings-service.js index 9ee1cec70d..da118134c6 100644 --- a/core/server/services/settings/settings-service.js +++ b/core/server/services/settings/settings-service.js @@ -122,6 +122,13 @@ module.exports = { return this.isMembersEnabled() && this.getActiveStripeKeys() !== null; }, + getFirstpromoterId() { + if (!SettingsCache.get('firstpromoter')) { + return null; + } + return SettingsCache.get('firstpromoter_id'); + }, + /** * */ @@ -131,6 +138,7 @@ module.exports = { fields.push(new CalculatedField({key: 'members_enabled', type: 'boolean', group: 'members', fn: this.isMembersEnabled.bind(this), dependents: ['members_signup_access']})); fields.push(new CalculatedField({key: 'members_invite_only', type: 'boolean', group: 'members', fn: this.isMembersInviteOnly.bind(this), dependents: ['members_signup_access']})); fields.push(new CalculatedField({key: 'paid_members_enabled', type: 'boolean', group: 'members', fn: this.isStripeConnected.bind(this), dependents: ['members_signup_access', 'stripe_secret_key', 'stripe_publishable_key', 'stripe_connect_secret_key', 'stripe_connect_publishable_key']})); + fields.push(new CalculatedField({key: 'firstpromoter_account', type: 'string', group: 'firstpromoter', fn: this.getFirstpromoterId.bind(this), dependents: ['firstpromoter', 'firstpromoter_id']})); return fields; }, diff --git a/core/shared/settings-cache/public.js b/core/shared/settings-cache/public.js index addb92214a..e3f169de48 100644 --- a/core/shared/settings-cache/public.js +++ b/core/shared/settings-cache/public.js @@ -29,5 +29,12 @@ module.exports = { members_support_address: 'members_support_address', members_enabled: 'members_enabled', members_invite_only: 'members_invite_only', - paid_members_enabled: 'paid_members_enabled' + paid_members_enabled: 'paid_members_enabled', + firstpromoter_account: 'firstpromoter_account', + portal_button_style: 'portal_button_style', + portal_button_signup_text: 'portal_button_signup_text', + portal_button_icon: 'portal_button_icon', + portal_plans: 'portal_plans', + portal_name: 'portal_name', + portal_button: 'portal_button' }; diff --git a/test/e2e-api/admin/__snapshots__/settings.test.js.snap b/test/e2e-api/admin/__snapshots__/settings.test.js.snap index 5fada7a16a..b310158d3e 100644 --- a/test/e2e-api/admin/__snapshots__/settings.test.js.snap +++ b/test/e2e-api/admin/__snapshots__/settings.test.js.snap @@ -1082,6 +1082,15 @@ Object { "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "value": true, }, + Object { + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "group": "firstpromoter", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "key": "firstpromoter_account", + "type": "string", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "value": null, + }, Object { "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, "flags": null, @@ -1112,7 +1121,7 @@ exports[`Settings API Can request all settings 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": "18289", + "content-length": "18486", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Origin, Accept-Encoding", diff --git a/test/e2e-api/admin/settings.test.js b/test/e2e-api/admin/settings.test.js index dca4e39c22..f63c30c76c 100644 --- a/test/e2e-api/admin/settings.test.js +++ b/test/e2e-api/admin/settings.test.js @@ -2,7 +2,7 @@ const assert = require('assert'); const {agentProvider, fixtureManager, mockManager, matchers} = require('../../utils/e2e-framework'); const {stringMatching, anyEtag, anyObjectId, anyISODateTime} = matchers; -const CURRENT_SETTINGS_COUNT = 86; +const CURRENT_SETTINGS_COUNT = 87; const settingsMatcher = { id: anyObjectId, diff --git a/test/e2e-api/content/__snapshots__/settings.test.js.snap b/test/e2e-api/content/__snapshots__/settings.test.js.snap index ec764a2413..1787d0eba5 100644 --- a/test/e2e-api/content/__snapshots__/settings.test.js.snap +++ b/test/e2e-api/content/__snapshots__/settings.test.js.snap @@ -10,6 +10,7 @@ Object { "cover_image": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg", "description": "Thoughts, stories and ideas", "facebook": "ghost", + "firstpromoter_account": null, "icon": null, "lang": "en", "locale": "en", @@ -45,6 +46,14 @@ Object { "og_image": null, "og_title": null, "paid_members_enabled": true, + "portal_button": true, + "portal_button_icon": null, + "portal_button_signup_text": "Subscribe", + "portal_button_style": "icon-and-text", + "portal_name": true, + "portal_plans": Array [ + "free", + ], "secondary_navigation": Array [ Object { "label": "Data & privacy", @@ -66,6 +75,7 @@ Object { "twitter_image": null, "twitter_title": null, "url": "http://127.0.0.1:2369/", + "version": Any, }, } `; @@ -86,7 +96,7 @@ exports[`Settings Content API Can request settings 2: [headers] 1`] = ` Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1021", + "content-length": "1235", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Encoding", diff --git a/test/e2e-api/content/settings.test.js b/test/e2e-api/content/settings.test.js index 680f92625c..d4073f06fd 100644 --- a/test/e2e-api/content/settings.test.js +++ b/test/e2e-api/content/settings.test.js @@ -1,6 +1,10 @@ const {agentProvider, fixtureManager, matchers} = require('../../utils/e2e-framework'); const {anyEtag} = matchers; +const settingsMatcher = { + version: matchers.anyString +}; + describe('Settings Content API', function () { let agent; @@ -16,6 +20,8 @@ describe('Settings Content API', function () { .matchHeaderSnapshot({ etag: anyEtag }) - .matchBodySnapshot(); + .matchBodySnapshot({ + settings: settingsMatcher + }); }); }); diff --git a/test/e2e-api/shared/__snapshots__/version.test.js.snap b/test/e2e-api/shared/__snapshots__/version.test.js.snap index ad67a11518..e53a7d8688 100644 --- a/test/e2e-api/shared/__snapshots__/version.test.js.snap +++ b/test/e2e-api/shared/__snapshots__/version.test.js.snap @@ -488,6 +488,7 @@ Object { "cover_image": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg", "description": "Thoughts, stories and ideas", "facebook": "ghost", + "firstpromoter_account": null, "icon": null, "lang": "en", "locale": "en", @@ -523,6 +524,14 @@ Object { "og_image": null, "og_title": null, "paid_members_enabled": true, + "portal_button": true, + "portal_button_icon": null, + "portal_button_signup_text": "Subscribe", + "portal_button_style": "icon-and-text", + "portal_name": true, + "portal_plans": Array [ + "free", + ], "secondary_navigation": Array [ Object { "label": "Data & privacy", @@ -544,6 +553,7 @@ Object { "twitter_image": null, "twitter_title": null, "url": "http://127.0.0.1:2369/", + "version": Any, }, } `; @@ -552,10 +562,10 @@ exports[`API Versioning Content API responds with current content version header Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1021", + "content-length": "1235", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, - "etag": "W/\\"3fd-PXBX1gYn1ftAeK+GoVbEnAsmVAE\\"", + "etag": "W/\\"4d3-mq2QrkgGnMZ8/BMmziwONEMrvLM\\"", "vary": "Accept-Encoding", "x-powered-by": "Express", } @@ -571,6 +581,7 @@ Object { "cover_image": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg", "description": "Thoughts, stories and ideas", "facebook": "ghost", + "firstpromoter_account": null, "icon": null, "lang": "en", "locale": "en", @@ -606,6 +617,14 @@ Object { "og_image": null, "og_title": null, "paid_members_enabled": true, + "portal_button": true, + "portal_button_icon": null, + "portal_button_signup_text": "Subscribe", + "portal_button_style": "icon-and-text", + "portal_name": true, + "portal_plans": Array [ + "free", + ], "secondary_navigation": Array [ Object { "label": "Data & privacy", @@ -627,6 +646,7 @@ Object { "twitter_image": null, "twitter_title": null, "url": "http://127.0.0.1:2369/", + "version": Any, }, } `; @@ -635,9 +655,9 @@ exports[`API Versioning Content API responds with no content version header when Object { "access-control-allow-origin": "*", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1021", + "content-length": "1235", "content-type": "application/json; charset=utf-8", - "etag": "W/\\"3fd-PXBX1gYn1ftAeK+GoVbEnAsmVAE\\"", + "etag": "W/\\"4d3-mq2QrkgGnMZ8/BMmziwONEMrvLM\\"", "vary": "Accept-Encoding", "x-powered-by": "Express", } diff --git a/test/e2e-api/shared/version.test.js b/test/e2e-api/shared/version.test.js index c16ae3372d..db2b6487e1 100644 --- a/test/e2e-api/shared/version.test.js +++ b/test/e2e-api/shared/version.test.js @@ -1,5 +1,11 @@ const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework'); -const {anyErrorId, stringMatching, anyObjectId, anyLocationFor, anyISODateTime, anyEtag} = matchers; +const {anyErrorId, stringMatching, anyObjectId, anyLocationFor, anyISODateTime, anyEtag, anyString} = matchers; + +const settingsMatcher = { + settings: { + version: anyString + } +}; describe('API Versioning', function () { describe('Admin API', function () { @@ -260,7 +266,7 @@ describe('API Versioning', function () { await agentContentAPI.get('settings/') .expectStatus(200) .matchHeaderSnapshot() - .matchBodySnapshot(); + .matchBodySnapshot(settingsMatcher); }); it('responds with current content version header when requested version is BEHIND current version and CAN respond', async function () { @@ -270,7 +276,7 @@ describe('API Versioning', function () { .matchHeaderSnapshot({ 'content-version': stringMatching(/v\d+\.\d+/) }) - .matchBodySnapshot(); + .matchBodySnapshot(settingsMatcher); }); it('Does an internal rewrite with accept version set when version is included in the URL', async function () { diff --git a/test/regression/api/admin/settings.test.js b/test/regression/api/admin/settings.test.js index cf444ee334..6c5cb568ec 100644 --- a/test/regression/api/admin/settings.test.js +++ b/test/regression/api/admin/settings.test.js @@ -264,6 +264,16 @@ const defaultSettingsKeyTypes = [ type: 'string', group: 'portal' }, + { + key: 'firstpromoter', + type: 'boolean', + group: 'firstpromoter' + }, + { + key: 'firstpromoter_id', + type: 'string', + group: 'firstpromoter' + }, { key: 'portal_button_icon', type: 'string', @@ -384,16 +394,6 @@ const defaultSettingsKeyTypes = [ type: 'string', group: 'newsletter' }, - { - key: 'firstpromoter', - type: 'boolean', - group: 'firstpromoter' - }, - { - key: 'firstpromoter_id', - type: 'string', - group: 'firstpromoter' - }, { key: 'oauth_client_id', type: 'string', @@ -426,7 +426,7 @@ const defaultSettingsKeyTypes = [ } ]; -const calculatedSettingsTypes = ['members_enabled', 'members_invite_only', 'paid_members_enabled']; +const calculatedSettingsTypes = ['members_enabled', 'members_invite_only', 'paid_members_enabled', 'firstpromoter_account']; describe('Settings API (canary)', function () { let request;