I am trying to get the response data from 3 APIs which is depended on each other. when I call each external API the response has an array of external APIs, as I need all the 3 external APIs responses.
Here is the code:
const options = {
JSON: true,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
"Authorization": process.env.authID,
},
body: {},
uri: ''
};
app.post('/sync', async (req, res) => {
try {
let getFirstAPI = Object.assign({}, options);
getFirstAPI.uri = 'https://abcde'; // first API
request(getFirstAPI, function (err, httpResponse, body) {
Promise.all(body.projects.map((prct) => getVuln(prct))).then(rs => {
res.JSON({ message: rs });
});
});
}
});
async function getVuln(prodId) {
try {
let getSecondAPI= Object.assign({}, options);
getSecondAPI.uri = 'abcd' + prodId.id + '/abc'; //Second API
return await new Promise((resolve, reject) => {
request(getSecondAPI, function (err, httpResponse, body) {
let final = {
products: [],
dataList: []
}
final.products.push({
product_id: prodId.id,
product_name: prodId.abc,
product_ver: prodId.wdc
})
body.issues.forEach(csa => {
final.dataList.push({
product_id: prodId.id,
version: csa.Versions,
name: csa.Name,
pathJsonValue:await getPathfun(csa.link) //Here i am getting error "SyntaxError: Unexpected identifier"
});
});
resolve(final);
});
})
}
}
function getPathfun(urlStr) {
let getPathUrl = Object.assign({}, options);
getPathUrl.url = urlStr;
return new Promise((resolve, reject) => {
request(getPathUrl, function (err, httpResponse, body) {
resolve(body);
});
});
}
at the "pathJsonValue" I am getting an error while running the code, if I remove the await then "pathJsonValue" is empty. I have attached the error image below. please help me with this issue.
request(getSecondAPI, function (err, httpResponse, body) {
Callback must be an ASYNC function to have the possibility to use 'await' keyword inside it.
'await' keyword doesnt work in "forEach" method. Use for...of loop instead.
Related
Error: function uses multiple asynchronous interfaces: callback and
promise
to use the callback interface: do not return a promise
to use the promise interface: remove the last argument to the function
I'm trying to write a cucumber test to one of my GET node API, and keep getting the above, looked at few GitHub and stack-overflow posts, and could not understand the issue, below are my test method details.
App.ts
async getSsoId(refId: any): Promise<string> {
let ssoId = '';
const secrets = await this.vaultService.getClientSecrets();
this.decrypt(refId, secrets.encryption_secret, secrets.encryption_Id).then((value: any) => {
ssoId = value;
});
return ssoId;
}
api.get('/status', async (req, res) => {
let id;
const statusCode = 200;
try {
id = await this.getId(String('123456'));
} catch (err: any) {
throw new ApiError('Error fetching id');
}
try {
const item = await dbService.getItem(id);
if (item) {
statusCode = 201;
} else {
statusCode = 202;
}
} catch (err: any) {
throw new ApiError(
'The API encountered an error while attempting to communicate with the DB service.'
);
}
res.status(statusCode).send(statusCode);
});
Step Definition:
Given('a valid customer', function () {});
When("I call the get-id api", { timeout: 2 * 5000 }, async function (val) {
util.callGetIdAPI().then((response) => {
this.setApiResponseStatus(response.status);
this.setResponseBody(JSON.stringify(response.body));
});
});
Then("the apiResponseStatus should be <apiResponseStatus>", function (status) {
assert.strictEqual(status, this.apiResponseStatus);
});
Then("the responseBody should be {string}", function (responseBody) {
assert.equal(responseBody, this.responseBody);
});
Util Function
callGetIdAPI = async () => {
const headers = {
'Content-Type': 'application/json;v=1',
Accept: 'application/json;v=1'
}
const client = await getClient('url');
const options = {
method: 'GET',
headers: headers,
version: 3
};
let response;
try {
response = await client.get('/status', options);
return {
status: response.statusCode,
body: response.body
};
} catch(error) {
return {
status: error.statusCode,
body: {
error: {
id: error.id,
message: error.message
}
}
}
}
};
I'm new to this and trying to understand how multiple Premisses and Callbacks works in parallel, any thoughts or inputs on what possibly cause the error, or am I missing anything ??
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 months ago.
I am trying to make an API call for each loop from the first API that provides an array of responses. In the end results, all the responses from the second API need to be pushed to an array.
const request = require('request');
const options = {
JSON: true,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
"Authorization": process.env.authID,
},
body: {},
uri:''
};
app.post('/vuln/sync', (req, res) => {
try {
let getProject = Object.assign({}, options);
getProject.uri = 'first API';
getProject.body = {
}
request(getProject, function (err, httpResponse, body) {
if (err) {
return console.error(err);
}
let finalData = [];
body.projects.forEach(function (prct) {
let valu = getVuln(prct);
finalData.push(valu)
})
res.send({ message: finalData }); /// now this data is empty
});
} catch (err) {
console.error(err);
return res.status(500).send(err);
}
});
function getVuln(res) {
try {
let getSecondAPi = Object.assign({}, options);
getSecondAPi.body = {
}
getSecondAPi.uri = 'second API'+res.id+'/XYZ';
request(getSecondAPi, function (err, httpResponse, body) {
if (err) {
return console.error(err);
}
console.log(body); //
return body;
});
} catch (err) {
if (error.status !== 409) {
console.log(error);
}
}
}
The current code gives me the empty "{message : [null, null]}", can you please help me with this?
There are 2 issues:
you use sync forEach to call async functions.
getVuln function should return promise but now it return undefined
I would write like that:
async function getVuln(res) {
try {
let getSecondAPi = Object.assign({}, options);
getSecondAPi.body = {
}
getSecondAPi.uri = 'second API'+res.id+'/XYZ';
return await new Promise((resolve,reject)=>{
request(getSecondAPi, function (err, httpResponse, body) {
if (err) {
console.error(err);
reject(error)
}
console.log(body);
resolve(body);
});
})
} catch (err) {
if (error.status !== 409) {
console.log(error);
}
}
}
then e.g make a parallel requests:
const finalData = await Promise.all(body.projects.map((prct)=>getVuln(prct)))
//or
Promise.all(body.projects.map((prct)=>getVuln(prct))).then(res=>{finalData=res})
I'm struggling with async await block. I have looked the multiple resources on the net but I just can't understand what I'm doing wrong here :
app.post('/api/my-api', async (req, res, next) => {
try {
filecontent = req.files.userFile.data.toString('utf8')
console.log("decoded file : ", filecontent);
let encoded_file = new Buffer(filecontent).toString('base64');
var apiClass = new RPC("http://localhost:1006", "my-api");
//the asynchronous function :
const answ = await apiMethod.call("api", [{"file" : encoded_file, "fileName":req.files.userFile.name}], res.json);
//the code I'd like to execute only after the previous function has finished :
console.log("answer : ", answ);
console.log("answering...");
res.json(answ);
} catch (err) {
console.log(err);
}
Apparently my console.log are executed before the the await line is done. I can tell because there's a console.log() in the asynchronous function too, and my res.json is sent before I receive answ.
How do I make sure the asynchronous function finishes before the rest of the code ?
Edit : here is the apiMethod.call function :
call(id, params) {
let options = {
url: this.url,
method: "post",
headers:
{
"content-type": "text/plain"
},
body: JSON.stringify( {"jsonrpc": "2.0", "id": id, "method": this.procedure, "params": params })
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error('An error has occurred: ', error);
} else {
console.log('Post successful: response: ', body);
}
});
}
Issue is in call function. Since it has async code (request call), it should be wrapped in promise which should resolve from callback function of request.
Updating call function to something like below should help:
function call(id, params) {
return new Promise((resolve, reject) => {
let options = {
url: this.url,
method: "post",
headers: {
"content-type": "text/plain",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: id,
method: this.procedure,
params: params,
}),
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error("An error has occurred: ", error);
reject(error);
} else {
console.log("Post successful: response: ", body);
resolve(body);
}
});
})
}
I have been trying to get this to work for while now, my request promise is not returning the streamer it has found. When i console.log(streamer) inside of the .then part of the request it works. Any ideas?
function getStreamerByName(name){
var streamer;
var options = {
url: "https://api.twitch.tv/helix/users?login=" + name,
method: 'GET',
headers: {
'Client-ID': 'CLIENT_ID',
}
};
requestP(options)
//SPREAD
.spread(function(res, body) {
streamer_data = JSON.parse(body);
})
//THEN
.then(function (body) {
streamer = ({
twitch_id: streamer_data["data"][0]["id"],
name: streamer_data["data"][0]["display_name"],
image: streamer_data["data"][0]["profile_image_url"],
description: streamer_data["data"][0]["description"]
});
Streamer.create(streamer);
return streamer;
})
//CATCH
.catch(function (err) {
console.log(err);
return streamer;
});
}
You need to return the promise chain:
function getStreamerByName(name){
var streamer;
var options = {
url: "https://api.twitch.tv/helix/users?login=" + name,
method: 'GET',
headers: {
'Client-ID': 'CLIENT_ID',
}
};
//A return was added here
return requestP(options)
//SPREAD
.spread(function(res, body) {
streamer_data = JSON.parse(body);
})
//THEN
.then(function (body) {
streamer = ({
twitch_id: streamer_data["data"][0]["id"],
name: streamer_data["data"][0]["display_name"],
image: streamer_data["data"][0]["profile_image_url"],
description: streamer_data["data"][0]["description"]
});
Streamer.create(streamer);
return streamer;
})
//CATCH
.catch(function (err) {
console.log(err);
return streamer;
});
}
When you all this function externally, you'll also need to use a .then() to get the result or use an async function and await.
so
async caller() {
var value = await getStreamerByName('stuff')
}
or
caller() {
getStreamerByName('stuff').then((result) => { //do stuff })
}
I'm not sure if this is what you mean but i just tried this and its not getting the streamer still, its coming up with an error message saying:
TypeError: Cannot read property 'then' of undefined
function getStreamers() {
getStreamerByName(search).then((streamer) => {
console.log(streamer)
})
}
getStreamers();
async function getStreamerByName (name) {
return new Promise(function (resolve, reject) {
Your code
});
}
Var myStreamer = getStreamerByName ('myStreamer');
I am trying to call a function inside a for loop and the problem is that the function is called after the loop was finished.
Taking the below as an example, it prints to the console:
here1
here1
here2
here2
Instead of
here1
here2
here1
here2
report.forEach(item => {
item.runs.forEach(run => {
waComplianceBusiness(req, run.id, (err, res) => {
const compliance = res.data.overviews[0].compliance;
var failureList = [];
compliance.forEach((rule, index) => {
console.log('here1');
waRuleOverview(req, run.id, rule.id, (err, res) => {
console.log('here2');
// handle the response
});
});
});
});
});
How can I fix this?
Please let me know if I need to provide additional information
Here is the complete code:
export default (req, callback) => {
const report = req.body.webAudits;
if(report.length > 0) {
report.forEach(item => {
item.runs.forEach(run => {
waComplianceBusiness(req, run.id, (err, res) => {
const compliance = res.data.overviews[0].compliance;
if(compliance) {
var failureList = [];
compliance.forEach((rule, index) => {
if(rule.pagesFailed > 0) {
waRuleOverview(req, run.id, rule.id, (err, res) => {
const failedConditions = res.data.failedConditions;
const ruleName = res.data.ruleName;
failedConditions.forEach((condition, failedIndex) => {
const request = {
itemId: condition.conditionResult.id,
itemType: condition.conditionResult.idType,
parentId: condition.conditionResult.parentId,
parentType: condition.conditionResult.parentType
}
const body = {
runId: run.id,
ruleId: rule.id,
payload: request
}
waConditionOverview(req, body, (err, res) => {
const description = res.data.description;
const conditionValues = res.data.conditionValues[0];
var actualValue = conditionValues.value;
if(actualValue == "") {
actualValue = 'empty';
}
if(description.idType == "variable") {
var failureObj = {
ruleName: ruleName,
expected: description.name + ' ' + description.matcher + ' ' + description.expected[0],
actual: description.name + ' ' + description.matcher + ' ' + actualValue
};
}
else if(description.idType == "tag") {
var failureObj = {
ruleName: ruleName,
expected: description.name + '\n' + description.matcher,
actual: actualValue
};
}
failureList.push(failureObj);
});
});
});
}
if(key + 1 == compliance.length) {
console.log(failureList);
}
});
}
});
});
});
}
}
These are the callback functions:
export function waComplianceBusiness(req, runId, callback) {
const apiToken = req.currentUser.apiToken;
const payload = {
'Authorization': 'api_key ' + apiToken
}
const options = {
'method': 'get',
'gzip': true,
'headers': payload,
'content-type': 'application/json',
'json': true,
'url': 'api_url'
}
request(options, (error, response, body) => {
callback(null, body);
});
}
export function waRuleOverview(req, runId, ruleId, callback) {
const apiToken = req.currentUser.apiToken;
const payload = {
'Authorization': 'api_key ' + apiToken
}
const options = {
'method': 'get',
'gzip': true,
'headers': payload,
'content-type': 'application/json',
'json': true,
'url': 'api_url'
}
request(options, (error, response, body) => {
callback(null, body);
});
}
export function waConditionOverview(req, body, callback) {
const apiToken = req.currentUser.apiToken;
const payload = {
'Authorization': 'api_key ' + apiToken
}
const options = {
'method': 'post',
'gzip': true,
'headers': payload,
'body': body.payload,
'content-type': 'application/json',
'json': true,
'url': 'api_url'
}
request(options, (error, response, body) => {
callback(null, body);
});
}
My goal is to return the failureList array after the loop over the compliance array is done
I found a similar question here but not sure if that would work in my case and I don't really know how to implement the promises
The for loop executes the statements inside the scope sequentially. But it does not wait for the the function calls to complete, it continues with the next statement(i.e works asynchronously). That is why the result is as such. You can make it work synchronously using Promises or by using the async module.
As it is not clear what you are going to perform in the function call and what you want the statements to do, I am not able to suggest either of which. . asyn.each is usually preferred for making the for loop execute synchronously. And promises are used when you want to wait for the function to finish executing and then perform operation. You might want to look at their documentation
Promises|MDN
async.each
Thank you, Ragul
If you want to do it in sequence use async.eachOfSeries
async.eachOfSeries(report, function(item, index, eachOfCallback1){
async.eachOfSeries(item.runs, function(run, index, eachOfCallback2){
waComplianceBusiness(req, run.id, (err, res) => {
var failureList = [];
async.eachOfSeries(compliance, function(rule, index, eachOfCallback3){
console.log('here1');
waRuleOverview(req, run.id, rule.id, (err, res) => {
console.log('here2');
return eachOfCallback3(err);
});
}, function(err){
if(err)
return eachOfCallback2(err);
else return eachOfCallback2();
});
});
}, function(err){
if(err)
return eachOfCallback1(err);
else return eachOfCallback1();
})
}, function(err){
// handle final response
})
If you want to optimise the process take a look at async.parallel