I want to create an AWS Lambda function to check whether the site is available. I tried this function.
'use strict';
var url = require('url');
var target = 'https://www.google.com'; // Change this one
exports.handler = function(event, context, callback) {
var urlObject = url.parse(target);
var mod = require(
urlObject.protocol.substring(0, urlObject.protocol.length - 1)
);
console.log('[INFO] - Checking ' + target);
var req = mod.request(urlObject, function(res) {
res.setEncoding('utf8');
res.on('data', function(chunk) {
console.log('[INFO] - Read body chunk');
});
res.on('end', function() {
console.log('[INFO] - Response end');
callback();
});
});
req.on('error', function(e) {
console.log('[ERROR] - ' + e.message);
callback(e);
});
req.end();
};
I tried this in "How it works" section before create the actual function. But when I run this, I get an error "Process exited before completing request"
My objective is to send an alert if the site is down (using AWS cloud-watch).
Your code terminates because you're invoking req.end before any the events are invoked. Under the hood, these APIs use the EventEmitter API in NodeJS so it publishes events to the channels that are listening to them, but since all of this happens asynchronously, req.end is being invoked before any of these events are fired.
You can greatly simplify your code by using the request module. Just pack it with your dependencies.
I have refactored your code a little bit to use async/await as well, so I needed to promifisy the callback. It's a good practice to do so. If you want to send a notification when something goes wrong, just put the code inside the catch block.
'use strict';
const target = 'https://www.google.com'; // Change this one
const request = require('request')
const handler = async (event) => {
try {
const data = await new Promise((res, rej) => {
request.get(target, (err, data) => {
if (err) {
return rej(err)
}
return res(data)
})
})
console.log(data)
} catch (e) {
console.log(e)
//send notification
}
};
Related
I am trying to process signup data for a uni project . I am using basic koa modules and I am not allowed to use express, ideally I want to get the data inside the variable post. I want to process the data for example to see if the password has less than 5 characters , if so i would like that the program would not redirect the user to different address but if no errors occur i would like the program to redirect to regOk.html, I tried many other ways like initializing the variable outside of ctx.req.on but none were successful . Can anyone help me ?
export async function postregister(ctx) {
let bodyString = "";
ctx.req.on("data", (chunk) => {
bodyString += chunk;
});
//let collectData = new Array();
ctx.req.on("end", () => {
var post = querystring.parse(bodyString);
var email = post["email"];
var password = post["password"];
var passbestätigen = post["passwort bestä"];
var vorname = post["vorname"];
var nachname = post["nachname"];
var adresse = post["adresse"];
var stadt = post["stadt"];
var telefonnummer = post["telefonnummer"];
var geburtsdatum = post["geburtsdatum"];
var regData = model.add(ctx.db, post);
regData.then(() => console.log("singup successful"))
});
await ctx.render("regOk.html");
}
I'm not very familiar with koa, but I believe your issue is related to the order in which your code is executed.
The event in charge of parsing the data received in the body of the request ends after the synchronic execution of your postregister method, so you never get to see the value of post in the order you'd expect.
One possible solution to go around this issue would be wrapping the parsing of data in a promise, waiting for that promise to complete, and executing then and catch functions once the processing is done.
export async function postregister(ctx) {
await new Promise((resolve) => {
let bodyString = "";
ctx.req.on("data", (chunk) => {
bodyString += chunk;
});
ctx.req.on("end", async () => {
resolve(querystring.parse(bodyString));
});
})
.then(async (post) => {
await model.add(ctx.db, post)
.then(async () => {
console.log("singup successful");
await ctx.render('regOk.html');
});
})
.catch(async (error) => {
console.error(error);
await ctx.render('error.html');
});
}
This way, you handle body parsing inside the Promise, and after that completed you get the result of querystring.parse(bodyString) as a variable named post in your then handler.
I am working with Azure Functions in NodeJS. I first wrote the function having azure funct locally on my laptop. The code worked fine and did everything I wanted. Now, I added the code to a new azure function in my Azure Visual Studio Code Extention - the exact same code. BUT now it does not work anymore. I don't get an error or something, the https request is just not started.
Here is my code:
const https = require('https');
const fs = require('fs');
const storage = require('azure-storage');
const STORAGE_ACCOUNT_NAME = 'SOMETHING';
const ACCOUNT_ACCESS_KEY = 'SOMETHING';
const blobService = storage.createBlobService(STORAGE_ACCOUNT_NAME, ACCOUNT_ACCESS_KEY);
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
let _browser;
let _page;
https.get(SOMEURL", (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', async () => {
context.log('here');
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
};
It never prints the "here" which should be done after the https request ends. (The first context.log is however printed in both cases)
So my question is, what am I doing wrong? Can I not use https request inside azure functions when using the visual studio code extension?
Edit:
anyone who needs the async, here is a link explained how to do it with util.promisify: https://gist.github.com/krnlde/797e5e0a6f12cc9bd563123756fc101f
I've kept your code callback based.
I removed the async moniker from the definition and added a call to context.done (this signals the functions host when your function has ended) in your resp.end handler
const https = require('https');
const fs = require('fs');
const storage = require('azure-storage');
const STORAGE_ACCOUNT_NAME = 'SOMETHING';
const ACCOUNT_ACCESS_KEY = 'SOMETHING';
const blobService = storage.createBlobService(STORAGE_ACCOUNT_NAME, ACCOUNT_ACCESS_KEY);
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
let _browser;
let _page;
https.get(SOMEURL", (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', async () => {
context.log('here');
context.done();
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
};
Another option would be to keep the function as async but you'd need to replace the callbacks with promise based calls. In some scenarios this can be achieved by wrapping them using util.promisify and then calling them with the await keyword
I'm trying to understand what .on('end', ...) does in the node package request.
My code:
const fs = require('fs');
const request = require('request');
function downloadAsset(relativeAssetURL, fileName) {
return new Promise((resolve, reject) => {
try {
let writeStream = fs.createWriteStream(fileName);
var remoteImage = request(`https:${relativeAssetURL}`);
remoteImage.on('data', function(chunk) {
writeStream.write(chunk);
});
remoteImage.on('end', function() {
let stats = fs.statSync(fileName);
resolve({ fileName: fileName, stats: stats });
});
} catch (err) {
reject(err);
}
});
}
What I'm trying to do is download a remote image, get some file statistics, and then resolve the promise so my code can do other things.
What I'm finding is that the promise doesn't always resolve after the file has been downloaded; it may resolve a little before then. I thought that's what .on('end', ... ) was for.
What can I do to have this promise resolve after the image has been downloaded in full?
As the docs say:
The writable.write() method writes some data to the stream, and calls the supplied callback once the data has been fully handled.
So, writable.write() is asynchronous. Just because your last writeStream.write has been called does not necessarily mean that all write operations have been completed. You probably want to call the .end method, which means:
Calling the writable.end() method signals that no more data will be written to the Writable. The optional chunk and encoding arguments allow one final additional chunk of data to be written immediately before closing the stream. If provided, the optional callback function is attached as a listener for the 'finish' event.
So, try calling writeStream.end when the remoteImage request ends, and pass a callback to writeStream.end that resolves the Promise once the writing is finished:
function downloadAsset(relativeAssetURL, fileName) {
return new Promise((resolve, reject) => {
try {
const writeStream = fs.createWriteStream(fileName);
const remoteImage = request(`https:${relativeAssetURL}`);
remoteImage.on('data', function(chunk) {
writeStream.write(chunk);
});
remoteImage.on('end', function() {
writeStream.end(() => {
const stats = fs.statSync(fileName);
resolve({ fileName: fileName, stats: stats });
});
});
} catch (err) {
reject(err);
}
});
}
(also try not to mix var and let/const - in an ES6+ environment, prefer const, which is generally easier to read and has fewer problems, like hoisting)
I'm working on my first Alexa skill and, as a starting point, would like Alexa to state data retrieved from a simple GET request (see lambda function below). For some reason, however, the request does not actually seem to be executing - nothing from inside request.get() is printing to the console and speechOutput is 'Outside Request' after the handler executes. I'm also new to looking through CloudWatch logs and have not been able to find any information about the network requests to even know if this is being attempted. Any help here would be welcome!
'use strict';
//Required node packages
const alexa = require('./node_modules/alexa-sdk');
const request = require('request');
// var https = require('https')
//this is the handler, when the lambda is invoked, this is whats called
exports.handler = function (event, context, callback) {
const skill = alexa.handler(event, context);
skill.appId = '<app_id>';
skill.registerHandlers(handlers);
skill.execute();
};
//Alexa handlers
const handlers = {
'LaunchRequest': function () {
console.log("inside of LaunchRequest");
const speechOutput = "Hello from NASA!";
this.response.speak(speechOutput).listen(speechOutput);
this.emit(':responseReady');
},
//Entering our main, part finding function
'GetAPOD': function () {
const intent_context= this
const speechOutput = getData()
intent_context.response.speak(speechOutput).listen(speechOutput);
intent_context.emit(':responseReady');
},
'Unhandled': function (){
console.log("inside of unhandled");
const speechOutput = "I didn't understand that. Please try again";
this.response.speak(speechOutput).listen(speechOutput);
this.emit(':responseReady');
}
};
const getData = function() {
const url = "https://api.nasa.gov/planetary/apod?api_key=<key>"
console.log("inside get data")
request.get(url, function (error, response, body) {
console.log("inside request")
console.log('error', error) //Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
return "complete request"
return body
});
return "outside request"
}
I have found in the past that such API requests will get clobbered because they are not synchronous, like David stated. To resolve this, I have had to tuck the request in a promise to get it to resolve, something similar to this in your case:
Change your function to contain the promise:
function getData = function() {
const url = "https://api.nasa.gov/planetary/apod?api_key=<key>"
console.log("inside get data")
return new Promise(function(resolve, reject) {
request.get(url, function (error, response, body) {
if (err) {
reject(err);
}
if (body) {
resolve(JSON.parse(body));
}
});
});
}
Then change your intent handler to use the promise:
//Entering our main, part finding function
'GetAPOD': function () {
getData()
.then(function(body) {
let speechOutput = body;
intent_context.response.speak(speechOutput).listen(speechOutput);
intent_context.emit(':responseReady');
}
Something along these lines. You would need to play with it a bit to make sure the results are produced as you intend. Hope this helps.
D
I am using bluebird for promises.
I am trying to promisify the download module.
Here is my implementation:
Promise = require('bluebird'),
download = require('download');
var methodNameToPromisify = ["download"];
function EventEmitterPromisifier(originalMethod) {
// return a function
return function promisified() {
var args = [].slice.call(arguments);
// Needed so that the original method can be called with the correct receiver
var self = this;
// which returns a promise
return new Promise(function(resolve, reject) {
// We call the originalMethod here because if it throws,
// it will reject the returned promise with the thrown error
var emitter = originalMethod.apply(self, args);
emitter
.on("response", function(data) {
resolve(data);
})
.on("data ", function(data) {
resolve(data);
})
.on("error", function(err) {
reject(err);
})
.on("close", function() {
resolve();
});
});
};
};
download = { download: download };
Promise.promisifyAll(download, {
filter: function(name) {
return methodNameToPromisify.indexOf(name) > -1;
},
promisifier: EventEmitterPromisifier
});
Then using it:
return download.downloadAsync(fileURL, copyTo, {});
My problem is that it doesn't download all of the files (I have a list sent to this function), what am I doing wrong?
An emitter does emit multiple data events, one for every chunk it receives. However, a represents only one future value, in your case you want that to be the complete response.
resolve is supposed to be called only once, to fulfill the promise with the passed value, which is then settled. Further calls will have no effect - and that's why you get only the first parts of your list.
Instead, you will need to accumulate all the data, and when the stream ends you can fulfill the promise with all of it.
var Promise = require('bluebird'),
download = require('download'),
Buffer = require('buffer'); // should be global anyway
exports = {
downloadAsync: function promisifiedDownload() {
var args = arguments, self = this;
return new Promise(function(resolve, reject) {
// We call the originalMethod here because if it throws,
// it will reject the returned promise with the thrown error
var emitter = download.apply(self, args);
var buffers = [];
emitter.on("data", function(data) {
buffers.push(data);
}).on("error", function(err) {
reject(err);
}).on("close", function() {
resolve(Buffer.concat(buffers));
});
});
};
};
Notice it's quite nonsensical to use promisifyAll when you only want to promisify a single method. I've omitted it for simplicity
You might also listen for the incoming response object, and attach the data listener directly to it. You can then use the end event instead of close.