2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Moved announcement bar settings to Frontend Members API

https://github.com/TryGhost/Team/issues/3121

- Because the announcement data has to be available with member's context, it's only possible to have it in cross-origin requests in the Members API.
- Exposed the announcement bar data through `GET /members/api/announcement` endpoint
This commit is contained in:
Naz 2023-04-27 12:52:28 +02:00 committed by naz
parent 57557cb2f7
commit 06c0a19718
8 changed files with 149 additions and 11 deletions

View file

@ -0,0 +1,12 @@
const announcementBarSettings = require('../../services/announcement-bar-service');
module.exports = {
docName: 'announcement',
browse: {
permissions: true,
query(frame) {
return announcementBarSettings.getAnnouncementSettings(frame.options.context?.member);
}
}
};

View file

@ -77,6 +77,10 @@ module.exports = {
return apiFramework.pipeline(require('./settings'), localUtils);
},
get announcements() {
return apiFramework.pipeline(require('./announcements'), localUtils);
},
get membersStripeConnect() {
return apiFramework.pipeline(require('./members-stripe-connect'), localUtils);
},
@ -239,5 +243,5 @@ module.exports = {
get feedbackMembers() {
return apiFramework.pipeline(require('./feedback-members'), localUtils, 'members');
}
}
};

View file

@ -1,25 +1,17 @@
const settingsCache = require('../../../shared/settings-cache');
const urlUtils = require('../../../shared/url-utils');
const ghostVersion = require('@tryghost/version');
const announcementBarSettings = require('../../services/announcement-bar-service');
const labs = require('../../../shared/labs');
module.exports = {
docName: 'settings',
browse: {
permissions: true,
query(frame) {
let announcementSettings;
if (labs.isSet('announcementBar')) {
announcementSettings = announcementBarSettings.getAnnouncementSettings(frame.options.context?.member);
}
query() {
// @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(),
announcementSettings, {
settingsCache.getPublic(), {
url: urlUtils.urlFor('home', true),
version: ghostVersion.safe
}

View file

@ -0,0 +1 @@
module.exports = require('./routes');

View file

@ -0,0 +1,15 @@
const express = require('../../../shared/express');
const api = require('../../api').endpoints;
const {http} = require('@tryghost/api-framework');
const shared = require('../shared');
module.exports = function apiRoutes() {
const router = express.Router('announcements');
// shouldn't be cached as it depends on member's context
router.use(shared.middleware.cacheControl('private'));
router.get('/', http(api.announcements.browse));
return router;
};

View file

@ -14,6 +14,7 @@ const {http} = require('@tryghost/api-framework');
const api = require('../../api').endpoints;
const commentRouter = require('../comments');
const announcementRouter = require('../announcement');
module.exports = function setupMembersApp() {
debug('Members App setup start');
@ -79,6 +80,14 @@ module.exports = function setupMembersApp() {
http(api.feedbackMembers.add)
);
// Announcement
membersApp.use(
'/api/announcement',
labs.enabledMiddleware('announcementBar'),
middleware.loadMemberSession,
announcementRouter()
);
// API error handling
membersApp.use('/api', errorHandler.resourceNotFound);
membersApp.use('/api', errorHandler.handleJSONResponse(sentry));

View file

@ -0,0 +1,64 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Announcement Can read announcement data 1: [body] 1`] = `
Object {
"announcement": Array [
Object {},
],
}
`;
exports[`Announcement Can read announcement data 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": "21",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Announcement Can read announcement endpoint 1: [body] 1`] = `
Object {
"announcement": Array [
Object {},
],
}
`;
exports[`Announcement Can read announcement endpoint 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": "21",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Announcement Can read announcement when it is present in announcement data 1: [body] 1`] = `
Object {
"announcement": Array [
Object {
"announcement": "<p>Test announcement</p>",
"announcement_background": "dark",
},
],
}
`;
exports[`Announcement Can read announcement when it is present in announcement data 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": "95",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",
"x-powered-by": "Express",
}
`;

View file

@ -0,0 +1,41 @@
const {agentProvider, mockManager, fixtureManager, matchers, configUtils} = require('../../utils/e2e-framework');
const {anyEtag} = matchers;
const settingsCache = require('../../../core/shared/settings-cache');
describe('Announcement', function () {
let membersAgent;
before(async function () {
membersAgent = await agentProvider.getMembersAPIAgent();
await fixtureManager.init('members');
});
afterEach(async function () {
await configUtils.restore();
mockManager.restore();
});
it('Can read announcement endpoint', async function () {
await membersAgent
.get(`/api/announcement/`)
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag
})
.matchBodySnapshot();
});
it('Can read announcement when it is present in announcement data', async function () {
settingsCache.set('announcement_content', {value: '<p>Test announcement</p>'});
settingsCache.set('announcement_visibility', {value: ['visitors']});
await membersAgent
.get(`/api/announcement/`)
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag
})
.matchBodySnapshot();
});
});