0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Merge pull request #2677 from jaswilli/issue-2635

Add location header to response when new object is created
This commit is contained in:
Hannah Wolfe 2014-05-04 16:05:54 +01:00
commit acd71d423e
5 changed files with 218 additions and 3 deletions

View file

@ -55,6 +55,33 @@ function cacheInvalidationHeader(req, result) {
return when(cacheInvalidate);
}
// if api request results in the creation of a new object, construct
// a Location: header that points to the new resource.
//
// arguments: request object, result object from the api call
// returns: a promise that will be fulfilled with the location of the
// resource
function locationHeader(req, result) {
var apiRoot = config.urlFor('api'),
location,
post,
notification,
parsedUrl = req._parsedUrl.pathname.replace(/\/$/, '').split('/'),
endpoint = parsedUrl[4];
if (req.method === 'POST') {
if (result.hasOwnProperty('posts')) {
post = result.posts[0];
location = apiRoot + '/posts/' + post.id + '/?status=' + post.status;
} else if (endpoint === 'notifications') {
notification = result;
location = apiRoot + '/notifications/' + notification.id;
}
}
return when(location);
}
// ### requestHandler
// decorator for api functions which are called via an HTTP request
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
@ -72,7 +99,17 @@ requestHandler = function (apiMethod) {
"X-Cache-Invalidate": header
});
}
res.json(result || {});
})
.then(function () {
return locationHeader(req, result).then(function (header) {
if (header) {
res.set({
'Location': header
});
}
res.json(result || {});
});
});
}, function (error) {
var errorCode = error.code || 500,

View file

@ -40,7 +40,8 @@ notifications = {
// ```
add: function add(notification) {
// **returns:** a promise for all notifications as a json object
return when(notificationsStore.push(notification));
notificationsStore.push(notification);
return when(notification);
}
};

View file

@ -103,7 +103,13 @@ function urlFor(context, data, absolute) {
var urlPath = '/',
secure,
knownObjects = ['post', 'tag', 'user'],
knownPaths = {'home': '/', 'rss': '/rss/'}; // this will become really big
// this will become really big
knownPaths = {
'home': '/',
'rss': '/rss/',
'api': '/ghost/api/v0.1'
};
// Make data properly optional
if (_.isBoolean(data)) {

View file

@ -0,0 +1,170 @@
var supertest = require('supertest'),
express = require('express'),
should = require('should'),
_ = require('lodash'),
testUtils = require('../../../utils'),
ghost = require('../../../../../core'),
httpServer,
request,
agent;
describe('Notifications API', function () {
var user = testUtils.DataGenerator.forModel.users[0],
csrfToken = '';
before(function (done) {
var app = express();
ghost({app: app}).then(function (_httpServer) {
httpServer = _httpServer;
request = supertest.agent(app);
testUtils.clearData()
.then(function () {
return testUtils.initData();
})
.then(function () {
return testUtils.insertDefaultFixtures();
})
.then(function () {
request.get('/ghost/signin/')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
pattern_meta.should.exist;
csrfToken = res.text.match(pattern_meta)[1];
setTimeout(function () {
request.post('/ghost/signin/')
.set('X-CSRF-Token', csrfToken)
.send({email: user.email, password: user.password})
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
request.saveCookies(res);
request.get('/ghost/')
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
csrfToken = res.text.match(pattern_meta)[1];
done();
});
});
}, 2000);
});
}, done);
}).otherwise(function (e) {
console.log('Ghost Error: ', e);
console.log(e.stack);
});
});
after(function () {
httpServer.close();
});
describe('Add', function () {
var newNotification = {
type: 'info',
message: 'test notification',
status: 'persistent',
id: 'add-test-1'
};
it('creates a new notification', function (done) {
request.post(testUtils.API.getApiQuery('notifications/'))
.set('X-CSRF-Token', csrfToken)
.send(newNotification)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
res.headers['location'].should.equal('/ghost/api/v0.1/notifications/' + newNotification.id);
var jsonResponse = res.body;
testUtils.API.checkResponse(jsonResponse, 'notification');
jsonResponse.type.should.equal(newNotification.type);
jsonResponse.message.should.equal(newNotification.message);
jsonResponse.status.should.equal(newNotification.status);
jsonResponse.id.should.equal(newNotification.id);
done();
});
});
});
describe('Delete', function () {
var newNotification = {
type: 'info',
message: 'test notification',
status: 'persistent',
id: 'delete-test-1'
};
it('deletes a notification', function (done) {
// create the notification that is to be deleted
request.post(testUtils.API.getApiQuery('notifications/'))
.set('X-CSRF-Token', csrfToken)
.send(newNotification)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var location = res.headers['location'];
location.should.equal('/ghost/api/v0.1/notifications/' + newNotification.id);
var jsonResponse = res.body;
testUtils.API.checkResponse(jsonResponse, 'notification');
jsonResponse.type.should.equal(newNotification.type);
jsonResponse.message.should.equal(newNotification.message);
jsonResponse.status.should.equal(newNotification.status);
jsonResponse.id.should.equal(newNotification.id);
// begin delete test
request.del(location)
.set('X-CSRF-Token', csrfToken)
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
// a delete returns a JSON object containing all notifications
// so we can make sure the notification we just deleted isn't
// included
var notifications = res.body;
var success;
notifications.forEach(function (n) {
success = n.id !== newNotification.id;
});
success.should.be.true;
done();
});
});
});
});
});

View file

@ -334,6 +334,7 @@ describe('Post API', function () {
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);