From 340192c5da746d018231f820c170d88235c114a4 Mon Sep 17 00:00:00 2001 From: Fabian Becker Date: Tue, 3 Jun 2014 22:38:15 +0000 Subject: [PATCH] Cache invalidation for post update closes #2833 - Handle status change of post --- core/server/api/index.js | 15 +-- core/server/api/posts.js | 3 +- core/test/functional/routes/api/posts_test.js | 100 ++++++++++++++++-- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/core/server/api/index.js b/core/server/api/index.js index e16736631e..8cfa057c3e 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -55,24 +55,25 @@ cacheInvalidationHeader = function (req, result) { cacheInvalidate, jsonResult = result.toJSON ? result.toJSON() : result, post, - wasPublished, - wasDeleted; + hasStatusChanged, + wasDeleted, + wasPublishedUpdated; if (method === 'POST' || method === 'PUT' || method === 'DELETE') { if (endpoint === 'settings' || endpoint === 'users' || endpoint === 'db') { cacheInvalidate = '/*'; } else if (endpoint === 'posts') { post = jsonResult.posts[0]; - wasPublished = post.statusChanged && post.status === 'published'; + hasStatusChanged = post.statusChanged; wasDeleted = method === 'DELETE'; + // Invalidate cache when post was updated but not when post is draft + wasPublishedUpdated = method === 'PUT' && post.status === 'published'; // Remove the statusChanged value from the response - if (post.statusChanged) { - delete post.statusChanged; - } + delete post.statusChanged; // Don't set x-cache-invalidate header for drafts - if (wasPublished || wasDeleted) { + if (hasStatusChanged || wasDeleted || wasPublishedUpdated) { cacheInvalidate = '/, /page/*, /rss/, /rss/*, /tag/*'; if (id && post.slug) { return config.urlForPost(settings, post).then(function (postUrl) { diff --git a/core/server/api/posts.js b/core/server/api/posts.js index 3f31dd13d0..8682c214ce 100644 --- a/core/server/api/posts.js +++ b/core/server/api/posts.js @@ -114,7 +114,8 @@ posts = { if (result) { var post = result.toJSON(); - // If previously was not published and now is, signal the change + // If previously was not published and now is (or vice versa), signal the change + post.statusChanged = false; if (result.updated('status') !== result.get('status')) { post.statusChanged = true; } diff --git a/core/test/functional/routes/api/posts_test.js b/core/test/functional/routes/api/posts_test.js index 8686c62786..fce2095281 100644 --- a/core/test/functional/routes/api/posts_test.js +++ b/core/test/functional/routes/api/posts_test.js @@ -411,7 +411,8 @@ describe('Post API', function () { } var updatedPost = res.body; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + // Require cache invalidation when post was updated and published + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; updatedPost.should.exist; @@ -458,7 +459,7 @@ describe('Post API', function () { } var putBody = res.body; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; putBody.should.exist; putBody.posts[0].title.should.eql(changedValue); @@ -469,6 +470,93 @@ describe('Post API', function () { }); }); + it('can edit a new draft and update post', function (done) { + var newTitle = 'My Post', + newTagName = 'My Tag', + publishedState = 'published', + newTag = {id: null, name: newTagName}, + newPost = {posts: [{status: 'draft', title: newTitle, markdown: 'my post', tags: [newTag]}]}; + + request.post(testUtils.API.getApiQuery('posts/?include=tags')) + .set('X-CSRF-Token', csrfToken) + .send(newPost) + .expect(201) + .end(function (err, res) { + if (err) { + return done(err); + } + + res.should.be.json; + var draftPost = res.body; + res.headers['location'].should.equal('/ghost/api/v0.1/posts/' + draftPost.posts[0].id + '/?status=draft'); + draftPost.posts.should.exist; + draftPost.posts.length.should.be.above(0); + draftPost.posts[0].title.should.eql(newTitle); + testUtils.API.checkResponse(draftPost.posts[0], 'post'); + + draftPost.posts[0].title = 'Vote for Casper in red'; + + request.put(testUtils.API.getApiQuery('posts/' + draftPost.posts[0].id + '/?include=tags')) + .set('X-CSRF-Token', csrfToken) + .send(draftPost) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + // Updating a draft should not send x-cache-invalidate headers + _.has(res.headers, 'x-cache-invalidate').should.equal(false); + done(); + }); + }); + }); + + it('can edit a new published post and unpublish', function (done) { + var newTitle = 'My Post', + newTagName = 'My Tag', + draftState = 'draft', + newTag = {id: null, name: newTagName}, + newPost = {posts: [{status: 'published', title: newTitle, markdown: 'my post', tags: [newTag]}]}; + + request.post(testUtils.API.getApiQuery('posts/?include=tags')) + .set('X-CSRF-Token', csrfToken) + .send(newPost) + .expect(201) + .end(function (err, res) { + if (err) { + return done(err); + } + + res.should.be.json; + var draftPost = res.body; + res.headers['location'].should.equal('/ghost/api/v0.1/posts/' + draftPost.posts[0].id + '/?status=published'); + draftPost.posts.should.exist; + draftPost.posts.length.should.be.above(0); + draftPost.posts[0].title.should.eql(newTitle); + testUtils.API.checkResponse(draftPost.posts[0], 'post'); + + draftPost.posts[0].title = 'Vote for Casper in red'; + draftPost.posts[0].status = draftState; + + request.put(testUtils.API.getApiQuery('posts/' + draftPost.posts[0].id + '/?include=tags')) + .set('X-CSRF-Token', csrfToken) + .send(draftPost) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + + var unpublishedPost = res.body; + // Unpublishing a post should send x-cache-invalidate headers + _.has(res.headers, 'x-cache-invalidate').should.equal(true); + done(); + }); + }); + }); + it('can change a post to a static page', function (done) { request.get(testUtils.API.getApiQuery('posts/1/?include=tags')) .end(function (err, res) { @@ -492,7 +580,7 @@ describe('Post API', function () { } var putBody = res.body; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; putBody.should.exist; putBody.posts[0].page.should.eql(changedValue); @@ -527,7 +615,7 @@ describe('Post API', function () { var putBody = res.body; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; putBody.should.exist; putBody.posts[0].page.should.eql(changedValue); @@ -615,7 +703,7 @@ describe('Post API', function () { } var putBody = res.body; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; putBody.should.exist; putBody.posts.should.exist; @@ -843,7 +931,7 @@ describe('Post API', function () { yyyy = today.getFullYear(), postLink = '/' + yyyy + '/' + mm + '/' + dd + '/' + putBody.posts[0].slug + '/'; - _.has(res.headers, 'x-cache-invalidate').should.equal(false); + _.has(res.headers, 'x-cache-invalidate').should.equal(true); res.should.be.json; putBody.should.exist; putBody.posts[0].title.should.eql(changedValue);