Returning nested promises to another function - javascript

I have a NodeJS application and I think I have an issue with returning from inside a nested Promise.
As below, the getToken function is working. It calls another function to retrieve a password. After this, it uses the password value when making a GET call.
We then successfully get a token and we print the body to the console. This works.
However, I now have the challenge of passing the value of body which is my token, to another method for later consumption. printBodyValue currently fails and fails with an 'undefined' error.
How can I pass the value from deep inside getToken to printBodyValue
getToken: function() {
module.exports.readCredentialPassword()
.then(result => {
var request = require('request-promise');
var passwd = result;
var basicAuthData = "Basic " + (new Buffer("fooUser" + ":" + passwd).toString("base64"));
var options = {
method: "GET",
uri: ("http://localhost:8001/service/verify"),
followRedirects: true,
headers: {
"Authorization": basicAuthData
}
};
return request(options)
.then(function (body) {
console.log("Token value is: ", body);
return body;
})
.catch(function (err) {
console.log("Oops! ", err);
});
});
}
printBodyValue: function() {
module.exports.getToken().then(function(body) {
console.log("Token value from printBodyValue is: \n", body);
});
}

In getToken, instead of using the nested promise anti-pattern, chain your promises instead, and return the final promise, so that you can then consume the promise and use its resolved value:
(also, since you're using ES6, prefer const over var)
getToken: function() {
return module.exports.readCredentialPassword()
.then(result => {
const request = require('request-promise');
const passwd = result;
const basicAuthData = "Basic " + (new Buffer("fooUser" + ":" + passwd).toString("base64"));
module.exports.log("Sending Request: ", jenkinsCrumbURL);
const options = {
method: "GET",
uri: ("http://localhost:8001/service/verify"),
followRedirects: true,
headers: {
"Authorization": basicAuthData
}
};
return request(options);
})
.then(function(body) {
console.log("Token value is: ", body);
// the return value below
// will be the final result of the resolution of
// `module.exports.readCredentialPassword`, barring errors:
return body;
})
.catch(function(err) {
console.log("Oops! ", err);
});
}
printBodyValue: function() {
module.exports.getToken().then(function(body) {
console.log("Token value from printBodyValue is: \n", body);
});
}

Related

Fetch always returns 200 even when server returns 400 or some error. How to get correct result?

The bounty expires in 2 days. Answers to this question are eligible for a +100 reputation bounty.
Ganesh Putta is looking for an answer from a reputable source.
I have below function in frontend, I have to make some api call here in this function. When i checked the called function via command prompt, the server api call returns 400. but the function in frontend fetch always returning 200 instead of error.
Thanks in advance.
Please guide to how to correct response which is thrown from server call?
Here is the frontend function.
function processFile() {
var fileToLoad = document.getElementById("fileToLoad").files[0];
var url;
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
var rows = fileLoadedEvent.target.result.split("\n");
for (var i = 0; i < rows.length - 1; i++) {
var cells = rows[i].split(",");
//alert(cells);
console.log("AgentteamID=" + cells[0] + " SkilltargetID=" + cells[1] + " flag=" + cells[2]);
url = "/updateAgentTeam?agentTeamID=" + cells[0] + "&skillTargetID=" + cells[1] + "&flag=" + cells[2],
//alert(url);
console.log("URL=" + url);
const response = fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}).then(response =>
response.json().then(data => ({
data: "success",
status: response.status
})).then(res => {
var statusCode = JSON.stringify(res.status);
//$('#Success_comment').html(JSON.stringify(res)+"---"+url;);
//alert(url);
document.getElementById("Success_comment").value += JSON.stringify(res) + "---" + url;
console.log("final result " + JSON.stringify(res) + "---" + url);
}))
}
}
fileReader.readAsText(fileToLoad, "UTF-8");
}
Here is the Server side code. For now we are using only Flag A block
app.get("/updateAgentTeam", (req, res) => {
var skillTargetID = req.query.skillTargetID;
console.log("req.query.skillTargetID =" + req.query.skillTargetID);
var agentTeamID = req.query.agentTeamID;
var flag = req.query.flag;
var finalurl = "http://198.18.133.11/unifiedconfig/config/agentteam/" + agentTeamID;
var xml;
//console.log("finalurl ="+finalurl);
axios({
url: finalurl,
method: "get",
auth: {
username: "xxx",
password: "yyy"
},
})
.then(async(response) => {
xml = response.data;
//console.log("after calling xml is "+JSON.stringify(xml));
res.send(response.data);
if (flag === 'D') {
agentremovedXML = removeAgentFromXML(xml, skillTargetID);
//newxml=removeAgentFromXML(xml);
//console.log("Final Agent remove XML "+JSON.stringify(agentremovedXML));
postAgentToTeam(agentTeamID, agentremovedXML);
//setSuccessJSON();
} else if (flag === 'A') {
AgentXML = await generateAgentXML(skillTargetID);
console.log("Returned agent xml is " + JSON.stringify(AgentXML));
console.log("xml where agent to be add " + JSON.stringify(xml));
if (JSON.stringify(xml.agentCount) == 0) {
AgentXML = {
"agent": [AgentXML]
};
xml.agents = [AgentXML];
console.log("xml with zero agents " + JSON.stringify(xml));
} else {
xml.agents[0].agent.push(AgentXML);
console.log("Compare " + JSON.stringify(xml));
}
console.log("xml send to postAgentToTeam is " + xml);
postAgentToTeam(agentTeamID, xml);
//postAgentToTeam(agentTeamID,agentXML);
}
})
.catch((err) => {
res.status(400);
res.send(err.response.status);
console.log(err);
//setErrorJSON()
});
});
async function postAgentToTeam(agentTeamID, xml) {
var teamurl = "http://198.18.133.11/unifiedconfig/config/agentteam/" + agentTeamID;
//console.log("final XML is "+JSON.stringify(xml));
xml.agents = xml.agents[0].agent.map(agent => ({
agent: agent
}));
var updatedJSONwithAgentTags = JSON.stringify(xml, null, " ");
//console.log("newwwwwwwwwwwwwwwwwwwwww "+ updatedJSONwithAgentTags);
//return;
js2xml = json2xml(JSON.parse(JSON.stringify(xml)));
json2 = "<agentTeam>" + js2xml + "</agentTeam>";
//console.log("newwww converted XML "+json2);
response = await axios({
url: teamurl,
method: "put",
auth: {
username: "administrator#dcloud.cisco.com",
password: "C1sco12345"
},
data: json2,
headers: {
'Content-Type': 'application/xml; charset=utf-8'
}
})
.then(response => {
console.log("hellooo " + response.status);
})
.catch((err) => {
console.log(err.response.data.apiError);
console.log("error res is " + err.response.status);
});
}
There's a quite few things wrong.
Inside your first .then(async(response) => {, you set the xml to response.data then right away call res.send(response.data) (200) which is the end of the execution order for Express. However, you go on to do a bunch of other things after the res.send (200) is sent. You should not do that.
You need to restructure your code. Starting from the top-down keeping in mind that all promises need to be returned and that res.send() is the end of the block. Throwing a 400 after this won't matter since Express already sent the response to the client.
You're also mutating theresponse output from Axios which is bad practice. Create a new one if you need to mutate it:
const newResponse = { ...response };
Additionally, this mixing of .then().catch() with an initial await is not how you should handles promises and would cause problems.
Don't write them like this:
response = await axios({}).then().catch().
Either do this:
axios({ // <--- no await, no let response =
url: finalurl,
method: "get",
auth: {
username: "xxx",
password: "yyy"
},
})
.then(async(response) => {
// res.send, etc.
}).catch((e) => {
// handle errors
});
Or use try / catch blocks like this:
try {
const results = await axios({
url: teamurl,
method: "put",
auth: {
username: "administrator#dcloud.cisco.com",
password: "C1sco12345"
},
data: json2,
headers: {
'Content-Type': 'application/xml; charset=utf-8'
}
}); // <--- the end.
console.log({results});
} catch (e) {
// handle errors
console.log(e);
}

Async/await/promises problem with MSAL.JS

I'm trying to write a function to acquire the Bearer token and return the headers before calling an API on behalf of the user, using MSAL.JS.
I think I'm struggling with the await/async/promises. Stuff I'm not really familiat with, at least in JS.
This is the function (I omitted the ssoSilent/login's part):
const prepareAPI = async (type) => {
const ssoRequest = {
loginHint: _userUpn,
scopes: ["openid", "profile", "user.read", _APIscope]
};
// if the user is already logged in you can acquire a token
if (msalInstance.getAccount()) {
var tokenRequest = {
scopes: [_APIscope]
};
try {
const resp = await msalInstance.acquireTokenSilent(tokenRequest);
console.log(resp.accessToken);
var headers = new Headers();
var bearer = "Bearer " + resp.accessToken;
headers.append("Authorization", bearer);
resolve({
method: type,
headers: headers
});
} catch (err) {
// could also check if err instance of InteractionRequiredAuthError if you can import the class.
if (err.name === "InteractionRequiredAuthError") {
return msalInstance.acquireTokenPopup(tokenRequest)
.then(response => {
return getOpts(response.accessToken, type);
})
.catch(err => {
console.log("Something went wrong:" + err);
});
}
}
} else {
// ... RequestSSO, loginPopup, etc,
}
};
I call the function like this:
prepareAPI("GET")
.then(opts => console.log("Headers: ", opts));
I get the token in the console.log(resp.accessToken) just before the resolve.
But "opts" is undefined, and looks like is executed before the token retrieval.
I don't see any reason in your code to use anything other than async await. Simplify the code by replacing .then with await and removing any .catch calls(It will throw instead). Make sure any async calls(resolve?) have an await statement. Keep in mind await is just syntax sugar for resolving a Promise where it returns the value on resolve or throws on reject.
Ex
prepareAPI("GET")
.then(opts => console.log("Headers: ", opts))
.catch(e => console.error(e));
becomes
try {
const opts = await prepareAPI("GET");
console.log("Headers: ", opts)
} catch (e) {console.error(e);}

Node.js Access Values from Anonymous function

Good morning!
I've been struggling to get a specific value returned from my function:
const getFolders = function (PID){
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
return body
})
// I NEED THE RETURN VALUE OF THE ABOVE FUNCTION HERE SO I CAN ACCESS WHEN CALLING getFolders()
}
Is this possible? If so, how?
Thanks!
Usually there will be three ways dealing with asynchronous stuff:
callback
promise
async/await
callback:
const getFolders = function(PID, callback) {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
callback(body)
})
}
getFolders(pid, (v) => {
console.log(v)
})
promise:
const getFolders = function(PID, callback) {
return new Promise((resolve, reject) => {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
if (err) {
return reject(err)
}
resolve(body)
})
})
}
getFolders(pid)
.then(v => {
console.log(v)
}).catch(error => {
console.error(error)
})
async/await:
Due to async/await is actually a syntax sugar, the getFolders function is the same as using promise's, the difference is when you call it:
(async function() {
try {
let v = await getFolders(pid)
console.log(v)
} catch(e) {
console.error(e)
}
})()
Not sure if this solve your confusion.
The way you are expecting is wrong, the test function which you have passed to request.get method is a callback function which will execute in asychronous manner , it means whenever your API responds from the server, that callback function will get execute.
So before that you are expecting the response (body) below the request method, which is wrong.
In this case you just have to write some other function to call this get method and in callback function you can easily access that response or just write your code in that test function itself.
like below - :
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
// instead of returning body
// use the body here only
let result = body;
// your code here
})
Or the other way -:
const getFolders = function (PID){
var token = getStoredToken();
this.get(function(err, response, body){
// do whatever you want with the response now
updateFolder()
})
}
function get(callback){
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, callback)
}

Using data from an aysnc Javascript http request? (aws serverless)

I'm using the nodejs serverless module to create a lambda aws function.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result;
request.get(options, (error, response, body) => {
result = JSON.parse(body).lastUpdatedOn; //never happens cuz of async
});
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};
The problem I'm having is that I can't return output from the get request, because the assignment to the result variable (in the async get request) happens after the return statement. I don't think I can turn the outer function into a callback function for the get request. How could I work around this?
An alternative could be to extract the request logic and put it into a new function.
Remember, you need to catch any errors, so use a try-catch block for doing that.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
};
function getResult() {
return new Promise(function (resolve, reject) {
request.get(options, (error, response, body) => {
if (error) return reject(error);
resolve(JSON.parse(body).lastUpdatedOn); //never happens cuz of async
});
});
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result = await getResult();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};

Return Promise From REST Call

I'm trying to invoke a REST call and return a promise so I can manipulate the data afterwhich.
var self = this;
var returnPromise;
returnPromise = self.httpService.testService();
returnPromise.then(function(result){
console.log(result);
});
My REST service is located in another file which has the following:
testService() {
console.log("start of testService");
var request = require('request');
var user = "test";
var ipadd = "127.0.0.1";
request({
url: 'https://' + 'LocalHost' + '/URLOFWEBSERVICE',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
userID: user,
userIpAddress: ipadd
}
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
console.log(response.statusCode, body);
var newPro = new Promise(function(fulfill, reject) {
fulfill(body);
});
console.log(newPro);
return newPro;
}
});
}
I'm able to print out response.stateCode , body( REST result) and the "invoked fulfill" console is printed out.
The problem lies with the
returnPromise.then
in the first page, in which ".then" returns me undefined.
At first I thought that it might be the promise has been called before the REST returns me a response. So, I thought of doing a timeout function to test.
setTimeout(
function(){
console.log(returnPromise);
}
, 5000);
But even so, returnPromise returns me "undefined".
If you expect a function to return a promise, it should return a promise
your testService does not return anything
this does:
testService() {
console.log("start of testService");
var request = require('request');
var user = "test";
var ipadd = "127.0.0.1";
return new Promise(function(fulfill, reject) {
request({
url: 'https://' + 'LocalHost' + '/URLOFWEBSERVICE',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
userID: user,
userIpAddress: ipadd
}
}, function(error, response, body) {
if (error) {
// reject the promise, handle with .catch
reject(error);
} else {
console.log(response.statusCode, body);
fulfill(body);
}
});
});
}
That can be called as
// var self = this; // why? not needed in the code shown
// var returnPromise; // why? it's not needed in the code shown
this.httpService.testService()
.then(function(result) {
console.log(result);
})
.catch(function(err) {
// handle your errors here
});

Categories

Resources