I have a problem with this piece of code:
async getDomains (req, res) {
try {
let domains = await Domain.findAll({ raw: true })
for(domain of domains) {
console.log('1')
var options = {
host: domain.name,
port: 443,
method: 'GET'
};
var request = https.request(options, (res) => {
console.log('2')
console.log('iam here')
domain.ssl = {
'valid_until': res.connection.getPeerCertificate().valid_from
}
});
console.log('3')
request.end();
}
console.log('4')
res.send(domains)
} catch(err) {
res.status(400).send({
error: err
})
}
},
The output should be 1, 2, 3, 4 but instead I got 1, 3, 4, 2.
Does anyone have an idea how to achieve that?
You've provided a callback to request, so it's going to send the request and move on, only logging 2 once a response is received. You'll want to use some sort of Promise or async/await to wait for the response from your request.
var request = await (new Promise((resolve, reject) => {
https.request(options, (res) => {
console.log('2');
console.log('i am here');
domain.ssl = { /* stuff */ };
resolve();
});
));
Related
I am using the package "fcm-node" in order to send notifications to certain device id.
the sendNotification function is as follows:
const FCM = require('fcm-node');
const serverKey = process.env.SERVER_KEY;
const fcm = new FCM(serverKey);
function sendNotification(registrationToken, title, body, type, key) {
const message = {
to: registrationToken,
collapse_key: key,
notification: {
title: title,
body: body,
delivery_receipt_requested: true,
sound: `ping.aiff`
},
data: {
type: type,
my_key: key,
}
};
fcm.send(message, function (err, value) {
if (err) {
console.log(err);
return false;
} else {
console.log(value);
return value;
}
});
};
module.exports = {
sendNotification
};
The api function I use to call this function is as follows:
router.get('/test', async (req, res, next) => {
const promise = new Promise((resolve, reject) => {
let data = sendNotification('', 'dfsa', 'asds', 'dfas', 'afsdf');
console.log(data)
if (data == false) reject(data);
else resolve(data);
});
promise
.then((data) => { return res.status(200).send(data); })
.catch((data) => { return res.status(500).send(data) })
});
When I console.log the "err" and "value" from the sendNotification, I get either of the followings:
{"multicast_id":4488027446433525506,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1652082785265643%557c6f39557c6f39"}]};
{"multicast_id":8241007545302148303,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
In case it is successful, I made sure that the device is receiving the notification.
The problem is in the api's data. It is always "undefined" and weither send notification is successful or not I get the 200 Ok status.
What seems to be the problem?
You can't return anything from the function (err, value) {} callback of a node-style asynchrnous function.
Your sendNotification() function needs to return a promise. util.promisify() makes the conversion from a node-style asynchronous function to a promise-returning asynchronous function convenient. Note the return, it's important:
const FCM = require('fcm-node');
const serverKey = process.env.SERVER_KEY;
const fcm = new FCM(serverKey);
const { promisify } = require('util');
fcm.sendAsync = promisify(fcm.send);
function sendNotification(registrationToken, title, body, type, key) {
return fcm.sendAsync({
to: registrationToken,
collapse_key: key,
notification: {
title: title,
body: body,
delivery_receipt_requested: true,
sound: `ping.aiff`
},
data: {
type: type,
my_key: key,
}
});
}
module.exports = {
sendNotification
};
Now you can do what you had in mind
router.get('/test', async (req, res, next) => {
try {
const data = await sendNotification('', 'dfsa', 'asds', 'dfas', 'afsdf');
return res.status(200).send(data);
} catch (err) {
return res.status(500).send(err);
}
});
Maybe it will help, at first try to return your response (the promise) in sendNotification, as actually you have a void function, that's why it's always undefined and after in your route
router.get('/test', async (req, res, next) => {
try {
const data = sendNotification('', 'dfsa', 'asds', 'dfas', 'afsdf');
if (data) {
return res.status(200).send(data);
}
} catch(err) {
return res.status(500).send(err);
}
});
I would like both resolve() to return {valid_to: cert.valid_to, statusCode, statusMessage} and reject() should return {error: -1, statusCode, statusMessage}.
Question
How can I do that, when statusCode, statusMessage are in a different scope?
const https = require('https');
(async () => {
const options = {
hostname: "github.com",
port: 443,
path: '/',
method: 'GET',
timeout: 1000
};
options.agent = new https.Agent(options);
let valid_to = await new Promise((resolve, reject) => {
const req = https.request({
...options, checkServerIdentity: function (host, cert) {
resolve(cert.valid_to);
}
}).on('error', error => {
reject(-2);
});
req.on("timeout", chunk => {
reject(-1);
});
req.on('response', response => {
console.log(response.statusCode);
console.log(response.statusMessage);
});
req.end();
}).catch(error => {
console.log(error);
return -3;
});
})();
I will do something like this.
Edit: You need to specify res.on('data') in the https.request Object. Otherwise, timeout will always emit because there is no activity from the stream.
You can resolve in res.on("data") or res.on("end") and it is up to your use case.
res is an IncomingMessage object is created by http.ClientRequest and passed as the first argument to the 'request' and 'response' event respectively.
req is A reference to the original http.ClientRequest.
Both streams can emit events and you may handle them separately.
Also, when you reject the Promise, you actually cannot get the statusCode and StatusMessage from the req because there is an error in the req and the .on("response") will not be emitted. So, you need to customize the statusCode and statusMessage yourself.
const https = require("https");
// {valid_to: cert.valid_to, statusCode, statusMessage}
// {error: -1, statusCode, statusMessage}.
(async () => {
const options = {
hostname: "githubasdfa.com",
port: 443,
path: "/",
method: "GET",
timeout: 1000,
};
options.agent = new https.Agent(options);
try {
const response = await new Promise((resolve, reject) => {
let valid_to;
let statusCode;
let statusMessage;
const req = https
.request(
{
...options,
checkServerIdentity: function (host, cert) {
valid_to = cert.valid_to;
},
},
res => {
res.on("data", chunk => {
resolve({
valid_to,
statusCode,
statusMessage,
});
});
res.on("end", () => {
console.log("No more data in response.");
});
}
)
.on("error", err => {
console.log(err);
reject({
error: -2,
statusCode: "custom code",
statusMessage: "unhandled error",
});
})
.on("timeout", chunk => {
reject({
error: -1,
statusCode: "custom code",
statusMessage: "unhandled error",
});
})
.on("response", response => {
statusCode = response.statusCode;
statusMessage = response.statusMessage;
})
.end();
});
console.log(response);
} catch (error) {
console.log(error);
}
})();
I want to test the response in nodejs using request, I tried doing some unit test but I cannot get inside the request function
let request = require('request');
function doRequest() {
new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if(!err) {
// Test error
} else {
// Test success
}
})
});
}
I think u can use nock instead of request, but since you need to use request module here is an example
const request = require('request');
const requestCB = (resolve, reject) => async (error, response, body) => {
if (!error) {
// Test success
console.log(response);
resolve();
} else {
// Test error
console.error(error);
// reject();
}
};
function doRequest(options) {
new Promise((resolve, reject) => {
request(options, requestCB(resolve, reject));
})
.then(() => {})
.catch((err) => {});
}
const options = {
/* you can use this url if you want to test
* https://api.github.com/repos/request/request
*/
url: 'https://restcountries.eu/rest/v2/all',
headers: {
'User-Agent': 'request',
},
};
// call doRequest
doRequest(options);
I've a problem with Axios and foreach loop. My Api provider support only 5 contemporary call so I want call one by one but when execute this code the each body not wait the the finish call function and receive the error code 429. How can resolve this? thanks.
async function call(url) {
var options = {
method: 'GET',
url: url,
auth: {
username: '*****',
password: '*****'
}
};
var response = await axios.request(options);
print(response.data["Id"])
}
app.get('/save', async (req, res) => {
var options = {
method: 'GET',
url: 'getListUser',
auth: {
username: '***',
password: '***'
}
};
var response = await axios.request(options);
response.data["users"].forEach( async (val) => {
console.log("ENTER");
var url = 'getDetailUser' + val["id"];
var res = await call(url); // <- How to wait finish this?
console.log("EXIT")
}, (err) => {
console.log(err)
})
res.status(200).send("ok").end();
});
FYI, Promise couldn't work with loop that involves callback ie forEach. Alternatively, you could use for of
try {
for (const val of response.data['users']) {
console.log("ENTER");
var url = 'getDetailUser' + val["id"];
var res = await call(url);
console.log("EXIT")
}
} catch (error) {
console.log(error)
}
I would say answer by #Ifaruki is correct with a minor change
await Promise.allSettled(response.data["users"].map(val => {
var url = 'getDetailUser' + val["id"];
return call(url);
}))
For details check the difference. In some cases Promise.all might work but if any of the Promise fails, the whole result of Promise.all will be a rejection.
The code 429 can be resolved by looking at 429 Too Many Requests
Promise.all() is the way
await Promise.all(response.data["users"].map(val => {
var url = 'getDetailUser' + val["id"];
return call(url);
}))
I have a node js with express application. I need to expose a rest endpoint that will return the response of a http call. Whatever I try it returns before the http request. Can you please help
app.all('/query', function(req, res){
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http.get('http://test.com', (response) => {
let sdata = '';
response.on('data', (fragments) => {
sdata += fragments;
});
response.on('end', () => {
let response_body = sdata;
resolve(response_body.toString());
});
response.on('error', (error) => {
// promise rejected on error
reject(error);
});
});
});
urlCall.then((response) => {
var responseData=response;
res.json(responseData);
res.end();
}).catch((error) => {
console.log(error);
res.end();
});
}
Your code should work, but I suspect a request error not being handled (the error event handler being missing on your request)
You can try moving the error handler from the response to the request
app.all("/query", function (req, res) {
// here i need to make a http call
let urlCall = new Promise((resolve, reject) => {
http
.get("http://test.com", (response) => {
let sdata = "";
response.on("data", (fragments) => {
sdata += fragments;
});
response.on("end", () => {
let response_body = sdata;
resolve(response_body.toString());
});
})
.on("error", (error) => { // handling request errors
console.error("error");
// promise rejected on error
reject(error);
});
});
urlCall.then(
(response) => {
res.json(response);
res.end();
},
(error) => {
console.log(error);
res.end();
}
);
});