node.js - Converting AWS Lambda function to use promises? -


i writing simple http 'ping' function being periodically executed using aws lambda. uses 4 asynchronous functions: http.get, s3.getobject, s3.putobject, , nodemailer.sendmail. each seems have different callback model.

after reading promises, spent way time trying convert following code use q promises , failed miserably.

for own education , of others, hoping me convert using promises (doesn't have q):

'use strict';  var http = require('http'); var nodemailer = require('nodemailer'); var aws = require('aws-sdk'); var s3 = new aws.s3( { params: { bucket: 'my-bucket' } } );  exports.handler = (event, context, callback) => {   var laststatus;    var options = {     host: event.server.host,     port: event.server.port ? event.server.port : 80,     path: event.server.path ? event.server.path : '',     method: event.server.method ? event.server.method : 'head',     timeout: 5000   };   var transporter = nodemailer.createtransport({     host: event.mail.host,     port: event.mail.port ? event.mail.port : 587,     auth: {       user: event.mail.user,       pass: event.mail.pass     }   });    var d = new date();   var utcstring = d.toutcstring();    // email templates    var downmail = {     from: event.mail.from,     to: event.mail.to,     subject: 'lambda down alert: site (' + event.server.host + ') down',     text: 'lambdaalert down:\r\nsite (' + event.server.host + ') down @ ' + utcstring + '.'   };   var upmail = {     from: event.mail.from,     to: event.mail.to,     subject: 'lambda alert: site (' + event.server.host + ') up',     text: 'lambdaalert up:\r\nsite (' + event.server.host + ') @ ' + utcstring + '.'   };    // run async chain ensure s3 calls execute in proper order   s3.getobject( { key: 'lastpingstatus' }, (err, data) => {     // last status s3     if (err) { laststatus = "up"; } else {       laststatus = data.body.tostring();       console.log("last observed status: " + laststatus);     }     http_request(options, laststatus);   });    function http_request(requestoptions, laststatus) {     var req = http.request(requestoptions, function(res) {       if (res.statuscode == 200) {         if (laststatus == "down") {           console.log('email notice sending...');           transporter.sendmail(upmail, function(error, info) {             if (error) {               console.log("error: " + error);               callback(null, "error: " + error);             } else {               console.log('no further details available.');               callback(null, 'up message sent');             }           });         }         s3.putobject({ key: 'lastpingstatus', body: 'up', contenttype: 'text/plain' }, (error, data) => { console.log("saved last state up"); });         callback(null, 'website ok.');       }     });     req.on('error', function(e) {       if (laststatus == "up") {         console.log('email down notice sending...');         transporter.sendmail(downmail, function(error, info) {           if (error) {             console.log("error: " + error);             callback(null, "error: " + error);           } else {             console.log('no further details available.');             callback(null, 'down message sent');           }         });         s3.putobject({ key: 'lastpingstatus', body: 'down', contenttype: 'text/plain' }, (error, data) => { console.log("saved last state down"); });         callback(null, 'website down.');       }     });     req.end();   } }; 

edit: first attempt @ writing using promises:

'use strict';  var http = require('http'); var nodemailer = require('nodemailer'); var aws = require('aws-sdk'); var s3 = new aws.s3( { params: { bucket: 'lambda-key-storage' } } );  exports.handler = (event, context, callback) => {   var laststatus;    var options = {     host: event.server.host,     port: event.server.port ? event.server.port : 80,     path: event.server.path ? event.server.path : '',     method: event.server.method ? event.server.method : 'head',     timeout: 5000   };   var transporter = nodemailer.createtransport({     host: event.mail.host,     port: event.mail.port ? event.mail.port : 587,     auth: {       user: event.mail.user,       pass: event.mail.pass     }   });    var d = new date();   var utcstring = d.toutcstring();    // email templates    var downmail = {     from: event.mail.from,     to: event.mail.to,     subject: 'lambda down alert: site (' + event.server.host + ') down',     text: 'lambdaalert down:\r\nsite (' + event.server.host + ') down @ ' + utcstring + '.'   };   var upmail = {     from: event.mail.from,     to: event.mail.to,     subject: 'lambda alert: site (' + event.server.host + ') up',     text: 'lambdaalert up:\r\nsite (' + event.server.host + ') @ ' + utcstring + '.'   };    var myprom = new promise(function(resolve, reject) {     console.log("called 1");     s3.getobject( { key: 'lastpingstatus' }, (err, data) => {       // last status s3       if (err) {          resolve("up");        } else {         resolve(data.body.tostring());       }     });   })   .then(function(laststatus) {     console.log("called 2");     console.log("last observed status: " + laststatus);     var req = http.request(options, function(res) {       resolve(res.statuscode);     });     req.on('error', function(e) {       reject(e);     });     req.end();     return "??";   })   .then(function(statuscode) {     console.log("called 3");     if (statuscode == 200) {       if (laststatus == "down") {         console.log('email notice sending...');         resolve("uptrigger");       } else {         resolve("upnotrigger");       }       s3.putobject({ key: 'lastpingstatus', body: 'up', contenttype: 'text/plain' }, (err, data) => { console.log("saved last state up"); });       callback(null, 'website ok.');     }   })   .catch(function(err){     console.log("called 3 - error");     // send mail notifying of error     if (laststatus == "up") {       console.log('email down notice sending...');       resolve("downtrigger");       s3.putobject({ key: 'lastpingstatus', body: 'down', contenttype: 'text/plain' }, (error, data) => { console.log("saved last state down"); });       callback(null, 'website down.');       return("downtrigger");     } else {       return "downnotrigger";     }   })   .then(function(trigger) {     console.log("called 4");     if (trigger == "uptrigger") {       transporter.sendmail(upmail, (error, info) => {         if (error) {           console.log("error: " + error);           callback(null, "error: " + error);         } else {           console.log('up message sent.');           callback(null, 'up message sent');         }       });     } else if (trigger == "downtrigger") {       transporter.sendmail(downmail, (error, info) => {         if (error) {           console.log("error: " + error);           callback(null, "error: " + error);         } else {           console.log('down message sent.');           callback(null, 'down message sent');         }       });     }     console.log("outcome of ping was: ", trigger);   }); }; 

this doesn't quite work. result logs are:

called 1 called 2 last observed status: called 3 called 4 outcome of ping was:  undefined referenceerror: resolve not defined 

converting typical async function promise pretty straight forward. i'd rather try , demonstrate how convert write code don't learn that.

usually node you'll have looks similar this:

  dosomethingasync(callback);      function dosomethingasync(callback){         var err, result;         // work         ...           callback(err, result);     }     function callback(err, result){         if(err){             // handle error         } else{             // success result         }     } 

a promise wrapping async function looks this:

var myprom = new promise(function(resolve, reject){      dosomethingasync(function(err, result){                if(err){             reject(err);         } else{             resolve(result)         }       }); }) .then(function(result){   // success result   console.log("success:", result) }) .catch(function(err){  // handle error   console.log("error: ", err); }) .then(function(result){    // where's result? - result == undefined didn't return chain   console.log("i execute result gone", result) }) 

to pass result down chain our "always then" method need return promise or value:

var myprom = new promise(function(resolve, reject){          dosomethingasync(function(err, result){                    if(err){                 reject(err);             } else{                 resolve(result)             }           });     })     .then(function(result){       // success result       console.log("success:", result)       return result;     })     .catch(function(err){      // handle error       console.log("error: ", err);       return err;     })     .then(function(result){       // err/result gets passed down chain :)       console.log("oh there is", result)     }) 

i think using above patterns should cater of async methods , events in code example if particular ones giving trouble drop comment in , i'll try cover specific examples.

here's attempt @ converting on promises - i'm pretty tired apologies mess or mistakes - there's still plenty of cleanup done.

essentially i've done try break down code tasks , wrap each of tasks in promise. way can resolve/reject , chain them needed.

'use strict';  var http = require('http'); var nodemailer = require('nodemailer'); var aws = require('aws-sdk'); var s3 = new aws.s3( { params: { bucket: 'my-bucket' } } );  exports.handler = function (event, context, callback) {     var laststatus;      var options = {         host: event.server.host,         port: event.server.port ? event.server.port : 80,         path: event.server.path ? event.server.path : '',         method: event.server.method ? event.server.method : 'head',         timeout: 5000     };     var transporter = nodemailer.createtransport({         host: event.mail.host,         port: event.mail.port ? event.mail.port : 587,         auth: {             user: event.mail.user,             pass: event.mail.pass         }     });      var d = new date();     var utcstring = d.toutcstring();      // email templates     var downmail = {         from: event.mail.from,         to: event.mail.to,         subject: 'lambda down alert: site (' + event.server.host + ') down',         text: 'lambdaalert down:\r\nsite (' + event.server.host + ') down @ ' + utcstring + '.'     };     var upmail = {         from: event.mail.from,         to: event.mail.to,         subject: 'lambda alert: site (' + event.server.host + ') up',         text: 'lambdaalert up:\r\nsite (' + event.server.host + ') @ ' + utcstring + '.'     };      // run async chain ensure s3 calls execute in proper order      function getlastpingstatus(){         return new promise(function(resolve, reject){             s3.getobject( { key: 'lastpingstatus' }, function(err, data) {                 // last status s3                 if (err) {                     laststatus = "up";                     reject(laststatus)                 } else {                     laststatus = data.body.tostring();                     resolve(laststatus);                     console.log("last observed status: " + laststatus);                 }             });         })     }     getlastpingstatus()         .then(httprequest)         .catch(httprequest); // otherwise reject throw error      function sendmail(mail, status){ // status = "up" or "down" -         return new promise(function(resolve, reject){             transporter.sendmail(mail, function(error, info) {                 if (error) {                     console.log("error: " + error);                     reject(null, "error: " + error);                 } else {                     console.log('no further details available.');                     resolve(null, status + ' message sent');                 }             });         });     }      function savestatus(up) {         return new promise(function (resolve, reject) {             var saveoptions,                 message;             // didn't bother refactoring these promises @ same thing regardless of outcome             if(up){                 saveoptions = [{ key: 'lastpingstatus', body: 'up', contenttype: 'text/plain' }, function(error, data) { console.log("saved last state up"); }];                 message = 'website ok.';             } else{                 saveoptions = [{ key: 'lastpingstatus', body: 'down', contenttype: 'text/plain' }, function(error, data)  { console.log("saved last state down"); }];                 message = 'website down.';             }             s3.putobject.apply(this, saveoptions);             callback(null, message);         });     }      function httprequest(laststatus) {         var requestoptions = options;         return new promise (function (resolve, reject){             var req = http.request(requestoptions, function(res) {                 if (res.statuscode == 200) {                     if (laststatus == "down") {                         console.log('email notice sending...');                         sendmail(upmail, "up")                             .then(resolve, reject)                              .then(savestatus(true))                             .then(callback)                     }                 }             });             req.on('error', function(e) {                 if (laststatus == "up") {                     console.log('email down notice sending...');                     sendmail(downmail, "down")                         .then(resolve, reject)                         .then(savestatus(false))                         .then(callback)                 }             });             req.end();          });      } }; 

Comments

Popular posts from this blog

java - SSE Emitter : Manage timeouts and complete() -

jquery - uncaught exception: DataTables Editor - remote hosting of code not allowed -

java - How to resolve error - package com.squareup.okhttp3 doesn't exist? -