-
-
Notifications
You must be signed in to change notification settings - Fork 228
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(export): add export command (#1050)
refs #468 - add export command & export taks
- Loading branch information
Showing
6 changed files
with
407 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const Command = require('../command'); | ||
|
||
class ExportCommand extends Command { | ||
async run(argv) { | ||
const {exportTask} = require('../tasks/import'); | ||
const {SystemError} = require('../errors'); | ||
|
||
const instance = this.system.getInstance(); | ||
const isRunning = await instance.isRunning(); | ||
|
||
if (!isRunning) { | ||
const shouldStart = await this.ui.confirm('Ghost instance is not currently running. Would you like to start it?', true); | ||
|
||
if (!shouldStart) { | ||
throw new SystemError('Ghost instance is not currently running'); | ||
} | ||
|
||
instance.checkEnvironment(); | ||
await this.ui.run(() => instance.start(), 'Starting Ghost'); | ||
} | ||
|
||
await this.ui.run(() => exportTask(this.ui, instance, argv.file), 'Exporting content'); | ||
this.ui.log(`Content exported to ${argv.file}`, 'green'); | ||
} | ||
} | ||
|
||
ExportCommand.description = 'Export content from a blog'; | ||
ExportCommand.params = '[file]'; | ||
|
||
module.exports = ExportCommand; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
const {importTask} = require('./tasks'); | ||
const {importTask, exportTask} = require('./tasks'); | ||
const parseExport = require('./parse-export'); | ||
|
||
module.exports = { | ||
importTask, | ||
exportTask, | ||
parseExport | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
const {expect} = require('chai'); | ||
const sinon = require('sinon'); | ||
const proxyquire = require('proxyquire').noCallThru(); | ||
|
||
const {SystemError} = require('../../../lib/errors'); | ||
|
||
const modulePath = '../../../lib/commands/export'; | ||
|
||
describe('Unit: Commands > export', function () { | ||
it('runs export task if instance is running', async function () { | ||
const exportTask = sinon.stub().resolves(); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(true) | ||
}; | ||
const ui = { | ||
run: sinon.stub().callsFake(fn => fn()), | ||
log: sinon.stub() | ||
}; | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const ExportCommand = proxyquire(modulePath, {'../tasks/import': {exportTask}}); | ||
const cmd = new ExportCommand(ui, {getInstance}); | ||
|
||
await cmd.run({file: 'test-output.json'}); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(ui.run.calledOnce).to.be.true; | ||
expect(exportTask.calledOnceWithExactly(ui, instance, 'test-output.json')).to.be.true; | ||
expect(ui.log.calledOnce).to.be.true; | ||
}); | ||
|
||
it('prompts to start if not running and throws if not confirmed', async function () { | ||
const exportTask = sinon.stub().resolves(); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(false) | ||
}; | ||
const ui = { | ||
confirm: sinon.stub().resolves(false), | ||
run: sinon.stub().callsFake(fn => fn()), | ||
log: sinon.stub() | ||
}; | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const ExportCommand = proxyquire(modulePath, {'../tasks/import': {exportTask}}); | ||
const cmd = new ExportCommand(ui, {getInstance}); | ||
|
||
try { | ||
await cmd.run({file: 'test-output.json'}); | ||
} catch (error) { | ||
expect(error).to.be.an.instanceof(SystemError); | ||
expect(error.message).to.include('not currently running'); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(ui.confirm.calledOnce).to.be.true; | ||
expect(ui.run.called).to.be.false; | ||
expect(exportTask.called).to.be.false; | ||
expect(ui.log.called).to.be.false; | ||
return; | ||
} | ||
|
||
expect.fail('run should have errored'); | ||
}); | ||
|
||
it('prompts to start if not running and starts if confirmed', async function () { | ||
const exportTask = sinon.stub().resolves(); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(false), | ||
start: sinon.stub().resolves(), | ||
checkEnvironment: sinon.stub() | ||
}; | ||
const ui = { | ||
confirm: sinon.stub().resolves(true), | ||
run: sinon.stub().callsFake(fn => fn()), | ||
log: sinon.stub() | ||
}; | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const ExportCommand = proxyquire(modulePath, {'../tasks/import': {exportTask}}); | ||
const cmd = new ExportCommand(ui, {getInstance}); | ||
|
||
await cmd.run({file: 'test-output.json'}); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(ui.confirm.calledOnce).to.be.true; | ||
expect(instance.checkEnvironment.calledOnce).to.be.true; | ||
expect(ui.run.calledTwice).to.be.true; | ||
expect(instance.start.calledOnce).to.be.true; | ||
expect(exportTask.calledOnceWithExactly(ui, instance, 'test-output.json')).to.be.true; | ||
expect(ui.log.calledOnce).to.be.true; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
const {expect} = require('chai'); | ||
const sinon = require('sinon'); | ||
const proxyquire = require('proxyquire').noCallThru(); | ||
|
||
const {SystemError} = require('../../../lib/errors'); | ||
|
||
const modulePath = '../../../lib/commands/import'; | ||
|
||
describe('Unit: Commands > import', function () { | ||
it('throws error if importing a 0.x import into a > 1.x blog', async function () { | ||
const parseExport = sinon.stub().returns({version: '0.11.14'}); | ||
const ImportCommand = proxyquire(modulePath, {'../tasks/import': {parseExport}}); | ||
const getInstance = sinon.stub().returns({version: '3.0.0'}); | ||
|
||
const cmd = new ImportCommand({}, {getInstance}); | ||
|
||
try { | ||
await cmd.run({file: 'test-output.json'}); | ||
} catch (error) { | ||
expect(error).to.be.an.instanceof(SystemError); | ||
expect(error.message).to.include('can only be imported by Ghost v1.x versions'); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(parseExport.calledOnceWithExactly('test-output.json')).to.be.true; | ||
return; | ||
} | ||
|
||
expect.fail('expected run to error'); | ||
}); | ||
|
||
it('runs import task from v0.x to 1.x if blog is running', async function () { | ||
const parseExport = sinon.stub().returns({version: '0.11.14'}); | ||
const run = sinon.stub().resolves(); | ||
const importTask = sinon.stub().resolves({run}); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(true), | ||
version: '1.0.0' | ||
}; | ||
|
||
const ImportCommand = proxyquire(modulePath, {'../tasks/import': {parseExport, importTask}}); | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const cmd = new ImportCommand({ui: true}, {getInstance}); | ||
|
||
await cmd.run({file: 'test-output.json'}); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(parseExport.calledOnceWithExactly('test-output.json')).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(importTask.calledOnceWithExactly({ui: true}, instance, 'test-output.json')).to.be.true; | ||
expect(run.calledOnce).to.be.true; | ||
}); | ||
|
||
it('runs import task from v1.x to any', async function () { | ||
const parseExport = sinon.stub().returns({version: '1.0.0'}); | ||
const run = sinon.stub().resolves(); | ||
const importTask = sinon.stub().resolves({run}); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(true), | ||
version: '3.0.0' | ||
}; | ||
|
||
const ImportCommand = proxyquire(modulePath, {'../tasks/import': {parseExport, importTask}}); | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const cmd = new ImportCommand({ui: true}, {getInstance}); | ||
|
||
await cmd.run({file: 'test-output.json'}); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(parseExport.calledOnceWithExactly('test-output.json')).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(importTask.calledOnceWithExactly({ui: true}, instance, 'test-output.json')).to.be.true; | ||
expect(run.calledOnce).to.be.true; | ||
}); | ||
|
||
it('prompts to start if not running and throws if not confirmed', async function () { | ||
const parseExport = sinon.stub().returns({version: '1.0.0'}); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(false), | ||
version: '3.0.0' | ||
}; | ||
const confirm = sinon.stub().resolves(false); | ||
|
||
const ImportCommand = proxyquire(modulePath, {'../tasks/import': {parseExport}}); | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const cmd = new ImportCommand({confirm}, {getInstance}); | ||
|
||
try { | ||
await cmd.run({file: 'test-output.json'}); | ||
} catch (error) { | ||
expect(error).to.be.an.instanceof(SystemError); | ||
expect(error.message).to.include('not currently running'); | ||
expect(getInstance.calledOnce).to.be.true; | ||
expect(parseExport.calledOnceWithExactly('test-output.json')).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(confirm.calledOnce).to.be.true; | ||
return; | ||
} | ||
|
||
expect.fail('run should have errored'); | ||
}); | ||
|
||
it('prompts to start if not running and starts if confirmed', async function () { | ||
const parseExport = sinon.stub().returns({version: '1.0.0'}); | ||
const runImport = sinon.stub().resolves(); | ||
const importTask = sinon.stub().resolves({run: runImport}); | ||
const instance = { | ||
isRunning: sinon.stub().resolves(false), | ||
checkEnvironment: sinon.stub(), | ||
start: sinon.stub().resolves(), | ||
version: '3.0.0' | ||
}; | ||
const confirm = sinon.stub().resolves(true); | ||
const run = sinon.stub().callsFake(fn => fn()); | ||
|
||
const ImportCommand = proxyquire(modulePath, {'../tasks/import': {parseExport, importTask}}); | ||
const getInstance = sinon.stub().returns(instance); | ||
|
||
const cmd = new ImportCommand({confirm, run}, {getInstance}); | ||
|
||
await cmd.run({file: 'test-output.json'}); | ||
|
||
expect(getInstance.calledOnce).to.be.true; | ||
expect(parseExport.calledOnceWithExactly('test-output.json')).to.be.true; | ||
expect(instance.isRunning.calledOnce).to.be.true; | ||
expect(confirm.calledOnce).to.be.true; | ||
expect(instance.checkEnvironment.calledOnce).to.be.true; | ||
expect(run.calledOnce).to.be.true; | ||
expect(instance.start.calledOnce).to.be.true; | ||
expect(importTask.calledOnceWithExactly({confirm, run}, instance, 'test-output.json')).to.be.true; | ||
expect(runImport.calledOnce).to.be.true; | ||
}); | ||
}); |
Oops, something went wrong.