From a177600b30e9dee6e10283e429c496357829be14 Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Tue, 27 Feb 2024 16:13:08 -0500 Subject: [PATCH] Supported setting headers on a per-request basis refs https://linear.app/tryghost/issue/ENG-674 This paves the way for us to have dynamic cache invalidation headers without clobbering the shared headers config. --- ghost/api-framework/lib/Frame.js | 10 +++ ghost/api-framework/lib/headers.js | 4 + ghost/api-framework/test/headers.test.js | 97 ++++++++++++------------ 3 files changed, 61 insertions(+), 50 deletions(-) diff --git a/ghost/api-framework/lib/Frame.js b/ghost/api-framework/lib/Frame.js index 2be8cb3b74..70d265bb8a 100644 --- a/ghost/api-framework/lib/Frame.js +++ b/ghost/api-framework/lib/Frame.js @@ -9,6 +9,7 @@ const _ = require('lodash'); * easiest to use. We always have access to the original input, we never loose track of it. */ class Frame { + #headers = {}; constructor(obj = {}) { this.original = obj; @@ -27,6 +28,7 @@ class Frame { this.user = {}; this.file = {}; this.files = []; + this.#headers = {}; this.apiType = null; this.docName = null; this.method = null; @@ -95,6 +97,14 @@ class Frame { debug('options', this.options); debug('data', this.data); } + + setHeader(header, value) { + this.#headers[header] = value; + } + + getHeaders() { + return Object.assign({}, this.#headers); + } } module.exports = Frame; diff --git a/ghost/api-framework/lib/headers.js b/ghost/api-framework/lib/headers.js index 4349777c12..f235e82906 100644 --- a/ghost/api-framework/lib/headers.js +++ b/ghost/api-framework/lib/headers.js @@ -147,6 +147,10 @@ module.exports = { Object.assign(headers, locationHeader); } + const headersFromFrame = frame.getHeaders(); + + Object.assign(headers, headersFromFrame); + debug(headers); return headers; } diff --git a/ghost/api-framework/test/headers.test.js b/ghost/api-framework/test/headers.test.js index c7ce6dfedd..941ba9160b 100644 --- a/ghost/api-framework/test/headers.test.js +++ b/ghost/api-framework/test/headers.test.js @@ -1,15 +1,16 @@ const shared = require('../'); +const Frame = require('../lib/Frame'); describe('Headers', function () { it('empty headers config', function () { - return shared.headers.get().then((result) => { + return shared.headers.get({}, {}, new Frame()).then((result) => { result.should.eql({}); }); }); describe('config.disposition', function () { it('json', function () { - return shared.headers.get({}, {disposition: {type: 'json', value: 'value'}}) + return shared.headers.get({}, {disposition: {type: 'json', value: 'value'}}, new Frame()) .then((result) => { result.should.eql({ 'Content-Disposition': 'Attachment; filename="value"', @@ -20,7 +21,7 @@ describe('Headers', function () { }); it('csv', function () { - return shared.headers.get({}, {disposition: {type: 'csv', value: 'my.csv'}}) + return shared.headers.get({}, {disposition: {type: 'csv', value: 'my.csv'}}, new Frame()) .then((result) => { result.should.eql({ 'Content-Disposition': 'Attachment; filename="my.csv"', @@ -39,7 +40,7 @@ describe('Headers', function () { return filename; } } - }); + }, new Frame()); result.should.eql({ 'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.csv"', 'Content-Type': 'text/csv' @@ -47,7 +48,7 @@ describe('Headers', function () { }); it('file', async function () { - const result = await shared.headers.get({}, {disposition: {type: 'file', value: 'my.txt'}}); + const result = await shared.headers.get({}, {disposition: {type: 'file', value: 'my.txt'}}, new Frame()); result.should.eql({ 'Content-Disposition': 'Attachment; filename="my.txt"' }); @@ -63,14 +64,14 @@ describe('Headers', function () { return filename; } } - }); + }, new Frame()); result.should.eql({ 'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.txt"' }); }); it('yaml', function () { - return shared.headers.get('yaml file', {disposition: {type: 'yaml', value: 'my.yaml'}}) + return shared.headers.get('yaml file', {disposition: {type: 'yaml', value: 'my.yaml'}}, new Frame()) .then((result) => { result.should.eql({ 'Content-Disposition': 'Attachment; filename="my.yaml"', @@ -83,7 +84,7 @@ describe('Headers', function () { describe('config.cacheInvalidate', function () { it('default', function () { - return shared.headers.get({}, {cacheInvalidate: true}) + return shared.headers.get({}, {cacheInvalidate: true}, new Frame()) .then((result) => { result.should.eql({ 'X-Cache-Invalidate': '/*' @@ -92,7 +93,7 @@ describe('Headers', function () { }); it('custom value', function () { - return shared.headers.get({}, {cacheInvalidate: {value: 'value'}}) + return shared.headers.get({}, {cacheInvalidate: {value: 'value'}}, new Frame()) .then((result) => { result.should.eql({ 'X-Cache-Invalidate': 'value' @@ -110,14 +111,13 @@ describe('Headers', function () { }; const apiConfigHeaders = {}; - const frame = { - docName: 'posts', - method: 'add', - original: { - url: { - host: 'example.com', - pathname: `/api/content/posts/` - } + const frame = new Frame(); + frame.docName = 'posts', + frame.method = 'add', + frame.original = { + url: { + host: 'example.com', + pathname: `/api/content/posts/` } }; @@ -146,14 +146,13 @@ describe('Headers', function () { } } }; - const frame = { - docName: 'posts', - method: 'copy', - original: { - url: { - host: 'example.com', - pathname: `/api/content/posts/existing_post_id_value/copy` - } + const frame = new Frame(); + frame.docName = 'posts'; + frame.method = 'copy'; + frame.original = { + url: { + host: 'example.com', + pathname: `/api/content/posts/existing_post_id_value/copy` } }; @@ -173,15 +172,15 @@ describe('Headers', function () { }; const apiConfigHeaders = {}; - const frame = { - docName: 'posts', - method: 'add', - original: { - url: { - host: 'example.com', - pathname: `/api/content/posts/`, - secure: false - } + const frame = new Frame(); + + frame.docName = 'posts'; + frame.method = 'add'; + frame.original = { + url: { + host: 'example.com', + pathname: `/api/content/posts/`, + secure: false } }; @@ -200,14 +199,13 @@ describe('Headers', function () { }; const apiConfigHeaders = {}; - const frame = { - docName: 'posts', - method: 'add', - original: { - url: { - host: 'example.com', - pathname: `/api/content/posts` - } + const frame = new Frame(); + frame.docName = 'posts'; + frame.method = 'add'; + frame.original = { + url: { + host: 'example.com', + pathname: `/api/content/posts` } }; @@ -224,14 +222,13 @@ describe('Headers', function () { const apiResult = {}; const apiConfigHeaders = {}; - const frame = { - docName: 'posts', - method: 'add', - original: { - url: { - host: 'example.com', - pathname: `/api/content/posts/` - } + const frame = new Frame(); + frame.docName = 'posts'; + frame.method = 'add'; + frame.original = { + url: { + host: 'example.com', + pathname: `/api/content/posts/` } };