I'm making a Messenger bot in NodeJS. I want users be able to request all their trains. The problem is that we want to execute a query before NodeJS sends a message to the user.
I searched for asynchronous functions
function handlePostback(sender_psid, received_postback) {
let response;
// Get the payload for the postback
let payload = received_postback.payload;
// Set the response based on the postback payload
switch(payload){
case "yes" :
let data = null
axios.get('http://api.irail.be/connections/?from=Mechelen&to=Puurs&date=010219&time=1650×el=departure&format=json&lang=en&fast=false&typeOfTransport=trains&alerts=false&resul1=1')
.then(function (response) {
// handle success
data = data.response;
})
response = {
"text": data.connections.arrival.name
}
break;
}
callSendAPI(sender_psid, response);
}
function callSendAPI(sender_psid, response) {
// Construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
// Send the HTTP request to the Messenger Platform
request({
"uri": "https://graph.facebook.com/v2.6/me/messages",
"qs": { "access_token": PAGE_ACCESS_TOKEN },
"method": "POST",
"json": request_body
}, (err, res, body) => {
if (!err) {
console.log('message sent!')
} else {
console.error("Unable to send message:" + err);
}
});
}
So as you can see, the script will already sending the message to the user on Messenger before the query is executed.
There are some unneccessary variables and problems in your code (data = data.response should probably be data = response.data, for example), this would be a modern version with async/await and arrow functions. You do not need a callback function in that case, callSendAPI will be called after the AJAX request. I have also removed the switch, because a simple if is sufficient:
const handlePostback = async (sender_psid, received_postback) => {
// Get the payload for the postback
const payload = received_postback.payload;
// Set the response based on the postback payload
if (payload === 'yes') {
try {
const response = await axios.get('http://api.irail.be/connections/?from=Mechelen&to=Puurs&date=010219&time=1650×el=departure&format=json&lang=en&fast=false&typeOfTransport=trains&alerts=false&resul1=1');
callSendAPI(sender_psid, {
'text': response.data.connections.arrival.name
});
} catch (err) {
console.log(err);
}
}
};
Side note: I would not use 2 different ways to do http requests, assuming you are using superagent too? Because http.request would be a possibility with Node.js, but request looks like superagent ;)
Put callSendAPI() in your Axios callback
function handlePostback(sender_psid, received_postback) {
let response;
// Get the payload for the postback
let payload = received_postback.payload;
// Set the response based on the postback payload
switch (payload) {
case "yes":
let data = null
axios.get('http://api.irail.be/connections/?from=Mechelen&to=Puurs&date=010219&time=1650×el=departure&format=json&lang=en&fast=false&typeOfTransport=trains&alerts=false&resul1=1')
.then(function(response) {
// handle success
data = data.response;
})
response = {
"text": data.connections.arrival.name
}
callSendAPI(sender_psid, response);
break;
}
}
Related
I'm trying to create function in AWS Lambda (node.js), which call some REST API, dan insert the API result to MySQL DB.
While the requirement is very simple, but I encounter some problem when deploying to AWS Lambda (not happening on my local machine), where my first API call only resulting only 1 data is inserted, while the second API call forward, it insert all 4 data as intended. I try various solution available on stack overflow, and all resulting the same.
Another problem is that the result is always {"message": "Internal server error"}, even though the data is inserted correctly on second API call forwards
Basically i don't have much experience with Node.js, so i would appreciate if anyone could help me.
'use strict';
const connection = require('serverless-mysql')({
config: {
host: 'xxxxxx.xxxxx.ap-southeast-1.rds.amazonaws.com',
user: 'xxx',
password: 'xxx',
database: 'xxx_db'
}
})
const axios = require('axios');
exports.handler = (event, context) => {
//Get Data From API
axios.get('https://xxx.xyz/wp-json/wp/v2/posts')
.then(res => {
const headerDate = res.headers && res.headers.date ? res.headers.date : 'no response date';
console.log('Status Code:', res.status);
console.log('Date in Response header:', headerDate);
//this should result 4 data
const posts = res.data;
posts.forEach(post => {
var sql = `INSERT INTO tbl_post(news_id, title, excerpt, content, category, image_link, modified_date, show_in_banner_F, show_in_list_F) VALUES ('${post.id}', '${post.title.rendered}', '${post.excerpt.rendered}', '${post.content.rendered}', '', '${post.yoast_head_json.og_image[0].url}', now(), 0, 0)`;
console.log(sql);
let insert_query = connection.query(sql);
});
console.log("finished");
connection.end();
let responseBody = { message: "OK" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify(responseBody)
};
return response;
})
.catch(err => {
console.log('Error: ', err.message);
let responseBody = { message: "Fail" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify(responseBody)
};
return response;
});
}
First of all a forEach loop will call connection.query(sql) function multiple times then exit the loop without actually waiting for each query to finish executing so you'll end up executing random number of queries each time you run this loop instead what you want to do is use async/await await connection.query(sql) in order to wait for each query inside the loop to finish executing before exiting the loop.
Also forEach loop is not designed for asynchronous code so you'll have to change that as well and use for...of instead. And you also have to use prepared statements using ? instead of inserting values with ${variable} to prevent sql injections.
for (const post of posts) {
const sql = "INSERT INTO tbl_post(news_id, title) VALUES (?, ?)";
const values = [post.id, post.title.rendered];
console.log(mysql.format(sql,values)); // This would log query after values substitution
await connection.execute(sql, values);
}
So the final code will look something like this:
exports.handler = async (event, context) => {
try {
//Get Data From API
const res = await axios.get("https://xxx.xyz/wp-json/wp/v2/posts");
const headerDate = res.headers && res.headers.date ? res.headers.date : "no response date";
console.log("Status Code:", res.status);
console.log("Date in Response header:", headerDate);
//this should result 4 data
const posts = res.data;
for (const post of posts) {
const sql = "INSERT INTO tbl_post(news_id, title) VALUES (?, ?)";
const values = [post.id, post.title.rendered];
console.log(mysql.format(sql, values)); // This would log query after values substitution
await connection.execute(sql, values); // Execute prepares statement first then executes it.
}
console.log("finished");
await connection.end();
let responseBody = { message: "OK" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
body: JSON.stringify(responseBody),
};
return response;
} catch (err) {
console.log("Error: ", err.message);
let responseBody = { message: "Fail" };
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
body: JSON.stringify(responseBody),
};
return response;
}
};
As a side note consider using transactions if u want to guarantee that all queries inside the loop either succeed or fail.
Pro tip: use Promise.all() if u want to execute multiple async functions at the same time not one after the other.
I have following function which calls the refresh service to get new token for authorization:
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if(!this.isRefreshingToken) {
this.isRefreshingToken = true;
return this.authService.refreshToken()
.subscribe((response)=> {
if(response) {
const httpsReq = request.clone({
url: request.url.replace(null, this.generalService.getUserId())
});
return next.handle(this.addTokenToRequest(httpsReq, response.accessToken));
}
return <any>this.authService.logout();
}, err => {
return <any>this.authService.logout();
}, () => {
this.isRefreshingToken = false;
})
} else {
this.isRefreshingToken = false;
return this.authService.currentRefreshToken
.filter(token => token != null)
.take(1)
.map(token => {
return next.handle(this.addTokenToRequest(request, token));
})
}
}
When the response is not undefined and request is returned back it does not call the new request
Ok the thing was that the bearer was quoted like below:
But I have still one issue the request does not invoke the new request, when I refresh the page it gives data with new token, instead like I previously had unauthorized error.
I'm working in a code with Promise to get JSON values, the code is working, but the output aways return between the words Promise { }.
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
console.log(info); //Output: Promise { [ { name: 'Gary'}, { name: 'John'} ] }
What I'm doing wrong?
I'd like my output was only as in the website:
[ { name: 'Gary'}, { name: 'John'} ]
==> UPDATE
It's works partially:
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
info.then(function(value) {console.log(value)});//output: [ { name: 'Gary'}, { name: 'John'} ]
but, I need to use the result outside of then to perform others queries operations using this Json result, I tried something like that:
info.then(function(value) { jsonfile = value });
console.log(jsonfile);//Output: jsonfile is not defined
You're console.logging the promise object. If you want the data from the resolve action, you can chain a function that uses that data.
ex:
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
//On resolve, perform some function to the returned value (This can be replaced with a function call)
info.then(function(value) {console.log(value)});
More examples and details can be found at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
Edit in response to comment (i don't have the rep to post comments): Promises are synchronous, while code not inside the promise or then() is not treated synchronously. This is worth mentioning because while you could a sign your value to a global variable or define your var 'value' before the promise, 'value' may not have been resolved by the promise yet, meaning you have introduced a race condition. Instead, if you need the data from your promise in another function (they need to run sequentially), that function should be invoked in your then().
Ex:
info.then(function(value){
otherfunction(value);
});
function otherfunction(val) {
//does whatever you need to value and will occur after the promise above resolves
}
info is a pending promise. You need to await of use .then on it
Sample code
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'http://httpbin.org/get',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
console.log(info); // Promise { <pending> }
info.then((result) => console.log(result)); // api result
I am new to angular 4 and REST API development. I have developed a Login API in back-end and it works fine when I call it using Postman:
In the front-end application which is an Angular 4 project, I have created a service to call this login API. Here is the method I created in this service:
sendCredential(username: string, password: string) {
const url = 'http://localhost:8080/authenticate/user';
const body = '{"username": "' + username + '", "password": "' + password + '"}';
const headers = new Headers(
{
'Content-Type': 'application/json'
});
return this.http.post(url, body, {headers: headers});
}
My first question is:
Is this the correct way to pass the json object and call this API?
And I also created a component which calls the method in the service. Here is the method/event-handler I created in this component:
onSubmit(uname: string, pwd: string) {
this.loginService.sendCredential(uname, pwd).subscribe(
res => {
this.loggedIn = true;
localStorage.setItem('PortalAdminHasLoggedIn', 'true');
location.reload();
},
err => console.log(err)
);
}
My second question is:
How should I check whether a token is returned back or an error?
Question 1:
You do not need to stringify the body object when you do a http.post() in angular. Just use a normal object will do, and the Http class will help you parse it internally:
sendCredential(username: string, password: string) {
const url = 'http://localhost:8080/authenticate/user';
//do not need to stringify your body
const body = {
username, password
}
const headers = new Headers(
{
'Content-Type': 'application/json'
});
return this.http.post(url, body, {headers: headers});
}
Question 2:
As for your error, note that Angular also catch every http error. and by http error, it means that any status code that is <200 or >=300 will be an error. So only status codes that is in between 200 and 300 is considered successful. Upon an error received, angular will throw an Observable error, which you will need to handle explicitly (which you did it correctly):
onSubmit(uname: string, pwd: string) {
this.loginService.sendCredential(uname, pwd).subscribe(
res => {
//token should be in your res object
this.loggedIn = true;
localStorage.setItem('PortalAdminHasLoggedIn', 'true');
location.reload();
},
err => {
//handle your error here.
//there shouldn't be any token here
console.log(error);
}
);
}
With your above code, you should receive your token in your successful callback, and it will be in the res object. If there's an error, no token should be received and you should handle the error at the error callback.
I'm writing a program in node js that does the following.
Get the Access token(a jwt)
Get the user firstName from that token
Pass this token and firstName to another method to post.
print the result.
My program is as below.
function getUserDetailsFromAccessToken(session) {
var token = session.user.accessToken;
try {
// parse this and get user attributes
var decoded = jwt.decode(token);
getTheUserProfile(decoded.firstname, token, session);
} catch (err) {
console.log(err);
}
}
var hostUrl= "myUrlToGetResult";
function getTheUserProfile(nameFromSession, token, session) {
console.log("Incomung values " + nameFromSession);
console.log("Inside getUsersProfile Block");
var jsonToPass = {
"accesstoken": token,
"domain": "DomainName",
"lanid": nameFromSession
};
console.log("json to pass " + JSON.stringify(jsonToPass));
var options = {
uri: hostUrl+ "/api/admin/GetUserInfo",
method: "POST",
json: jsonToPass
};
request(options, function (error, resp, body) {
console.log("resp code is " + resp.statusCode);
if (!error && resp.statusCode == 200) {
if (body) {
console.log("Insode B1");
console.log(body.firstName);
} else {
console.log("I am unable to authenticate you. please disable the skill and re link your account");
}
} else {
console.log(error);
}
});
}
when I run this program, I'm able to print only till console.log("json to pass " + JSON.stringify(jsonToPass));, after that I'm unable to get any result from request() block.
please let me know where am I going wrong and how can I fix it.