mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-18 02:21:47 -05:00
Merge pull request #2706 from appleYaks/db-api-update
DB API returns JSON-API compatible objects. Export triggers 'Save As' di...
This commit is contained in:
commit
1c3ba536c9
4 changed files with 133 additions and 9 deletions
|
@ -20,7 +20,9 @@ db = {
|
|||
|
||||
// Export data, otherwise send error 500
|
||||
return canThis(self.user).exportContent.db().then(function () {
|
||||
return dataExport().otherwise(function (error) {
|
||||
return dataExport().then(function (exportedData) {
|
||||
return when.resolve({ db: [exportedData] });
|
||||
}).otherwise(function (error) {
|
||||
return when.reject({type: 'InternalServerError', message: error.message || error});
|
||||
});
|
||||
}, function () {
|
||||
|
@ -46,7 +48,7 @@ db = {
|
|||
|
||||
return api.settings.read.call({ internal: true }, { key: 'databaseVersion' }).then(function (response) {
|
||||
var setting = response.settings[0];
|
||||
|
||||
|
||||
return when(setting.value);
|
||||
}, function () {
|
||||
return when('002');
|
||||
|
@ -90,7 +92,7 @@ db = {
|
|||
}).then(function importSuccess() {
|
||||
return api.settings.updateSettingsCache();
|
||||
}).then(function () {
|
||||
return when.resolve({message: 'Posts, tags and other data successfully imported'});
|
||||
return when.resolve({ db: [] });
|
||||
}).otherwise(function importFailure(error) {
|
||||
return when.reject({type: 'InternalServerError', message: error.message || error});
|
||||
}).finally(function () {
|
||||
|
@ -107,7 +109,7 @@ db = {
|
|||
return canThis(self.user).deleteAllContent.db().then(function () {
|
||||
return when(dataProvider.deleteAllContent())
|
||||
.then(function () {
|
||||
return when.resolve({message: 'Successfully deleted all content from your blog.'});
|
||||
return when.resolve({ db: [] });
|
||||
}, function (error) {
|
||||
return when.reject({message: error.message || error});
|
||||
});
|
||||
|
|
|
@ -109,6 +109,22 @@ function locationHeader(req, result) {
|
|||
return when(location);
|
||||
}
|
||||
|
||||
// create a header that invokes the 'Save As' dialog
|
||||
// in the browser when exporting the database to file.
|
||||
// The 'filename' parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
|
||||
//
|
||||
// for encoding whitespace and non-ISO-8859-1 characters, you MUST
|
||||
// use the "filename*=" attribute, NOT "filename=". Ideally, both.
|
||||
// see: http://tools.ietf.org/html/rfc598
|
||||
// examples: http://tools.ietf.org/html/rfc6266#section-5
|
||||
//
|
||||
// we'll use ISO-8859-1 characters here to keep it simple.
|
||||
function dbExportSaveAsHeader() {
|
||||
// replace ':' with '_' for OS that don't support it
|
||||
var now = (new Date()).toJSON().replace(/:/g, '_');
|
||||
return 'Attachment; filename="ghost-' + now + '.json"';
|
||||
}
|
||||
|
||||
// ### 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
|
||||
|
@ -127,6 +143,13 @@ requestHandler = function (apiMethod) {
|
|||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
if (apiMethod === db.exportContent) {
|
||||
res.set({
|
||||
"Content-Disposition": dbExportSaveAsHeader()
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return locationHeader(req, result).then(function (header) {
|
||||
if (header) {
|
||||
|
@ -134,7 +157,7 @@ requestHandler = function (apiMethod) {
|
|||
'Location': header
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
res.json(result || {});
|
||||
});
|
||||
});
|
||||
|
@ -148,10 +171,10 @@ requestHandler = function (apiMethod) {
|
|||
|
||||
_.each(error, function (erroritem) {
|
||||
var errorContent = {};
|
||||
|
||||
|
||||
//TODO: add logic to set the correct status code
|
||||
errorCode = errorTypes[erroritem.type].code || 500;
|
||||
|
||||
|
||||
errorContent['message'] = _.isString(erroritem) ? erroritem : (_.isObject(erroritem) ? erroritem.message : 'Unknown API Error');
|
||||
errorContent['type'] = erroritem.type || 'InternalServerError';
|
||||
errors.push(errorContent);
|
||||
|
|
98
core/test/functional/routes/api/db_test.js
Normal file
98
core/test/functional/routes/api/db_test.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*global describe, it, before, after */
|
||||
var supertest = require('supertest'),
|
||||
express = require('express'),
|
||||
should = require('should'),
|
||||
testUtils = require('../../../utils'),
|
||||
|
||||
ghost = require('../../../../../core'),
|
||||
|
||||
httpServer,
|
||||
request;
|
||||
|
||||
|
||||
describe('DB 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(app);
|
||||
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];
|
||||
|
||||
process.nextTick(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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}).catch(done);
|
||||
}).catch(function (e) {
|
||||
console.log('Ghost Error: ', e);
|
||||
console.log(e.stack);
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
httpServer.close();
|
||||
});
|
||||
|
||||
it('attaches the Content-Disposition header on export', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('db/'))
|
||||
.expect(200)
|
||||
.expect('Content-Disposition', /Attachment; filename="[A-Za-z0-9._-]+\.json"/)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.db);
|
||||
jsonResponse.db.should.have.length(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -39,8 +39,9 @@ describe('DB API', function () {
|
|||
permissions.init().then(function () {
|
||||
return dbAPI.deleteAllContent.call({user: 1});
|
||||
}).then(function (result) {
|
||||
should.exist(result.message);
|
||||
result.message.should.equal('Successfully deleted all content from your blog.');
|
||||
should.exist(result.db);
|
||||
result.db.should.be.instanceof(Array);
|
||||
result.db.should.be.empty;
|
||||
}).then(function () {
|
||||
TagsAPI.browse().then(function (results) {
|
||||
should.exist(results);
|
||||
|
|
Loading…
Add table
Reference in a new issue