mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
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
224 lines
8.7 KiB
JavaScript
224 lines
8.7 KiB
JavaScript
const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework');
|
|
const {anyErrorId, anyString, stringMatching, anyLocationFor} = matchers;
|
|
|
|
describe('API Versioning', function () {
|
|
describe('Admin API', function () {
|
|
let agentAdminAPI;
|
|
|
|
before(async function () {
|
|
agentAdminAPI = await agentProvider.getAdminAPIAgent();
|
|
await fixtureManager.init();
|
|
await agentAdminAPI.loginAsOwner();
|
|
});
|
|
|
|
beforeEach(function () {
|
|
mockManager.mockMail();
|
|
});
|
|
|
|
afterEach(function () {
|
|
mockManager.restore();
|
|
});
|
|
|
|
it('responds with no content version header when accept version header is NOT PRESENT', async function () {
|
|
await agentAdminAPI
|
|
.get('site/')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
site: {
|
|
version: stringMatching(/\d+\.\d+/)
|
|
}
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
});
|
|
});
|
|
|
|
it('responds with current content version header when requested version is BEHIND current version and CAN respond', async function () {
|
|
await agentAdminAPI
|
|
.get('site/')
|
|
.header('Accept-Version', 'v3.0')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
site: {
|
|
version: stringMatching(/\d+\.\d+/)
|
|
}
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyString,
|
|
'content-version': stringMatching(/v\d+\.\d+/)
|
|
});
|
|
});
|
|
|
|
it('responds with current content version header when requested version is AHEAD and CAN respond', async function () {
|
|
await agentAdminAPI
|
|
.get('site/')
|
|
.header('Accept-Version', 'v999.5')
|
|
.expectStatus(200)
|
|
.matchBodySnapshot({
|
|
site: {
|
|
version: stringMatching(/\d+\.\d+/)
|
|
}
|
|
})
|
|
.matchHeaderSnapshot({
|
|
etag: anyString,
|
|
'content-version': stringMatching(/v\d+\.\d+/)
|
|
});
|
|
});
|
|
|
|
it('responds with error requested version is AHEAD and CANNOT respond', async function () {
|
|
// CASE 2: If accept-version is behind, send a 406 & tell them the client needs updating.
|
|
await agentAdminAPI
|
|
.get('removed_endpoint')
|
|
.header('Accept-Version', 'v999.1')
|
|
.expectStatus(406)
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
context: stringMatching(/Provided client version v999\.1 is ahead of current Ghost instance version v\d+\.\d+/),
|
|
id: anyErrorId
|
|
}]
|
|
});
|
|
});
|
|
|
|
it('responds with error when requested version is BEHIND and CANNOT respond', async function () {
|
|
// CASE 2: If accept-version is behind, send a 406 & tell them the client needs updating.
|
|
await agentAdminAPI
|
|
.get('removed_endpoint')
|
|
.header('Accept-Version', 'v3.1')
|
|
.header('User-Agent', 'Zapier 1.3')
|
|
.expectStatus(406)
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
context: stringMatching(/Provided client version v3.1 is outdated and is behind current Ghost version v\d+\.\d+/),
|
|
id: anyErrorId
|
|
}]
|
|
});
|
|
|
|
// temporarily disable email sending
|
|
mockManager.assert.sentEmailCount(0);
|
|
// mockManager.assert.sentEmail({
|
|
// subject: 'Attention required: Your Zapier 1.3 integration has failed',
|
|
// to: 'jbloggs@example.com'
|
|
// });
|
|
});
|
|
|
|
it('responds with error and sends email ONCE when requested version is BEHIND and CANNOT respond multiple times', async function () {
|
|
await agentAdminAPI
|
|
.get('removed_endpoint')
|
|
.header('Accept-Version', 'v3.5')
|
|
.header('User-Agent', 'Zapier 1.4')
|
|
.expectStatus(406)
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
context: stringMatching(/Provided client version v3.5 is outdated and is behind current Ghost version v\d+\.\d+/),
|
|
id: anyErrorId
|
|
}]
|
|
});
|
|
|
|
// temporarily disable email sending
|
|
mockManager.assert.sentEmailCount(0);
|
|
// mockManager.assert.sentEmail({
|
|
// subject: 'Attention required: Your Zapier 1.4 integration has failed',
|
|
// to: 'jbloggs@example.com'
|
|
// });
|
|
|
|
await agentAdminAPI
|
|
.get('removed_endpoint')
|
|
.header('Accept-Version', 'v3.5')
|
|
.header('User-Agent', 'Zapier 1.4')
|
|
.expectStatus(406)
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
context: stringMatching(/Provided client version v3.5 is outdated and is behind current Ghost version v\d+\.\d+/),
|
|
id: anyErrorId
|
|
}]
|
|
});
|
|
|
|
// temporarily disable email sending
|
|
mockManager.assert.sentEmailCount(0);
|
|
});
|
|
|
|
it('responds with 404 error when the resource cannot be found', async function () {
|
|
await agentAdminAPI
|
|
.get('/members/member_does_not_exist@example.com')
|
|
.header('Accept-Version', 'v4.1')
|
|
.expectStatus(404)
|
|
.matchHeaderSnapshot({
|
|
etag: anyString
|
|
})
|
|
.matchBodySnapshot({
|
|
errors: [{
|
|
id: anyErrorId
|
|
}]
|
|
});
|
|
});
|
|
|
|
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 () {
|
|
let agentContentAPI;
|
|
|
|
before(async function () {
|
|
agentContentAPI = await agentProvider.getContentAPIAgent();
|
|
await fixtureManager.init('api_keys');
|
|
agentContentAPI.authenticate();
|
|
});
|
|
|
|
it('responds with no content version header when accept version header is NOT PRESENT', async function () {
|
|
await agentContentAPI.get('settings/')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot()
|
|
.matchBodySnapshot();
|
|
});
|
|
|
|
it('responds with current content version header when requested version is BEHIND current version and CAN respond', async function () {
|
|
await agentContentAPI.get('settings/')
|
|
.header('Accept-Version', 'v3.0')
|
|
.expectStatus(200)
|
|
.matchHeaderSnapshot({
|
|
'content-version': stringMatching(/v\d+\.\d+/)
|
|
})
|
|
.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();
|
|
});
|
|
});
|
|
});
|