Skip to content

Commit

Permalink
Close #26 by removing Bluebird promises and replacing processing with…
Browse files Browse the repository at this point in the history
… async/await
  • Loading branch information
jeremydaly committed Apr 23, 2018
1 parent bc35b77 commit a524445
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 205 deletions.
235 changes: 61 additions & 174 deletions index.js
Expand Up @@ -9,7 +9,6 @@


const REQUEST = require('./lib/request.js') // Response object const REQUEST = require('./lib/request.js') // Response object
const RESPONSE = require('./lib/response.js') // Response object const RESPONSE = require('./lib/response.js') // Response object
const Promise = require('bluebird') // Promise library


// Create the API class // Create the API class
class API { class API {
Expand Down Expand Up @@ -51,47 +50,24 @@ class API {
// Executed after the callback // Executed after the callback
this._finally = () => {} this._finally = () => {}


// Promise placeholder for final route promise resolution
this._promise = function() { console.log('no promise to resolve') }
this._reject = function() { console.log('no promise to reject') }

// Global error status // Global error status
this._errorStatus = 500 this._errorStatus = 500


// Testing flag // Testing flag (disables logging)
this._test = false this._test = false


} // end constructor } // end constructor


// GET: convenience method
get(path, handler) {
this.METHOD('GET', path, handler)
}


// POST: convenience method
post(path, handler) {
this.METHOD('POST', path, handler)
}


// PUT: convenience method // Convenience methods (path, handler)
put(path, handler) { get(p,h) { this.METHOD('GET',p,h) }
this.METHOD('PUT', path, handler) post(p,h) { this.METHOD('POST',p,h) }
} put(p,h) { this.METHOD('PUT',p,h) }
patch(p,h) { this.METHOD('PATCH',p,h) }
delete(p,h) { this.METHOD('DELETE',p,h) }
options(p,h) { this.METHOD('OPTIONS',p,h) }


// PATCH: convenience method
patch(path, handler) {
this.METHOD('PATCH', path, handler)
}

// DELETE: convenience method
delete(path, handler) {
this.METHOD('DELETE', path, handler)
}

// OPTIONS: convenience method
options(path, handler) {
this.METHOD('OPTIONS', path, handler)
}


// METHOD: Adds method and handler to routes // METHOD: Adds method and handler to routes
METHOD(method, path, handler) { METHOD(method, path, handler) {
Expand Down Expand Up @@ -124,112 +100,88 @@ class API {
this._routes, this._routes,
(i === route.length-1 ? { ['__'+method.toUpperCase()]: { vars: pathVars, handler: handler, route: '/'+parsedPath.join('/') } } : {}), (i === route.length-1 ? { ['__'+method.toUpperCase()]: { vars: pathVars, handler: handler, route: '/'+parsedPath.join('/') } } : {}),
route.slice(0,i+1) route.slice(0,i+1)
); )


} // end for loop } // end for loop


} // end main METHOD function } // end main METHOD function




// RUN: This runs the routes
run(event,context,cb) { // TODO: Make this dynamic

this.startTimer('total')


this._done = false // RUN: This runs the routes
async run(event,context,cb) {


// Set the event, context and callback // Set the event, context and callback
this._event = event this._event = event
this._context = context this._context = context
this._cb = cb this._cb = cb


// Initalize response object try {
let response = new RESPONSE(this) // Initalize response and request objects
let request = {} this.response = new RESPONSE(this)
this.request = new REQUEST(this)


Promise.try(() => { // Start a promise // Loop through the middleware and await response
for (const mw of this._middleware) {
await new Promise(r => { mw(this.request,this.response,() => { r() }) })
} // end for


// Initalize the request object // Execute the primary handler
request = new REQUEST(this) await this.handler(this.request,this.response)


// Execute the request } catch(e) {
return this.execute(request,response) this.catchErrors(e)
}


}).catch((e) => { } // end run function


// Error messages should never be base64 encoded
response._isBase64 = false


// Strip the headers (TODO: find a better way to handle this)
response._headers = {}


let message; // Catch all async/sync errors
async catchErrors(e) {


if (e instanceof Error) { // Error messages should never be base64 encoded
response.status(this._errorStatus) this.response._isBase64 = false
message = e.message
!this._test && console.log(e)
} else {
message = e
!this._test && console.log('API Error:',e)
}


// Execute error middleware // Strip the headers (TODO: find a better way to handle this)
if (this._errors.length > 0) { this.response._headers = {}

// Init stack queue
let queue = []

// Loop through the middleware and queue promises
for (let i in this._errors) {
queue.push(() => {
return new Promise((resolve, reject) => {
this._promise = () => { resolve() } // keep track of the last resolve()
this._reject = (e) => { reject(e) } // keep track of the last reject()
this._errors[i](e,request,response,() => { resolve() }) // execute the errors with the resolve callback
}) // end promise
}) // end queue
} // end for

// Return Promise.each serialially
return Promise.each(queue, function(queue_item) {
return queue_item()
}).then(() => {
response.json({'error':message})
})


} else { let message;
response.json({'error':message})
}


}).finally(() => { if (e instanceof Error) {
this._finally(request,response) this.response.status(this._errorStatus)
}) message = e.message
} // end run function !this._test && console.log(e)
} else {
message = e
!this._test && console.log('API Error:',e)
}


// Execute error middleware
for (const err of this._errors) {
// Promisify error middleware
await new Promise(r => { err(e,this.request,this.response,() => { r() }) })
} // end for


// Custom callback this.response.json({'error':message})
_callback(err, res) {


// Resolve any outstanding promise } // end catch
this._promise()


this._done = true


this.endTimer('total')


if (res) { // Custom callback
if (this._debug) { async _callback(err, res) {
console.log(this._procTimes)
} // Execute finally
} await this._finally(this.request,this.response)


// Execute the primary callback // Execute the primary callback
this._cb(err,res) this._cb(err,res)


} // end _callback } // end _callback





// Middleware handler // Middleware handler
use(fn) { use(fn) {
if (fn.length === 3) { if (fn.length === 3) {
Expand All @@ -241,79 +193,12 @@ class API {
} }
} // end use } // end use


// Finally function
// Finally handler
finally(fn) { finally(fn) {
this._finally = fn this._finally = fn
} }


// Process
execute(req,res) {

// Init stack queue
let queue = []

// If execute is called after the app is done, just return out
if (this._done) { return; }

// If there is middleware
if (this._middleware.length > 0) {
// Loop through the middleware and queue promises
for (let i in this._middleware) {
queue.push(() => {
return new Promise((resolve, reject) => {
this._promise = () => { resolve() } // keep track of the last resolve()
this._reject = (e) => { reject(e) } // keep track of the last reject()
this._middleware[i](req,res,() => { resolve() }) // execute the middleware with the resolve callback
}) // end promise
}) // end queue
} // end for
} // end if

// Push the main execution path to the queue stack
queue.push(() => {
return new Promise((resolve, reject) => {
this._promise = () => { resolve() } // keep track of the last resolve()
this._reject = (e) => { reject(e) } // keep track of the last reject()
this.handler(req,res) // execute the handler with no callback
})
})

// Return Promise.each serialially
return Promise.each(queue, function(queue_item) {
return queue_item()
})

} // end execute



//-------------------------------------------------------------------------//
// TIMER FUNCTIONS
//-------------------------------------------------------------------------//

// Returns the calculated processing times from all stopped timers
getTimers(timer) {
if (timer) {
return this._procTimes[timer]
} else {
return this._procTimes
}
} // end getTimers

// Starts a timer for debugging purposes
startTimer(name) {
this._timers[name] = Date.now()
} // end startTimer

// Ends a timer and calculates the total processing time
endTimer(name) {
try {
this._procTimes[name] = (Date.now()-this._timers[name]) + ' ms'
delete this._timers[name]
} catch(e) {
console.error('Could not end timer: ' + name)
}
} // end endTimer




//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
Expand All @@ -324,20 +209,20 @@ class API {
return path.trim().replace(/^\/(.*?)(\/)*$/,'$1').split('/').filter(x => x.trim() !== '') return path.trim().replace(/^\/(.*?)(\/)*$/,'$1').split('/').filter(x => x.trim() !== '')
} }



// Recursive function to create routes object
setRoute(obj, value, path) { setRoute(obj, value, path) {
if (typeof path === "string") { if (typeof path === "string") {
let path = path.split('.') let path = path.split('.')
} }


if (path.length > 1){ if (path.length > 1){
let p = path.shift() let p = path.shift()
if (obj[p] === null) { // || typeof obj[p] !== 'object') { if (obj[p] === null) {
obj[p] = {} obj[p] = {}
} }
this.setRoute(obj[p], value, path) this.setRoute(obj[p], value, path)
} else { } else {
if (obj[path[0]] === null) { // || typeof obj[path[0]] !== 'object') { if (obj[path[0]] === null) {
obj[path[0]] = value obj[path[0]] = value
} else { } else {
obj[path[0]] = Object.assign(value,obj[path[0]]) obj[path[0]] = Object.assign(value,obj[path[0]])
Expand Down Expand Up @@ -386,7 +271,9 @@ class API {


} // end register } // end register



} // end API class } // end API class


// Export the API class
// Export the API class as a new instance
module.exports = opts => new API(opts) module.exports = opts => new API(opts)

0 comments on commit a524445

Please sign in to comment.