Skip to content

Commit

Permalink
Add middlewares option
Browse files Browse the repository at this point in the history
  • Loading branch information
Juan José committed Sep 8, 2016
1 parent 3a523f4 commit e56f7c5
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dafuq allows you to create an api that executes files on the os command line (vi
* **debug** (optional): Show debug info. If true, `console.log` will be used as loggin function. If a function it will used as loggin function instead of the default . Defaults to `false`.
* **bearer** (optional): Add bearer token authorization method to the api. The acces token is provided as the value of this config. Defaults to ''
* **timeout** (optional): Time to wait before killing an spawned command. Defaults to `0` which means infinite.
* **middlewares** (optional): Array of middlewares that will be executed after the command has run but before sending the api respnse. The response object has a `dafuq` property containing `result` and `type` properties with the result of the execution.

### Example

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"mocha": "^3.0.2",
"nyc": "^8.1.0",
"should": "^11.1.0",
"sinon": "^1.17.5",
"supertest": "^2.0.0"
},
"repository": "Upplication/node-dafuq",
Expand Down
38 changes: 29 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ const fs = require('fs')

const IS_TEST = process.env['NODE_ENV'] === 'test'

const RESULT_PROPERTY = 'dafuq_result'
const RESULT_TYPE_PROPERTY = 'dafuq_type'
const RESPONSE_RESULT_CONTAINER = 'dafuq'
const RESPONSE_RESULT_TYPE = 'type'
const RESPONSE_RESULT = 'result'

/**
* @typedef DafuqPath
Expand Down Expand Up @@ -249,10 +250,13 @@ function accessMiddleware(token) {
*/
function resultMiddleware() {
return (req, res, next) => {
if (res[RESULT_TYPE_PROPERTY] === RESULT_TYPE_OBJECT)
res.type('json').json(res[RESULT_PROPERTY])
else if (res[RESULT_TYPE_PROPERTY] === RESULT_TYPE_FILE)
res.download(res[RESULT_PROPERTY])
const result = res[RESPONSE_RESULT_CONTAINER][RESPONSE_RESULT]
, type = res[RESPONSE_RESULT_CONTAINER][RESPONSE_RESULT_TYPE]

if (type === RESULT_TYPE_OBJECT)
res.type('json').json(result)
else if (type === RESULT_TYPE_FILE)
res.download(result)
else // This shouldn't happen, but just in case
res.status(500).send()
}
Expand All @@ -269,7 +273,8 @@ export default function dafuq(config) {
shebang: '',
debug: false,
brearer: '',
timeout: 0
timeout: 0,
middlewares: []
}, config)

// Options validation
Expand All @@ -290,6 +295,10 @@ export default function dafuq(config) {
if (opts.timeout && (typeof opts.timeout !== 'number'))
throw new TypeError('timeout must be a number')

// If middlewares provided, but not valid
if (opts.middlewares !== undefined && !Array.isArray(opts.middlewares))
throw new TypeError('middlewares must be a an array')

if (opts.debug !== undefined) {
if (opts.debug === true)
opts.debug = IS_TEST ? (() => {}) : console.log
Expand Down Expand Up @@ -334,6 +343,9 @@ export default function dafuq(config) {
*/
function executionMiddleware(file) {
return (req, res, next) => {
// Initialize the dafuq result container
Object.defineProperty(res, RESPONSE_RESULT_CONTAINER, { value: {} })

// Build the base command
let cmd = file
if (opts.shebang)
Expand All @@ -349,8 +361,14 @@ export default function dafuq(config) {

opts.debug(`$ ${cmd}`)
execCommand(cmd, opts.timeout, (result, type) => {
Object.defineProperty(res, RESULT_PROPERTY, { value: result })
Object.defineProperty(res, RESULT_TYPE_PROPERTY, { value: type })
Object.defineProperty(res[RESPONSE_RESULT_CONTAINER], RESPONSE_RESULT_TYPE, {
enumerable: true,
value: type
})
Object.defineProperty(res[RESPONSE_RESULT_CONTAINER], RESPONSE_RESULT, {
enumerable: true,
value: result
})
next()
})
}
Expand All @@ -373,6 +391,8 @@ export default function dafuq(config) {
middlewares.push(upload.any())

middlewares.push(executionMiddleware(file.absolute))
// Allow clients to do something with the responses before sending it
middlewares.push(...opts.middlewares)
middlewares.push(resultMiddleware())

opts.debug(`Adding ${ method } ${ url }`)
Expand Down
126 changes: 121 additions & 5 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
require('should')
const p = require('path')
const fs = require('fs')
const request = require('supertest');
const request = require('supertest')
const sinon = require('sinon')
const dafuq = require(process.env['DAFUQ_COVERAGE'] ? '../src-cov' : '../src')

describe('Constructor', function() {
Expand Down Expand Up @@ -124,22 +125,78 @@ describe('Constructor', function() {
build({
path: './commands',
debug: ''
}).should.throw();
}).should.throw(/debug/);
build({
path: './commands',
debug: 'string'
}).should.throw();
}).should.throw(/debug/);
build({
path: './commands',
debug: 123
}).should.throw();
}).should.throw(/debug/);
build({
path: './commands',
debug: {}
}).should.throw();
}).should.throw(/debug/);
build({
path: './commands',
debug: []
}).should.throw(/debug/);
})

it('should throw if middlewares is not an array of functions', function() {
build({
path: './commands',
middlewares: []
}).should.not.throw();
build({
path: './commands',
middlewares: true
}).should.throw(/middlewares/);
build({
path: './commands',
middlewares: function() {}
}).should.throw(/middlewares/);
build({
path: './commands',
middlewares: ''
}).should.throw(/middlewares/);
build({
path: './commands',
middlewares: 'string'
}).should.throw(/middlewares/);
build({
path: './commands',
middlewares: 123
}).should.throw(/middlewares/);
build({
path: './commands',
middlewares: {}
}).should.throw(/middlewares/);
// Arrays of
build({
path: './commands',
middlewares: [ () => {} ]
}).should.not.throw();
build({
path: './commands',
middlewares: [ true ]
}).should.throw();
build({
path: './commands',
middlewares: [ '' ]
}).should.throw();
build({
path: './commands',
middlewares: [ 'string' ]
}).should.throw();
build({
path: './commands',
middlewares: [ 123 ]
}).should.throw();
build({
path: './commands',
middlewares: [ {} ]
}).should.throw();
})
})
Expand Down Expand Up @@ -317,6 +374,65 @@ describe('Invoking a file', () => {
.end(done)
})
})

describe('specifing middlewares', () => {
let app, spy1, spy2;

before(() => {
spy1 = sinon.stub().callsArg(2)
spy2 = sinon.stub().callsArg(2)
app = dafuq({
path: './commands',
middlewares: [ spy1, spy2 ]
});
})

beforeEach(() => {
spy1.reset()
spy2.reset()
})

it('should call each middleware once in order of definition', (done) => {
request(app)
.get('/hello')
.expect(200)
.expect('Content-Type', /json/)
.expect(() => {
spy1.calledOnce.should.be.true()
spy2.calledOnce.should.be.true()
spy1.calledBefore(spy2)
})
.end(done)
})

it('should pass each middleware the result type of execution', (done) => {
request(app)
.get('/hello')
.expect(200)
.expect('Content-Type', /json/)
.expect(response => {
const [ req, res, next ] = spy1.args[0]
res.should.have.property('dafuq')
res.dafuq.should.have.property('result')
response.body.should.be.eql(res.dafuq.result)
})
.end(done)
})

it('should pass each middleware the result of execution', (done) => {
request(app)
.get('/hello')
.expect(200)
.expect('Content-Type', /json/)
.expect(response => {
const [ req, res, next ] = spy1.args[0]
res.should.have.property('dafuq')
res.dafuq.should.have.property('type')
})
.end(done)
})
})

})

describe('Arguments', () => {
Expand Down

0 comments on commit e56f7c5

Please sign in to comment.