0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-15 03:01:37 -05:00

Added 307 redirects for old API endpoints

closes: https://github.com/TryGhost/Toolbox/issues/296

- This is a small change to permit any known API version to redirect to an unversioned URL
- We include v2 because although it should have been deleted in 5.0 anyway, in the spirit of the change away from versioned URLs there's
  absolutely no sense in forcing people to update clients that still work for no reason.
- We use a 307, because this preserves the original HTTP method, allowing POSTS, PUTs and DELETEs through as well as GETs
- We set the accept-version header on the redirect, meaning that for example with a request to the old /v4/ api, Ghost will respond as though
  the client sent `accept-version: v4.0` and if there are known breaking changes, it may choose to inform the admin and owner users of these
This commit is contained in:
Hannah Wolfe 2022-04-21 09:47:45 +01:00 committed by Daniel Lockyer
parent 5e020f1a8f
commit 0eedb1c556
No known key found for this signature in database
GPG key ID: D21186F0B47295AD
3 changed files with 88 additions and 1 deletions

View file

@ -14,6 +14,19 @@ module.exports = function setupApiApp() {
apiApp.use(require('./testmode')());
}
// If there is a version in the URL, and this is a valid API URL containing admin/content
// Then 307 redirect (preserves the HTTP method) to a versionless URL with `accept-version` set.
apiApp.all('/:version(v2|v3|v4|canary)/:api(admin|content)/*', (req, res) => {
const {version} = req.params;
const versionlessURL = req.originalUrl.replace(`${version}/`, '');
if (version.startsWith('v')) {
res.header('accept-version', `${version}.0`);
} else {
res.header('accept-version', version);
}
res.redirect(307, versionlessURL);
});
apiApp.lazyUse('/content/', require('./canary/content/app'));
apiApp.lazyUse('/admin/', require('./canary/admin/app'));

View file

@ -1,5 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`API Versioning Admin API 307 redirects GET with accept version set when version is included in the URL 1: [headers] 1`] = `
Object {
"accept-version": "canary",
"content-length": "57",
"content-type": "text/plain; charset=utf-8",
"location": StringMatching /\\^\\\\/ghost\\\\/api\\\\/admin\\\\/site\\\\/\\$/,
"vary": "Accept, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`API Versioning Admin API 307 redirects POST with accept version set when version is included in the URL 1: [headers] 1`] = `
Object {
"accept-version": "v3.0",
"content-length": "60",
"content-type": "text/plain; charset=utf-8",
"location": StringMatching /\\^\\\\/ghost\\\\/api\\\\/admin\\\\/session\\\\/\\$/,
"vary": "Accept, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`API Versioning Admin API 307 redirects with accept version set when version is included in the URL 1: [headers] 1`] = `
Object {
"accept-version": "canary",
"content-length": "57",
"content-type": "text/plain; charset=utf-8",
"location": StringMatching /\\^\\\\/ghost\\\\/api\\\\/admin\\\\/site\\\\/\\$/,
"vary": "Accept, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`API Versioning Admin API responds with 404 error when the resource cannot be found 1: [body] 1`] = `
Object {
"errors": Array [
@ -225,6 +258,17 @@ Object {
}
`;
exports[`API Versioning Content API 307 redirects with accept version set when version is included in the URL 1: [headers] 1`] = `
Object {
"accept-version": "canary",
"content-length": "91",
"content-type": "text/plain; charset=utf-8",
"location": StringMatching /\\^\\\\/ghost\\\\/api\\\\/content\\\\/posts\\\\//,
"vary": "Accept, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`API Versioning Content API responds with current content version header when requested version is BEHIND current version and CAN respond 1: [body] 1`] = `
Object {
"meta": Object {},

View file

@ -1,5 +1,5 @@
const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework');
const {anyErrorId, anyString, stringMatching} = matchers;
const {anyErrorId, anyString, stringMatching, anyLocationFor} = matchers;
describe('API Versioning', function () {
describe('Admin API', function () {
@ -163,6 +163,26 @@ describe('API Versioning', function () {
}]
});
});
it('307 redirects GET with accept version set when version is included in the URL', async function () {
await agentAdminAPI
.get('/site/', {baseUrl: '/ghost/api/canary/admin/'})
.expectStatus(307)
.matchHeaderSnapshot({
location: stringMatching(/^\/ghost\/api\/admin\/site\/$/)
})
.expectEmptyBody();
});
it('307 redirects POST with accept version set when version is included in the URL', async function () {
await agentAdminAPI
.post('/session/', {baseUrl: '/ghost/api/v3/admin/'})
.expectStatus(307)
.matchHeaderSnapshot({
location: stringMatching(/^\/ghost\/api\/admin\/session\/$/)
})
.expectEmptyBody();
});
});
describe('Content API', function () {
@ -190,5 +210,15 @@ describe('API Versioning', function () {
})
.matchBodySnapshot();
});
it('307 redirects with accept version set when version is included in the URL', async function () {
await agentContentAPI
.get('/posts/', {baseUrl: '/ghost/api/canary/content/'})
.expectStatus(307)
.matchHeaderSnapshot({
location: stringMatching(/^\/ghost\/api\/content\/posts\//)
})
.expectEmptyBody();
});
});
});