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.
This commit is contained in:
Fabien O'Carroll 2024-02-27 16:13:08 -05:00 committed by Fabien 'egg' O'Carroll
parent b91bf74236
commit a177600b30
3 changed files with 61 additions and 50 deletions

View File

@ -9,6 +9,7 @@ const _ = require('lodash');
* easiest to use. We always have access to the original input, we never loose track of it. * easiest to use. We always have access to the original input, we never loose track of it.
*/ */
class Frame { class Frame {
#headers = {};
constructor(obj = {}) { constructor(obj = {}) {
this.original = obj; this.original = obj;
@ -27,6 +28,7 @@ class Frame {
this.user = {}; this.user = {};
this.file = {}; this.file = {};
this.files = []; this.files = [];
this.#headers = {};
this.apiType = null; this.apiType = null;
this.docName = null; this.docName = null;
this.method = null; this.method = null;
@ -95,6 +97,14 @@ class Frame {
debug('options', this.options); debug('options', this.options);
debug('data', this.data); debug('data', this.data);
} }
setHeader(header, value) {
this.#headers[header] = value;
}
getHeaders() {
return Object.assign({}, this.#headers);
}
} }
module.exports = Frame; module.exports = Frame;

View File

@ -147,6 +147,10 @@ module.exports = {
Object.assign(headers, locationHeader); Object.assign(headers, locationHeader);
} }
const headersFromFrame = frame.getHeaders();
Object.assign(headers, headersFromFrame);
debug(headers); debug(headers);
return headers; return headers;
} }

View File

@ -1,15 +1,16 @@
const shared = require('../'); const shared = require('../');
const Frame = require('../lib/Frame');
describe('Headers', function () { describe('Headers', function () {
it('empty headers config', function () { it('empty headers config', function () {
return shared.headers.get().then((result) => { return shared.headers.get({}, {}, new Frame()).then((result) => {
result.should.eql({}); result.should.eql({});
}); });
}); });
describe('config.disposition', function () { describe('config.disposition', function () {
it('json', 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) => { .then((result) => {
result.should.eql({ result.should.eql({
'Content-Disposition': 'Attachment; filename="value"', 'Content-Disposition': 'Attachment; filename="value"',
@ -20,7 +21,7 @@ describe('Headers', function () {
}); });
it('csv', 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) => { .then((result) => {
result.should.eql({ result.should.eql({
'Content-Disposition': 'Attachment; filename="my.csv"', 'Content-Disposition': 'Attachment; filename="my.csv"',
@ -39,7 +40,7 @@ describe('Headers', function () {
return filename; return filename;
} }
} }
}); }, new Frame());
result.should.eql({ result.should.eql({
'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.csv"', 'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.csv"',
'Content-Type': 'text/csv' 'Content-Type': 'text/csv'
@ -47,7 +48,7 @@ describe('Headers', function () {
}); });
it('file', async 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({ result.should.eql({
'Content-Disposition': 'Attachment; filename="my.txt"' 'Content-Disposition': 'Attachment; filename="my.txt"'
}); });
@ -63,14 +64,14 @@ describe('Headers', function () {
return filename; return filename;
} }
} }
}); }, new Frame());
result.should.eql({ result.should.eql({
'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.txt"' 'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.txt"'
}); });
}); });
it('yaml', function () { 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) => { .then((result) => {
result.should.eql({ result.should.eql({
'Content-Disposition': 'Attachment; filename="my.yaml"', 'Content-Disposition': 'Attachment; filename="my.yaml"',
@ -83,7 +84,7 @@ describe('Headers', function () {
describe('config.cacheInvalidate', function () { describe('config.cacheInvalidate', function () {
it('default', function () { it('default', function () {
return shared.headers.get({}, {cacheInvalidate: true}) return shared.headers.get({}, {cacheInvalidate: true}, new Frame())
.then((result) => { .then((result) => {
result.should.eql({ result.should.eql({
'X-Cache-Invalidate': '/*' 'X-Cache-Invalidate': '/*'
@ -92,7 +93,7 @@ describe('Headers', function () {
}); });
it('custom value', function () { it('custom value', function () {
return shared.headers.get({}, {cacheInvalidate: {value: 'value'}}) return shared.headers.get({}, {cacheInvalidate: {value: 'value'}}, new Frame())
.then((result) => { .then((result) => {
result.should.eql({ result.should.eql({
'X-Cache-Invalidate': 'value' 'X-Cache-Invalidate': 'value'
@ -110,14 +111,13 @@ describe('Headers', function () {
}; };
const apiConfigHeaders = {}; const apiConfigHeaders = {};
const frame = { const frame = new Frame();
docName: 'posts', frame.docName = 'posts',
method: 'add', frame.method = 'add',
original: { frame.original = {
url: { url: {
host: 'example.com', host: 'example.com',
pathname: `/api/content/posts/` pathname: `/api/content/posts/`
}
} }
}; };
@ -146,14 +146,13 @@ describe('Headers', function () {
} }
} }
}; };
const frame = { const frame = new Frame();
docName: 'posts', frame.docName = 'posts';
method: 'copy', frame.method = 'copy';
original: { frame.original = {
url: { url: {
host: 'example.com', host: 'example.com',
pathname: `/api/content/posts/existing_post_id_value/copy` pathname: `/api/content/posts/existing_post_id_value/copy`
}
} }
}; };
@ -173,15 +172,15 @@ describe('Headers', function () {
}; };
const apiConfigHeaders = {}; const apiConfigHeaders = {};
const frame = { const frame = new Frame();
docName: 'posts',
method: 'add', frame.docName = 'posts';
original: { frame.method = 'add';
url: { frame.original = {
host: 'example.com', url: {
pathname: `/api/content/posts/`, host: 'example.com',
secure: false pathname: `/api/content/posts/`,
} secure: false
} }
}; };
@ -200,14 +199,13 @@ describe('Headers', function () {
}; };
const apiConfigHeaders = {}; const apiConfigHeaders = {};
const frame = { const frame = new Frame();
docName: 'posts', frame.docName = 'posts';
method: 'add', frame.method = 'add';
original: { frame.original = {
url: { url: {
host: 'example.com', host: 'example.com',
pathname: `/api/content/posts` pathname: `/api/content/posts`
}
} }
}; };
@ -224,14 +222,13 @@ describe('Headers', function () {
const apiResult = {}; const apiResult = {};
const apiConfigHeaders = {}; const apiConfigHeaders = {};
const frame = { const frame = new Frame();
docName: 'posts', frame.docName = 'posts';
method: 'add', frame.method = 'add';
original: { frame.original = {
url: { url: {
host: 'example.com', host: 'example.com',
pathname: `/api/content/posts/` pathname: `/api/content/posts/`
}
} }
}; };