IBM Action not returning anything after GET request - node.js

First of all, I am a beginner in Javascript, so if there are any uncertainities or unclarities in my message, please feel free to correct me.
I try to create an action to support my IBM Watson Assistant. Once called, the action should get some info from a http and give some answer back.
The "get" action was part of a Webhook, successfully deployed via Heroku as support for Dialogflow. I just changed it a little bit, to make the answer easier.
function main(req){
const http = require('http');
const API_KEY = '85324cac';
const prodname = req.prodname;
const reqUrl = encodeURI(`http://www.omdbapi.com/?t=${prodname}&apikey=${API_KEY}`);
http.get(reqUrl, (responseFromAPI) => {
let completeResponse = '';
responseFromAPI.on('data', (chunk) => {
completeResponse += chunk;
});
responseFromAPI.on('end', () => {
const movie = JSON.parse(completeResponse);
let dataToSend = prodname ;
dataToSend += (typeof movie.Title === "undefined") ? `Sorry the film is not in our database` : `${movie.Title} is a ${movie.Actors} starer ${movie.Genre} movie, released in ${movie.Year}. It was directed by ${movie.Director}.`;
return {answer: dataToSend};
});
});
//return {answer: dataToSend};
}
I was expecting an answer after the "return" action, but it is only showing empty values. I am pretty sure that the action does never get into the "http.get" part. When I remove the // and I invoke the code, it returns the following message: "dataToSend is not defined"; if I keep code as it is (with the comment), no errors pop up.
The omdapi is for free, but hosted in the US, in case that could matter.
Any ideas? In any case, thanks in advance.

Think you will find that your ibm function is completing before your external call to omdbapi is returning. Your best choice here is to use promises ( being new I expect you may not have used promises yet - would recommend reading https://cloud.ibm.com/docs/openwhisk?topic=cloud-functions-creating-javascript-actions#creating-javascript-actions
Not your complete program, leave you something to play with;
function main(req){
const http = require('http');
const API_KEY = '85324ca';
//const prodname = req.prodname;
const prodname = 'Game%20of%20Thrones&Season=1';
const reqUrl = 'http://www.omdbapi.com/?t=Game%20of%20Thrones&Season=1&apikey=85324cac';
//const reqUrl = encodeURI(`http://www.omdbapi.com/?t=${prodname}&apikey=${API_KEY}`);
return new Promise(function(resolve, reject) {
http.get(reqUrl, (responseFromAPI) => {
let completeResponse = '';
responseFromAPI.on('data', (chunk) => {
completeResponse += chunk;
// you could return answer here via resolve.
//var parsedData = JSON.parse(completeResponse);
//console.log(parsedData);
//resolve(parsedData);
})
responseFromAPI.on('error', (error) => {
console.log(error);
reject(error);
})
responseFromAPI.on('end', () => {
var parsedData = JSON.parse(completeResponse);
console.log(parsedData);
resolve(parsedData);
});
});
});
}

Related

Dialogflow is giving error unreachable code after return

I'am fetching the data via axios from mini crypto compare api for getting the crypto prices in dialogflow, but in my fulfillment code I'am receiving this error (if unreachable code after return). If I bring the if section inside the promise, than the agent.add() will not work and it give no response defined for the platform error in the console.
function priceFinder(agent) {
const data = agent.parameters[CRYPTO_NAMES];
let btc = '';
let eth = '';
return axios.get('https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,BNB,SOL,LUNA&tsyms=USD')
.then(response => {
btc = response.data.BTC.USD;
eth = response.data.ETH.USD;
});
if(data === "btc") {
agent.add(`BTC Price is : ${btc}`);
}
else if(data === "eth") {
agent.add(`ETH Price is : ${eth}`);
}
}
I was able to get your function working on my side, here is my updated code to your function based on the block you provide:
used library
const https = require('https');
main function
function getBitcoinInfo(agent){
const url= 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,BNB,SOL,LUNA&tsyms=USD';
const req = https.get(url, res => {
console.log(`statusCode: ${res.statusCode}`);
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
console.log(JSON.parse(data).BTC);
});
});
agent.add(`I found bitcoins!`);
}
IntentMap
let intentMap = new Map();
intentMap.set('Get Bitcoin Info', getBitcoinInfo);
agent.handleRequest(intentMap);
Useful Tips
return will execute that last statement and will not proceed further effectively making the rest code lines unreachable.
You can check the logs on by going to cloud functions -> my_deployed_ fullfillment -> logs
Latest results are always show at the end.
If no console message reach the log, make sure your function executes properly
Once you parse the json you can pick elements from it and fill variables and add it to your response. You can use JSON parse for it.
Useful links:
http-requests

Node js repeating a get request until there is a change in response

I will start off by saying I am a complete newbie when it comes to node js. I have the code below which currently sends a get request to the URL. It parses a specific value of the response and stores it as the search variable. It then uses the instagram api to change the bio on my instagram account to that search variable. However I would like the get request to continue until it detects a change. Ex. When the program is first run it fires off a get request. The first response value we get we will call 1. However after the first response I want it to continue to do get requests say every 5 seconds. The moment the response value changes from 1 to anything else I want that new value to be sent to the instagram bio. Can anyone help?
const { IgApiClient } = require("instagram-private-api")
const ig = new IgApiClient()
const https = require('https')
const USERNAME = "MYUSERNAME"
const PASSWORD = "MYPASS"
ig.state.generateDevice(USERNAME)
const main = async () => {
let url = "https://11z.co/_w/14011/selection";
https.get(url,(res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", async () => {
try {
search = await JSON.parse(body).value;
} catch (error) {
console.error(error.message);
};
});
}).on("error", (error) => {
console.error(error.message);
});
await ig.simulate.preLoginFlow()
await ig.account.login(USERNAME, PASSWORD)
// log out of Instagram when done
process.nextTick(async () => await ig.simulate.postLoginFlow())
// fill in whatever you want your new Instagram bio to be
await ig.account.setBiography(search)
}
main()
// code is written in main() so that I can use async/await
to be good citizen to the target endpoint:
have a look on exponential-backoff package - https://www.npmjs.com/package/exponential-backoff
A utility that allows retrying a function with an exponential delay between attempts.

Node js extract data from a nested function

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.

Can't add key from function to dictionary

My code:
var price = {};
function getPrice(price) {
const https = require('https');
var item = ('M4A1-S | Decimator (Field-Tested)')
var body = '';
var price = {};
https.get('https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item, res => {
res.on('data', data => {
body += data;
})
res.on('end', () => price ['value'] = parseFloat(JSON.parse(body).median_price.substr(1))); //doesnt add to dict
}).on('error', error => console.error(error.message));
}
price['test'] = "123" //adds to dict fine
getPrice(price)
console.log(price);
Output:
{ test: '123' }
as you can see, the "test: 123" gets added, but the "value: xxx" from the function doesn't. Why is that?
There are two main problems here:
You're redeclaring the variable inside your function so you're declaring a separate, new variable and modifying that so the higher scoped variable, never gets your .value property.
You're assigning the property inside an asynchronous callback that runs sometime later after your function has returned and thus your function actually returns and you do the console.log() too soon before you have even obtained the value. This is a classic issue with return asynchronously obtained data from a function in Javascript. You will need to communicate back that data with a callback or with a promise.
I would also suggest that you use a higher level library that supports promises for getting your http request and parsing the results. There are many that already support promises, already read the whole response, already offer JSON parsing built-in, do appropriate error detection and propagation, etc... You don't need to write all that yourself. My favorite library for this is got(), but you can see a list of many good choices here. I would strongly advise that you use promises to communicate back your asynchronous result.
My suggestion for fixing this would be this code:
const got = require('got');
async function getPrice() {
const item = 'M4A1-S | Decimator (Field-Tested)';
const url = 'https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=' + item;
const body = await got(url).json();
if (!body.success || !body.median_price) {
throw new Error('Could not obtain price');
}
return parseFloat(body.median_price.substr(1));
}
getPrice().then(value => {
// use value here
console.log(value);
}).catch(err => {
console.log(err);
});
When I run this, it logs 5.2.
You're actually console.logging .price before you're setting .value; .value isn't set until the asynchronous call fires.
You are declaring price again inside the function and also not waiting for the asynchronous task to finish.
const https = require("https");
const getPrice = () =>
new Promise((resolve, reject) => {
const item = "M4A1-S | Decimator (Field-Tested)";
let body = "";
return https
.get(
`https://steamcommunity.com/market/priceoverview/?appid=730&market_hash_name=${item}`,
res => {
res.on("data", data => {
body += data;
});
res.on("end", () =>
resolve(
parseFloat(JSON.parse(body).median_price.substr(1))
)
);
}
)
.on("error", error => reject(error));
});
const main = async () => {
try{
const price = await getPrice();
//use the price value to do something
}catch(error){
console.error(error);
}
};
main();

Azure Functions NodeJS: Https Request not working when using Azure Visual Studio Code Extension

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

Resources