Skip to content

Commit

Permalink
Exit, error handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
ViShell committed Mar 22, 2020
1 parent e07966d commit 9de4e6f
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 74 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "console-command-manager",
"version": "0.0.5",
"version": "0.0.7",
"description": "Console command manager. It helps run custom commands from bash, is able to control your service from console like the think client does. String parser command manager and console runner are main parts of the project.",
"main": "src/index.js",
"types": "dts/index.d.ts",
Expand Down
65 changes: 33 additions & 32 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const {parse, parseCommand, tokensToCommand, extractValue} = require('./parser')
const {Manager, EVENTS} = require('./manager')
const readline = require('readline')
const EventEmmiter = require('events')

const commands = [
// List commands.
Expand Down Expand Up @@ -29,8 +28,9 @@ const commands = [
{
name: 'exit',
title: 'It terminates the application.',
handler: ({injection: {console}}) => {
handler: ({injection: {console, readLine}}) => {
console.log('See you.')
readLine.close()

// To break the input cycle return false.
return false
Expand All @@ -40,7 +40,6 @@ const commands = [

class CLI {
constructor(manager, readline, greetings) {
this.events = new EventEmmiter()
this.manager = manager
this.readline = readline
this.greetings = greetings
Expand All @@ -51,7 +50,6 @@ class CLI {
// Cycled command processing.
const command = parse(input)
const toContinue = await this.manager.execute(command)
this.events.emit(EVENTS.commandExecuted, command)

// If termination signal is received the readline stream is closed. The application is closed.
if (toContinue === false) {
Expand All @@ -60,7 +58,7 @@ class CLI {
}
}

function runCLI(greetings, _commands, injection = null, readLine = null) {
function bootstrapCommandManager( _commands, injection = null) {
const _injection = {
console
}
Expand All @@ -77,12 +75,38 @@ function runCLI(greetings, _commands, injection = null, readLine = null) {
injection = _injection
}
const manager = new Manager(allCommands, injection)
manager.events.on(EVENTS.error, ({e, request}) => {
const { console } = injection
console.log(e.toString())
const stack = extractValue(request.args, 'stack', false)
if (stack) {
console.log(e.stack)
}
})

return manager
}

function runCLI(greetings, _commands, injection = null, readLine = null) {
if (!readLine) {
readLine = readline.createInterface({
input: process.stdin,
output: process.stdout
})
}
if (injection) {
injection = {
...injection,
readLine
}
}

const manager = bootstrapCommandManager(_commands, injection)
manager.events.on(EVENTS.executed, ({ request, result }) => {
if (request.name == 'exit' && !!result) {
cli.readline.close()
}
})
const cli = new CLI(manager, readLine, greetings)
const {console: _console} = injection
_console.log(greetings)
Expand All @@ -91,38 +115,15 @@ function runCLI(greetings, _commands, injection = null, readLine = null) {
}

function runCommand(_commands, injection = null, tokens) {
const _injection = {
console
}
const allCommands = [
...commands,
..._commands
]
if (injection) {
injection = {
..._injection,
...injection
}
} else {
injection = _injection
}
const manager = new Manager(allCommands, injection)
manager.events.on(EVENTS.error, ({e, request}) => {
const { console } = injection
console.log(e.toString())
const stack = extractValue(request.args, 'stack', false)
if (stack) {
console.log(e.stack)
}
})
request = tokensToCommand(tokens)
const manager = bootstrapCommandManager(_commands, injection)
const request = tokensToCommand(tokens)

return manager.execute(request)
}

module.exports = {
bootstrapCommandManager,
commands,
runCLI,
runCommand,
EVENTS
runCommand
}
8 changes: 5 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
const { extractValue, parse, parseCommand } = require('./parser');
const { Manager } = require('./manager')
const { runCLI, runCommand } = require('./cli')
const { Manager, EVENTS } = require('./manager')
const { runCLI, runCommand, bootstrapCommandManager } = require('./cli')

module.exports = {
bootstrapCommandManager,
runCLI,
runCommand,
extractValue,
parse,
parseCommand,
Manager
Manager,
EVENTS
}
3 changes: 3 additions & 0 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ function parseArgument (token) {
}

function extractValue (args, key, defaultVal=null) {
if (!(args instanceof Array)) {
return defaultVal
}
const arg = args
.filter(arg => arg.key === key)
if (arg.length === 0) {
Expand Down
86 changes: 48 additions & 38 deletions test/cli.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const assert = require('assert')
const { runCLI, EVENTS, runCommand } = require('../src/cli')
const { runCLI, runCommand, EVENTS, bootstrapCommandManager } = require('../src')
const EventEmitter = require('events')
const { _console } = require('./helper')

describe('Commander', () => {
describe('Commander run', () => {
before(() => {
_console.clear()
})
Expand All @@ -27,41 +27,49 @@ describe('Commander', () => {
const line = _console.objects.pop()
assert.equal(line, 'Run commander testing.')
})
})

describe('Handle error message', async () => {
const runWithError = async (tokens) => {
await runCommand(
[
{
name: 'print',
title: 'Prints values',
handler: async ({ injection: { console } }) => {
throw new Error('Printer does not work.')
}
},
],
{
console: _console
},
tokens
)
}
describe('Manager assembled', () => {
const commands = [
{
name: 'print',
title: 'Prints values',
handler: async () => {
throw new Error('Printer does not work.')
}
},
]
const injection = {
console: _console
}
const manager = bootstrapCommandManager(commands, injection)

it('Error', async () => {
await runWithError(['print'])
const line = _console.objects.pop()
const etalon = 'Error: Printer does not work.'
assert.equal(line, etalon)
})
it('Error', async () => {
const request = {
name: 'print',
}
await manager.execute(request)
const line = _console.objects.pop()
const etalon = 'Error: Printer does not work.'
assert.equal(line, etalon)
})

it('Error with stack', async () => {
await runWithError(['print', '--stack'])
const lines = _console.objects.pop().split('\n')
const etalon = 'Error: Printer does not work.'
assert.equal(lines[0], etalon)
assert.notEqual(lines[6].match(/at callFn/i), null)
})
it('Error with stack', async () => {
const request = {
name: 'print',
args: [{
key: 'stack',
value: 'true',
short: false
}]
}
await manager.execute(request)
const lines = _console.objects.pop().split('\n')
const etalon = 'Error: Printer does not work.'
assert.equal(lines[0], etalon)
assert.notEqual(lines[2].match(/at Manager\.execute/i), null)
})

})

describe('CLI', () => {
Expand All @@ -71,7 +79,7 @@ describe('CLI', () => {
_console.clear()
readline = new EventEmitter()
readline.close = function() {
this.isClosed = true
this.emit('end', true)
}
const commands = [
{
Expand Down Expand Up @@ -121,7 +129,7 @@ describe('CLI', () => {

it('Async', (done) => {
readline.emit('line', 'async')
cli.events.once(EVENTS.commandExecuted, () => {
cli.manager.events.once(EVENTS.executed, () => {
const etalon = 'Async test.'
const lines = _console.objects.pop()
assert.equal(lines, etalon)
Expand All @@ -131,14 +139,16 @@ describe('CLI', () => {
assert.equal(undefined, lines)
})

it('Exit', () => {
it('Exit', (done) => {
readline.emit('line', 'exit')
const lines = _console.objects.pop()
const etalon = 'See you.'
assert.equal(lines, etalon)

// @todo Bug: async pipe is not chained.
// assert.equal(readline.isClosed, true)
readline.once('end', (result) => {
assert.equal(result, true)
done()
})
})
})

Expand Down

0 comments on commit 9de4e6f

Please sign in to comment.