@@ -58,9 +58,6 @@ class API {
58
58
// Init callback
59
59
this . _cb
60
60
61
- // Middleware stack
62
- this . _middleware = [ ]
63
-
64
61
// Error middleware stack
65
62
this . _errors = [ ]
66
63
@@ -73,27 +70,31 @@ class API {
73
70
// Global error status (used for response parsing errors)
74
71
this . _errorStatus = 500
75
72
76
- } // end constructor
73
+ // Methods
74
+ this . _methods = [ 'get' , 'post' , 'put' , 'patch' , 'delete' , 'options' , 'head' , 'any' ]
77
75
76
+ // Convenience methods for METHOD
77
+ this . _methods . forEach ( m => {
78
+ this [ m ] = ( ...a ) => this . METHOD ( m . toUpperCase ( ) , ...a )
79
+ } )
78
80
81
+ } // end constructor
79
82
80
- // Convenience methods (path, handler)
81
- get ( p , h ) { this . METHOD ( 'GET' , p , h ) }
82
- post ( p , h ) { this . METHOD ( 'POST' , p , h ) }
83
- put ( p , h ) { this . METHOD ( 'PUT' , p , h ) }
84
- patch ( p , h ) { this . METHOD ( 'PATCH' , p , h ) }
85
- delete ( p , h ) { this . METHOD ( 'DELETE' , p , h ) }
86
- options ( p , h ) { this . METHOD ( 'OPTIONS' , p , h ) }
87
- head ( p , h ) { this . METHOD ( 'HEAD' , p , h ) }
88
- any ( p , h ) { this . METHOD ( 'ANY' , p , h ) }
83
+ // METHOD: Adds method, middleware, and handlers to routes
84
+ METHOD ( method , ...args ) {
89
85
86
+ // Extract path if provided, otherwise default to global wildcard
87
+ let path = typeof args [ 0 ] === 'string' ? args . shift ( ) : '/*'
90
88
91
- // METHOD: Adds method and handler to routes
92
- METHOD ( method , path , handler ) {
89
+ // Extract the execution stack
90
+ let stack = args . map ( ( fn , i ) => {
91
+ if ( typeof fn === 'function' && ( fn . length === 3 || ( fn . length === 2 && i === args . length - 1 ) ) )
92
+ return fn
93
+ throw new ConfigurationError ( 'Route-based middleware must have 3 parameters' )
94
+ } )
93
95
94
- if ( typeof handler !== 'function' ) {
95
- throw new ConfigurationError ( `No route handler specified for ${ method } method on ${ path } route.` )
96
- }
96
+ if ( stack . length === 0 )
97
+ throw new ConfigurationError ( `No handler or middleware specified for ${ method } method on ${ path } route.` )
97
98
98
99
// Ensure method is an array
99
100
let methods = Array . isArray ( method ) ? method : method . split ( ',' )
@@ -105,46 +106,100 @@ class API {
105
106
let route = this . _prefix . concat ( parsedPath )
106
107
107
108
// For root path support
108
- if ( route . length === 0 ) { route . push ( '' ) }
109
+ if ( route . length === 0 ) { route . push ( '' ) }
109
110
110
111
// Keep track of path variables
111
112
let pathVars = { }
112
113
114
+ // Make a local copy of routes
115
+ let routes = this . _routes
116
+
117
+ // Create a local stack for inheritance
118
+ let _stack = { }
119
+
113
120
// Loop through the paths
114
121
for ( let i = 0 ; i < route . length ; i ++ ) {
115
122
123
+ let end = i === route . length - 1
124
+
116
125
// If this is a variable
117
126
if ( / ^ : ( .* ) $ / . test ( route [ i ] ) ) {
118
127
// Assign it to the pathVars (trim off the : at the beginning)
119
- pathVars [ i ] = route [ i ] . substr ( 1 )
128
+ pathVars [ i ] = [ route [ i ] . substr ( 1 ) ]
120
129
// Set the route to __VAR__
121
130
route [ i ] = '__VAR__'
122
131
} // end if variable
123
132
133
+ // Add methods to routess
124
134
methods . forEach ( _method => {
125
135
if ( typeof _method === 'string' ) {
136
+
137
+ if ( routes [ 'ROUTES' ] ) {
138
+
139
+ // Wildcard routes
140
+ if ( routes [ 'ROUTES' ] [ '*' ] ) {
141
+
142
+ // Inherit middleware
143
+ if ( routes [ 'ROUTES' ] [ '*' ] [ 'MIDDLEWARE' ] ) {
144
+ _stack [ method ] = _stack [ method ] ?
145
+ <
10000
span class=pl-s1>_stack[ method ] . concat ( routes [ 'ROUTES' ] [ '*' ] [ 'MIDDLEWARE' ] . stack )
146
+ : routes [ 'ROUTES' ] [ '*' ] [ 'MIDDLEWARE' ] . stack
147
+ }
148
+
149
+ // Inherit methods and ANY
150
+ if ( routes [ 'ROUTES' ] [ '*' ] [ 'METHODS' ] && routes [ 'ROUTES' ] [ '*' ] [ 'METHODS' ] ) {
151
+ [ 'ANY' , method ] . forEach ( m => {
152
+ if ( routes [ 'ROUTES' ] [ '*' ] [ 'METHODS' ] [ m ] ) {
153
+ _stack [ method ] = _stack [ method ] ?
154
+ _stack [ method ] . concat ( routes [ 'ROUTES' ] [ '*' ] [ 'METHODS' ] [ m ] . stack )
155
+ : routes [ 'ROUTES' ] [ '*' ] [ 'METHODS' ] [ m ] . stack
156
+ }
157
+ } ) // end for
158
+ }
159
+ }
160
+
161
+ // Matching routes
162
+ if ( routes [ 'ROUTES' ] [ route [ i ] ] ) {
163
+
164
+ // Inherit middleware
165
+ if ( end && routes [ 'ROUTES' ] [ route [ i ] ] [ 'MIDDLEWARE' ] ) {
166
+ _stack [ method ] = _stack [ method ] ?
167
+ _stack [ method ] . concat ( routes [ 'ROUTES' ] [ route [ i ] ] [ 'MIDDLEWARE' ] . stack )
168
+ : routes [ 'ROUTES' ] [ route [ i ] ] [ 'MIDDLEWARE' ] . stack
169
+ }
170
+
171
+ // Inherit ANY methods (DISABLED)
172
+ // if (end && routes['ROUTES'][route[i]]['METHODS'] && routes['ROUTES'][route[i]]['METHODS']['ANY']) {
173
+ // _stack[method] = _stack[method] ?
174
+ // _stack[method].concat(routes['ROUTES'][route[i]]['METHODS']['ANY'].stack)
175
+ // : routes['ROUTES'][route[i]]['METHODS']['ANY'].stack
176
+ // }
177
+ }
178
+ }
179
+
126
180
// Add the route to the global _routes
127
181
this . setRoute (
128
182
this . _routes ,
129
- ( i === route . length - 1 ? {
130
- [ '__' + _method . trim ( ) . toUpperCase ( ) ] : {
131
- vars : pathVars ,
132
- handler : handler ,
133
- route : '/' + parsedPath . join ( '/' ) ,
134
- path : '/' + this . _prefix . concat ( parsedPath ) . join ( '/' ) }
135
- } : { } ) ,
183
+ _method . trim ( ) . toUpperCase ( ) ,
184
+ ( end ? {
185
+ vars : pathVars ,
186
+ stack,
187
+ inherited : _stack [ method ] ? _stack [ method ] : [ ] ,
188
+ route : '/' + parsedPath . join ( '/' ) ,
189
+ path : '/' + this . _prefix . concat ( parsedPath ) . join ( '/' )
190
+ } : null ) ,
136
191
route . slice ( 0 , i + 1 )
137
192
)
193
+
138
194
}
139
195
} ) // end methods loop
140
196
197
+ routes = routes [ 'ROUTES' ] [ route [ i ] ]
141
198
142
199
} // end for loop
143
200
144
201
} // end main METHOD function
145
202
146
-
147
-
148
203
// RUN: This runs the routes
149
204
async run ( event , context , cb ) {
150
205
@@ -162,44 +217,24 @@ class API {
162
217
// Parse the request
163
218
await request . parseRequest ( )
164
219
165
- // Loop through the middleware and await response
166
- for ( const mw of this . _middleware ) {
167
- // Only run middleware if in processing state
220
+ // Loop through the execution stack
221
+ for ( const fn of request . _stack ) {
222
+ // Only run if in processing state
168
223
if ( response . _state !== 'processing' ) break
169
224
170
- // Init for matching routes
171
- let matched = false
172
-
173
- // Test paths if they are supplied
174
- for ( const path of mw [ 0 ] ) {
175
- if (
176
- path === request . path || // If exact path match
177
- path === request . route || // If exact route match
178
- // If a wildcard match
179
- ( path . substr ( - 1 ) === '*' && new RegExp ( '^' + path . slice ( 0 , - 1 ) + '.*$' ) . test ( request . route ) )
180
- ) {
181
- matched = true
182
- break
183
- }
184
- }
185
-
186
- if ( mw [ 0 ] . length > 0 && ! matched ) continue
187
-
188
- // Promisify middleware
189
225
await new Promise ( async r => {
190
- let rtn = await mw [ 1 ] ( request , response , ( ) => { r ( ) } )
191
- if ( rtn ) response . send ( rtn )
192
- if ( response . _state === 'done' ) r ( ) // if state is done, resolve promise
226
+ try {
227
+ let rtn = await fn ( request , response , ( ) => { r ( ) } )
228
+ if ( rtn ) response . send ( rtn )
229
+ if ( response . _state === 'done' ) r ( ) // if state is done, resolve promise
230
+ } catch ( e ) {
231
+ await this . catchErrors ( e , response )
232
+ r ( ) // resolve the promise
233
+ }
193
234
} )
194
235
195
236
} // end for
196
237
197
- // Execute the primary handler if in processing state
198
- if ( response . _state === 'processing' ) {
199
- let rtn = await request . _handler ( request , response )
200
- if ( rtn ) response . send ( rtn )
201
- }
202
-
203
238
} catch ( e ) {
204
239
await this . catchErrors ( e , response )
205
240
}
@@ -214,8 +249,6 @@ class API {
214
249
// Catch all async/sync errors
215
250
async catchErrors ( e , response , code , detail ) {
216
251
217
- // console.log('\n\n------------------------\n',e,'\n------------------------\n\n');
218
-
219
252
// Error messages should never be base64 encoded
220
253
response . _isBase64 = false
221
254
@@ -297,24 +330,34 @@ class API {
297
330
298
331
299
332
// Middleware handler
300
- use ( path ) {
333
+ use ( ... args ) {
301
334
302
335
// Extract routes
303
- let routes = typeof path === 'string' ? Array . of ( path ) : ( Array . isArray ( path ) ? path : [ ] )
336
+ let routes = typeof args [ 0 ] === 'string' ? Array . of ( args . shift ( ) ) : ( Array . isArray ( args [ 0 ] ) ? args . shift ( ) : [ '/*' ] )
337
+
338
+ // Init middleware stack
339
+ let middleware = [ ]
304
340
305
341
// Add func args as middleware
306
- for ( let arg in arguments ) {
307
- if ( typeof arguments [ arg ] === 'function' ) {
308
- if ( arguments [ arg ] . length === 3 ) {
309
- this . _middleware . push ( [ routes , arguments [ arg ] ] )
310
- } else if ( arguments [ arg ] . length === 4 ) {
311
- this . _errors . push ( arguments [ arg ] )
342
+ for ( let arg in args ) {
343
+ if ( typeof args [ arg ] === 'function' ) {
344
+ if ( args [ arg ] . length === 3 ) {
345
+ middleware . push ( args [ arg ] )
346
+ } else if ( args [ arg ] . length === 4 ) {
347
+ this . _errors . push ( args [ arg ] )
312
348
} else {
313
349
throw new ConfigurationError ( 'Middleware must have 3 or 4 parameters' )
314
350
}
315
351
}
316
352
}
317
353
354
+ // Add middleware to path
355
+ if ( middleware . length > 0 ) {
356
+ routes . forEach ( route => {
357
+ this . METHOD ( '__MW__' , route , ...middleware )
358
+ } )
359
+ }
360
+
318
361
} // end use
319
362
320
363
@@ -333,28 +376,58 @@ class API {
333
376
return path . trim ( ) . replace ( / ^ \/ ( .* ?) ( \/ ) * $ / , '$1' ) . split ( '/' ) . filter ( x => x . trim ( ) !== '' )
334
377
}
335
378
336
- // Recursive function to create routes object
337
- setRoute ( obj , value , path ) {
338
- if ( typeof path === 'string' ) {
339
- path = path . split ( '.' )
340
- }
341
-
342
- if ( path . length > 1 ) {
379
+ // Recursive function to create/merge routes object
380
+ setRoute ( obj , method , value , path ) {
381
+ if ( path . length > 1 ) {
343
382
let p = path . shift ( )
344
- if ( obj [ p ] === null ) {
345
- obj [ p ] = { }
346
- }
347
- this . setRoute ( obj [ p ] , value , path )
383
+ if ( p === '*' ) { throw new ConfigurationError ( 'Wildcards can only be at the end of a route definition.' ) }
384
+ this . setRoute ( obj [ 'ROUTES' ] [ p ] , method , value , path )
348
385
} else {
349
- if ( obj [ path [ 0 ] ] === null ) {
350
- obj [ path [ 0 ] ] = value
351
- } else {
352
- obj [ path [ 0 ] ] = Object . assign ( value , obj [ path [ 0 ] ] )
386
+ // Create routes and add path if they don't exist
387
+ if ( ! obj [ 'ROUTES' ] ) obj [ 'ROUTES' ] = { }
388
+ if ( ! obj [ 'ROUTES' ] [ path [ 0 ] ] ) obj [ 'ROUTES' ] [ path [ 0 ] ] = { }
389
+
390
+ // If a value exists in this iteration
391
+ if ( value !== null ) {
392
+
393
+ // TEMP: debug
394
+ // value._STACK = value.stack.map(x => x.name)
395
+ // value._STACK2 = value.inherited.map(x => x.name)
396
+
397
+ // If mounting middleware
398
+ if ( method === '__MW__' ) {
399
+ // Merge stacks if middleware exists
400
+ if ( obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'MIDDLEWARE' ] ) {
401
+ value . stack = obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'MIDDLEWARE' ] . stack . concat ( value . stack )
402
+ value . vars = UTILS . mergeObjects ( obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'MIDDLEWARE' ] . vars , value . vars )
403
+ }
404
+
405
+ // Add/Update the middleware
406
+ obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'MIDDLEWARE' ] = value
407
+
408
+ // Else if mounting a regular route
409
+ } else {
410
+
411
+ // Create the methods section if it doesn't exist
412
+ if ( ! obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] ) obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] = { }
413
+
414
+ // Merge stacks if method exists
415
+ if ( obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] [ method ] ) {
416
+ value . stack = obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] [ method ] . stack . concat ( value . stack )
417
+ value . vars = UTILS . mergeObjects ( obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] [ method ] . vars , value . vars )
418
+ }
419
+
420
+ // Add/Update the method
421
+ obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] = Object . assign (
422
+ { } , obj [ 'ROUTES' ] [ path [ 0 ] ] [ 'METHODS' ] , { [ method ] : value }
423
+ )
424
+
425
+ }
353
426
}
427
+
354
428
}
355
429
} // end setRoute
356
430
357
-
358
431
// Load app packages
359
432
app ( packages ) {
360
433
0 commit comments