Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for async/await #51

Merged
merged 6 commits into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/__tests__/isPromise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const isPromise = require('../isPromise')

test('It is a promise if has `then` and `catch` and they are functions', () => {
const promiseLike = {
then: () => {},
catch: () => {}
}

expect(isPromise(promiseLike)).toBeTruthy()
})

test('It is not a promise otherwise', () => {
const notPromises = [
{
then: 'not a function',
catch: () => {}
},
{
then: () => {},
catch: 'not a function'
},
{
then: () => {}
},
{
catch: () => {}
},
{
catch: () => {}
}
]

notPromises.forEach((obj) => {
expect(isPromise(obj)).toBe(false)
})
})
103 changes: 103 additions & 0 deletions src/__tests__/middy.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,107 @@ describe('🛵 Middy test suite', () => {
endTest()
})
})

test('It should handle async middlewares', (endTest) => {
const asyncBefore = async (handler) => {
handler.event.asyncBefore = true
}

const asyncAfter = async (handler) => {
handler.event.asyncAfter = true
}

const handler = middy((event, context, callback) => {
return callback(null, {some: 'response'})
})

handler
.before(asyncBefore)
.after(asyncAfter)

handler({}, {}, (err, response) => {
expect(err).toBe(null)
expect(handler.event.asyncBefore).toBeTruthy()
expect(handler.event.asyncAfter).toBeTruthy()
endTest()
})
})

test('It should handle async error middlewares', (endTest) => {
const expectedError = new Error('Error in handler')

const asyncOnError = async (handler) => {
handler.error = null
handler.response = {result: 'The error is handled'}
}

const handler = middy((event, context, callback) => {
return callback(expectedError)
})

handler
.onError(asyncOnError)

handler({}, {}, (err, response) => {
expect(err).toBe(null)
expect(response).toEqual({result: 'The error is handled'})
endTest()
})
})

test('A middleware that return a non-promise should trigger an error', (endTest) => {
const beforeMiddleware = (handler) => {
return 'this is not a promise'
}

const handler = middy((event, context, callback) => {
return callback(null, {foo: 'bar'})
})

handler
.before(beforeMiddleware)

handler({}, {}, (err, response) => {
expect(err.message).toBe('Unexpected return value in middleware')
endTest()
})
})

test('An error middleware that return a non-promise should trigger an error', (endTest) => {
const onErrorMiddleware = (handler) => {
return 'this is not a promise'
}

const handler = middy((event, context, callback) => {
throw new Error('something happened')
})

handler
.onError(onErrorMiddleware)

handler({}, {}, (err, response) => {
expect(err.message).toBe('Unexpected return value in onError middleware')
expect(err.originalError.message).toBe('something happened')
endTest()
})
})

// see issue #49 (https://github.com/middyjs/middy/issues/49)
test.only('Handles error thrown in async functions', (endTest) => {
const beforeMiddleware = async (handler) => {
throw new Error('I am throwing in an async func')
}

const handler = middy((event, context, callback) => {
return callback(null, {foo: 'bar'})
})

handler
.before(beforeMiddleware)

handler({}, {}, (err, response) => {
expect(err.message).toBe('I am throwing in an async func')
endTest()
})
})
})
5 changes: 5 additions & 0 deletions src/isPromise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = (val) => {
return val &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
}
36 changes: 33 additions & 3 deletions src/middy.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const isPromise = require('./isPromise')

/**
* @typedef middy
* @type function
Expand All @@ -8,7 +10,7 @@
* @property {middlewareAttachFunction} before - attach a new *before-only* middleware
* @property {middlewareAttachFunction} after - attach a new *after-only* middleware
* @property {middlewareAttachFunction} onError - attach a new *error-handler-only* middleware
* @property {Object} __middlewares - contains the list of all the attached
* @property {Object} __middlewares - contains the list of all the attached
* middlewares organised by type (`before`, `after`, `onError`). To be used only
* for testing and debugging purposes
*/
Expand Down Expand Up @@ -55,7 +57,19 @@ const runMiddlewares = (middlewares, instance, done) => {
const nextMiddleware = stack.shift()

if (nextMiddleware) {
return nextMiddleware(instance, runNext)
const retVal = nextMiddleware(instance, runNext)

if (retVal) {
if (!isPromise(retVal)) {
throw new Error('Unexpected return value in middleware')
}

retVal
.then(runNext)
.catch(done)
}

return
}

return done()
Expand All @@ -79,7 +93,23 @@ const runErrorMiddlewares = (middlewares, instance, done) => {
const nextMiddleware = stack.shift()

if (nextMiddleware) {
return nextMiddleware(instance, runNext)
// return nextMiddleware(instance, runNext)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

const retVal = nextMiddleware(instance, runNext)

if (retVal) {
if (!isPromise(retVal)) {
const invalidMiddlewareReturnError = new Error('Unexpected return value in onError middleware')
// embed original error to avoid swallowing the real exception
invalidMiddlewareReturnError.originalError = err
throw invalidMiddlewareReturnError
}

retVal
.then(runNext)
.catch(done)
}

return
}

return done(instance.__handledError ? null : err)
Expand Down