From b69b9780a95f4bf417388730db8986998c9ba7cb Mon Sep 17 00:00:00 2001 From: David Wolfe Date: Thu, 11 Jan 2018 15:03:21 +0000 Subject: [PATCH] Backup endpoint improvements (#9365) closes #9297 - backup endpoint returns JSON - allows setting of export filename - DRY up code - the endpoint is not documented, no breaking change --- core/server/api/db.js | 32 ++++++++++------------ core/server/data/db/backup.js | 1 + core/server/data/export/index.js | 7 +++++ core/test/functional/routes/api/db_spec.js | 23 +++++++++++++++- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/core/server/api/db.js b/core/server/api/db.js index f1ffe60250..2c98625eff 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -1,17 +1,13 @@ // # DB API // API for DB operations var Promise = require('bluebird'), - path = require('path'), - fs = require('fs-extra'), pipeline = require('../lib/promise/pipeline'), localUtils = require('./utils'), exporter = require('../data/export'), importer = require('../data/importer'), backupDatabase = require('../data/db/backup'), models = require('../models'), - config = require('../config'), common = require('../lib/common'), - urlService = require('../services/url'), docName = 'db', db; @@ -23,26 +19,26 @@ var Promise = require('bluebird'), db = { /** * ### Archive Content - * Generate the JSON to export - for Moya only + * Generate the JSON to export * * @public * @returns {Promise} Ghost Export JSON format */ - backupContent: function () { - var props = { - data: exporter.doExport(), - filename: exporter.fileName() - }; + backupContent: function (options) { + var tasks; - return Promise.props(props) - .then(function successMessage(exportResult) { - var filename = path.resolve(urlService.utils.urlJoin(config.get('paths').contentPath, 'data', exportResult.filename)); + options = options || {}; - return fs.writeFile(filename, JSON.stringify(exportResult.data)) - .then(function () { - return filename; - }); - }); + function jsonResponse(filename) { + return {db: [{filename: filename}]}; + } + + tasks = [ + backupDatabase, + jsonResponse + ]; + + return pipeline(tasks, options); }, /** * ### Export Content diff --git a/core/server/data/db/backup.js b/core/server/data/db/backup.js index 8a377c5033..f6821190f0 100644 --- a/core/server/data/db/backup.js +++ b/core/server/data/db/backup.js @@ -35,6 +35,7 @@ backup = function backup(options) { .then(writeExportFile) .then(function successMessage(filename) { common.logging.info('Database backup written to: ' + filename); + return filename; }); }; diff --git a/core/server/data/export/index.js b/core/server/data/export/index.js index abfa51448a..300ea7a40b 100644 --- a/core/server/data/export/index.js +++ b/core/server/data/export/index.js @@ -21,6 +21,13 @@ exportFileName = function exportFileName(options) { var datetime = (new Date()).toJSON().substring(0, 10), title = ''; + options = options || {}; + + // custom filename + if (options.filename) { + return Promise.resolve(options.filename + '.json'); + } + return models.Settings.findOne({key: 'title'}, _.merge({}, modelOptions, options)).then(function (result) { if (result) { title = security.string.safe(result.get('value')) + '.'; diff --git a/core/test/functional/routes/api/db_spec.js b/core/test/functional/routes/api/db_spec.js index c8de36b060..fe5cd221f4 100644 --- a/core/test/functional/routes/api/db_spec.js +++ b/core/test/functional/routes/api/db_spec.js @@ -116,7 +116,28 @@ describe('DB API', function () { if (err) { return done(err); } - res.body.should.match(/data/); + + (typeof res.body).should.be.Object; + should.exist(res.body.db[0].filename); + fsStub.calledOnce.should.eql(true); + + done(); + }); + }); + + it('export can be triggered and named by backup client', function (done) { + backupQuery = '?client_id=' + backupClient.slug + '&client_secret=' + backupClient.secret + '&filename=test'; + fsStub = sandbox.stub(fs, 'writeFile').resolves(); + request.post(testUtils.API.getApiQuery('db/backup' + backupQuery)) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + (typeof res.body).should.be.Object; + res.body.db[0].filename.should.match(/test\.json/); fsStub.calledOnce.should.eql(true); done();