mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Added 'content-version' header response
refs https://github.com/TryGhost/Toolbox/issues/280 - In response to 'Accept-Version' header in the request headers, Ghost will always respond with a content-version header indicating the version of the Ghost install that is responding. This should signal to the client the content version that is bein g served - This is a bare bones implementation and more logic with edge cases where `content-version` is served with a version value of "best format API could respond with" will be added later.
This commit is contained in:
parent
132726fe20
commit
76aa2479f8
4 changed files with 84 additions and 2 deletions
|
@ -64,7 +64,7 @@ const http = (apiImpl) => {
|
|||
const result = await apiImpl(frame);
|
||||
|
||||
debug(`External API request to ${frame.docName}.${frame.method}`);
|
||||
const headers = await shared.headers.get(result, apiImpl.headers, frame);
|
||||
const headers = await shared.headers.get(result, apiImpl.headers, frame) || {};
|
||||
|
||||
// CASE: api ctrl wants to handle the express response (e.g. streams)
|
||||
if (typeof result === 'function') {
|
||||
|
@ -82,6 +82,9 @@ const http = (apiImpl) => {
|
|||
res.status(statusCode);
|
||||
|
||||
// CASE: generate headers based on the api ctrl configuration
|
||||
if (req && req.headers && req.headers['accept-version'] && res.locals) {
|
||||
headers['content-version'] = `v${res.locals.safeVersion}`;
|
||||
}
|
||||
res.set(headers);
|
||||
|
||||
const send = (format) => {
|
||||
|
|
26
test/e2e-api/admin/__snapshots__/version.test.js.snap
Normal file
26
test/e2e-api/admin/__snapshots__/version.test.js.snap
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`API Versioning responds with current content version header when requested version is behind current version with no known changes 1: [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": "167",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||
"etag": Any<String>,
|
||||
"vary": "Origin, Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`API Versioning responds with no content version header when accept version header is not present 1: [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": "167",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": Any<String>,
|
||||
"vary": "Origin, Accept-Encoding",
|
||||
"x-powered-by": "Express",
|
||||
}
|
||||
`;
|
28
test/e2e-api/admin/version.test.js
Normal file
28
test/e2e-api/admin/version.test.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
const {agentProvider, matchers} = require('../../utils/e2e-framework');
|
||||
const {anyString, stringMatching} = matchers;
|
||||
|
||||
describe('API Versioning', function () {
|
||||
let agent;
|
||||
|
||||
before(async function () {
|
||||
agent = await agentProvider.getAdminAPIAgent();
|
||||
});
|
||||
|
||||
it('responds with no content version header when accept version header is NOT PRESENT', async function () {
|
||||
await agent
|
||||
.get('site/')
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyString
|
||||
});
|
||||
});
|
||||
|
||||
it('responds with current content version header when requested version is behind current version with no known changes', async function () {
|
||||
await agent
|
||||
.get('site/')
|
||||
.header('Accept-Version', 'v3.0')
|
||||
.matchHeaderSnapshot({
|
||||
etag: anyString,
|
||||
'content-version': stringMatching(/v\d+\.\d+/)
|
||||
});
|
||||
});
|
||||
});
|
|
@ -22,7 +22,9 @@ describe('Unit: api/shared/http', function () {
|
|||
|
||||
res.status = sinon.stub();
|
||||
res.json = sinon.stub();
|
||||
res.set = sinon.stub();
|
||||
res.set = (headers) => {
|
||||
res.headers = headers;
|
||||
};
|
||||
res.send = sinon.stub();
|
||||
|
||||
sinon.stub(shared.headers, 'get').resolves();
|
||||
|
@ -85,4 +87,27 @@ describe('Unit: api/shared/http', function () {
|
|||
|
||||
shared.http(apiImpl)(req, res, next);
|
||||
});
|
||||
|
||||
it('adds content-version header to the response when accept-version header is present in the request', function (done) {
|
||||
const apiImpl = sinon.stub().resolves('data');
|
||||
req.headers = {
|
||||
'accept-version': 'v5.1'
|
||||
};
|
||||
apiImpl.headers = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
res.locals = {
|
||||
safeVersion: '5.4'
|
||||
};
|
||||
next.callsFake(done);
|
||||
|
||||
res.json.callsFake(function () {
|
||||
shared.headers.get.calledOnce.should.be.true();
|
||||
res.status.calledOnce.should.be.true();
|
||||
res.headers['content-version'].should.equal('v5.4');
|
||||
done();
|
||||
});
|
||||
|
||||
shared.http(apiImpl)(req, res, next);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue