UNPKG

3.05 kBJavaScriptView Raw
1'use strict'
2
3const async = require('async')
4
5class Middleware {
6 constructor (robot) {
7 this.robot = robot
8 this.stack = []
9 }
10
11 // Public: Execute all middleware in order and call 'next' with the latest
12 // 'done' callback if last middleware calls through. If all middleware is
13 // compliant, 'done' should be called with no arguments when the entire
14 // round trip is complete.
15 //
16 // context - context object that is passed through the middleware stack.
17 // When handling errors, this is assumed to have a `response` property.
18 //
19 // next(context, done) - Called when all middleware is complete (assuming
20 // all continued by calling respective 'next' functions)
21 //
22 // done() - Initial (final) completion callback. May be wrapped by
23 // executed middleware.
24 //
25 // Returns nothing
26 // Returns before executing any middleware
27 execute (context, next, done) {
28 const self = this
29
30 if (done == null) {
31 done = function () {}
32 }
33
34 // Execute a single piece of middleware and update the completion callback
35 // (each piece of middleware can wrap the 'done' callback with additional
36 // logic).
37 function executeSingleMiddleware (doneFunc, middlewareFunc, cb) {
38 // Match the async.reduce interface
39 function nextFunc (newDoneFunc) {
40 cb(null, newDoneFunc || doneFunc)
41 }
42
43 // Catch errors in synchronous middleware
44 try {
45 middlewareFunc(context, nextFunc, doneFunc)
46 } catch (err) {
47 // Maintaining the existing error interface (Response object)
48 self.robot.emit('error', err, context.response)
49 // Forcibly fail the middleware and stop executing deeper
50 doneFunc()
51 }
52 }
53
54 // Executed when the middleware stack is finished
55 function allDone (_, finalDoneFunc) {
56 next(context, finalDoneFunc)
57 }
58
59 // Execute each piece of middleware, collecting the latest 'done' callback
60 // at each step.
61 process.nextTick(async.reduce.bind(null, this.stack, done, executeSingleMiddleware, allDone))
62 }
63
64 // Public: Registers new middleware
65 //
66 // middleware - A generic pipeline component function that can either
67 // continue the pipeline or interrupt it. The function is called
68 // with (robot, context, next, done). If execution should
69 // continue (next middleware, final callback), the middleware
70 // should call the 'next' function with 'done' as an optional
71 // argument.
72 // If not, the middleware should call the 'done' function with
73 // no arguments. Middleware may wrap the 'done' function in
74 // order to execute logic after the final callback has been
75 // executed.
76 //
77 // Returns nothing.
78 register (middleware) {
79 if (middleware.length !== 3) {
80 throw new Error(`Incorrect number of arguments for middleware callback (expected 3, got ${middleware.length})`)
81 }
82 this.stack.push(middleware)
83 }
84}
85
86module.exports = Middleware