How to wait for async callback method before continuing? - javascript

I have the following 2 methods.
But in the get method that I'm overriding from the Http module, the authentication success callback is called after it has already executed the request and returned a response. This way it's adding
the JWT token to the headers in the wrong order, and too late.
I'm not super knowledgeable with promises and observables.. But what can I do so that it actually waits for the callback to be done before executing the request and returning the response?
authenticate(authCompletedCallback, errorCallback) {
let authContext = new Microsoft.ADAL.AuthenticationContext(AUTHORITY_URL);
authContext.tokenCache.readItems().then((items) => {
if (items.length > 0) {
AUTHORITY_URL = items[0].authority;
authContext = new Microsoft.ADAL.AuthenticationContext(AUTHORITY_URL);
}
// Attempt to authorize user silently.
authContext
.acquireTokenSilentAsync(RESOURCE_URL, CLIENT_ID)
.then(authCompletedCallback, () => {
// We require user credentials so trigger authentication dialog.
authContext
.acquireTokenAsync(RESOURCE_URL, CLIENT_ID, REDIRECT_URL)
.then(authCompletedCallback, errorCallback);
});
});
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
this.authenticate((authResponse) => {
// This is executed second.
if (!options) {
options = { headers: new Headers() };
}
options.headers.set('Authorization', 'Bearer ' + authResponse.accessToken);
}, (err) => {
alert(JSON.stringify(err));
});
// This is executed first.
return super.get(url, options);
}

You might want to make the get function return a promise, since the actual Response object shouldn't exist until after you have authenticated. It looks like you're overriding a method, though, so maybe instead you'll want to make a different method.
getWithAuthentication(url: string, options?: RequestOptionsArgs): Promise<Observable<Response>> {
return new Promise((resolve, reject) => {
this.authenticate((authResponse) => {
if (!options) {
options = { headers: new Headers() };
}
options.headers.set('Authorization', 'Bearer ' + authResponse.accessToken);
let response = super.get(url, options);
resolve(response);
}, (err) => {
alert(JSON.stringify(err));
reject(err);
});
}
}

Related

chaining responses with axios and Vue js

I am trying to chain three requests in one with Axios. I am practicing vue.js so please correct me if my approach is not ideal.
The goal is to have three requests. One is a post followed by 2 Gets. What I would like to achieve is one chained request.
My questions are:
Is this a good way to handle this? Should they be chained together like this?
Is it possible to map the response to a model like I did in the first post request and pass it to the next?
const apiClient: AxiosInstance = axios.create({
headers: {
'content-type': 'application/json',
'X-RapidAPI-Key': '08f852e........22fb3e2dc0...',
'X-RapidAPI-Host': 'judge0-ce.p.rapidapi.com'
},
params: {base64_encoded: 'true', fields: '*'},
});
export const api = {
async submitCode(code: Code) {
code.language_id = 60
code.stdin = "Sn..2Uw"
code.source_code = btoa(code.source_code)
apiClient.post<Token>(`https://judge0-ce.p.rapidapi.com/submissions?language_id=${code.language_id}&source_code=${code.source_code}&stdin=SnVkZ2Uw`)
.then(function (response) {
console.log("res.data", response.data.token);
}).then(function (token) {
console.log("token", token); // <---- empty
`https://judge0-ce.p.rapidapi.com/submissions/${token}` // <---- get request
}).then(function (response) {
console.log("res.data", response);
}).then(function (response ) {
// here I will need a model
})
.catch((err) => {
const error = err.response ? err.response.data : err;
console.log("error" + error);
})
}
}
You have to await each function if the next one is dependent on the previous. Or you could use Promise chaining in the traditional sense using new Promise(resolve, reject). Async only applies to top level, so you will need to declare subsequent functions 'async' again as shown.
I would also suggest setting axios defaults with a base URL so you don't have to repeat the full URL each time. Note that console.log statements are not "Thenable" so your 1st statement has no effect, nor does your 3rd, other than to define your next variable.
const apiClient = axios.create({
baseURL: 'https://judge0-ce.p.rapidapi.com/submissons',
// ... headers ... //
});
export const api = {
async submitCode(code){
// ... code variable ...//
await apiClient.post(
`?language_id=${code.language_id}&source_code=${code.source_code}&stdin=SnVkZ2Uw`)
.then(async (response1) => {
const token = response1.data.token
await api.get(`/${token}`)
}).then(async (response2) => {
console.log(response2.data)
const model = response2.data.map((val1) => apiClient.get(`anotherGet/${val1.id}`))
const result = await Promise.all(model)
return result.map((val2) => val2.data)
})
// ... catch ...//
}}
You useasync, then don't chain promices
async function submitCode(code: Code) {
code.language_id = 60
code.stdin = "Sn..2Uw"
code.source_code = btoa(code.source_code)
try { // ~ global `.catch`
const token = await apiClient.post<Token>(`https://blah`)
const get1result = await apiClient.get<Blah>(`https://balah?${token}`)
const get2result = await apiClient.get<Blah>(`https://balah?${token}`)
doBlah({token, get1result, get2result})
} catch (err) { // maybe should check object type here
const error = err.response ? err.response.data : err;
console.log("error" + error);
}
}
As for Vue, I can only recomment to use asyncComputed which you can feet Promise into if you need that
Express also had express.api or something with which you can skip https://blah/com/api/ part of url, check it

Using Promise.reject() for Error-Handling in http.post

I wrote a login method which sends a http post to an authorization service. If the credentials are wrong, the service returns a 403.
My idea is to return a Promise.reject() and let the caller catch the rejection to show an alert. My problem is, the calling method does not handle the error correctly.
Is there anything I am doing wrong with my error procedure?
public async login(username: string, password: string): Promise<void> {
let authToken = null;
const obj = this;
const url = this.backendService.getAuthServiceURL(username, password);
this.http.post<any>(url, { }, {
headers: new HttpHeaders(),
observe: 'response',
}).subscribe(
(result) => {
authToken = result.headers.get('Authorization');
localStorage.setItem(authTokenIdentifier, authToken);
obj.username = username;
localStorage.setItem(usernameIdentifier, username);
},
(error) => {
if (error.status === 403) {
localStorage.removeItem(authTokenIdentifier);
return Promise.reject(error.message);
} else {
return Promise.reject(error.message);
}
},
() => {
},
);
}
async login(): Promise<void> {
this.authService.login(this.username, this.password)
.then( () => console.log("SUCCESS!"))
.catch(() => console.log("ERROR!"));
}
I already tried wrapping the authService.login call in Try/Catch, calling it with await
await this.authService.login(this.username, this.password)
and not running the code async at all.
Thank you for any advice in advance.
Edit:
If I understand you guys correctly, returning the Error wrapped in a Promise is unnecessary and/or can cause problems. I have to admit I didn't really understand that subscribe will create an Observable.
(error) => {
if (error.status === 403) {
localStorage.removeItem(authTokenIdentifier);
throwError(error.message);
} else {
throwError(error.message);
}
},
I tried throwing the error directly with the same result.

Calling a Promise function inside Google Cloud Function

I am trying to issue an HTTP request to another web service, from a Google Cloud Function (GCF) that I have created. I need the HTTP request to complete and return that result inside of my GCF so that I can do something else with it.
My question is; What is the best way to use Promise inside a Google Cloud Function? Is what I am trying to do possible?
My code currently looks like this:
export const MyGCF = functions.https.onRequest((request, response) => {
let dayOfTheWeek: any;
const request1 = require('request');
const url = 'http://worldclockapi.com/api/json/pst/now';
function getDay() {
return new Promise((resolve, reject) => {
request1(url, { json: true }, (err: any, res: any, body: any) => {
if (err) {
return reject(err);
}
resolve(body.dayOfTheWeek);
});
});
}
getDay().then((data) => {
dayOfTheWeek = data;
console.log(dayOfTheWeek);
});
});
In general your approach will work, and you can define additional functions inside of your MyGCF handler, in the same way that you have defined getDay(). One problem with you current code however is that you're forgetting to "write a response" for the request being processed by MyGCF.
You can write a response for the request by calling send() on the second res argument of your MyGCF request handler. A simple example would be:
/* Sends a response of "hello" for the request */
res.send("hello");
With respect to your code, you can use res.send() in your .then() callback to send a response back to the client after getDay() has completed (see code below). Note also to include a .catch() clause and callback for the error case (with an error status) to ensure the client receives an appropriate error response if the call to getDay() fails:
export const MyGCF = functions.https.onRequest((req, res) => {
const request = require('request');
const url = 'http://worldclockapi.com/api/json/pst/now';
function getDay() {
return new Promise((resolve, reject) => {
request(url, {
json: true
}, (err: any, r: any, body: any) => {
if (err) {
reject(err);
} else {
resolve(body.dayOfTheWeek);
}
});
});
}
getDay().then((dayOfTheWeek) => {
/* Send a response once the getDay() request complete */
res.send(dayOfTheWeek);
})
.catch(err => {
/* Don't forget the error case */
res.status(500).send(err);
});
});

Node.JS recursive promise not resolving

i'm working with an API that allows me to sync data to a local DB. There is a syncReady API that I'm calling recursively until the sync batch is ready to start sending data. The recursion is working correctly and the .then callback is called, but the resolve function never resolves the response.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
return new Promise((res, rej) => {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
res(response);
}, (error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
this.syncReady(token, batchID);
} else rej(error);
}
);
});
};
I get the 497 logged and the "The Response: {"pagesTotal";0}" response but the res(response) never sends the response down the chain. I've added a console.log message along the entire chain and none of the .then functions back down the chain are firing.
I hope I've explained this well enough :-). Any ideas why the promise isn't resolving?
Thanks!
First, you don't need to wrap something that returns a promise with a new Promise. Second, for your error case you don't resolve the promise if it is 497.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
return request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
return response;
})
.catch((error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
return this.syncReady(token, batchID);
} else {
throw error;
}
})
);
};
Maybe something like the above will work for you instead. Maybe try the above instead. As a general rule of thumb, it's you almost always want to return a Promise.

Chaining promises synchronously

I'm working with AWS Lambda. I'm trying to decrypt the key in a async function & then making a POST request to another URL which depends on getting the key first from the first call.
module.exports = function (payload) {
return new Promise(function(resolve, reject) {
var headers = config.Headers;
decrypt(headers.Authorization)
.then(function (auth_token) {
headers.Authorization = 'Basic ' + auth_token;
console.log('dispatch got the key from kms');
return makePostCall(headers, payload);
})
.then(function (changeNumber){
console.log(changeNumber);
return resolve(changeNumber);
})
.catch (function (error) {
console.error('Error during dispatch: ' + error);
return reject(error);
});
});
};
The decrypt & makePostCall call both return either reject or resolve. Runs fine locally, but when running on Lambda, runs successfully only some times, which led me to believe that issue is with asynchronous calling of the makePostCall function. The error I get(from catch) is:
Error during dispatch: null
I need to run decrypt first-> Get the key -> then MakePostCall.
Edit: makePostCall looks like this:
function makePostCall(headers, payload) {
return new Promise(function (resolve, reject) {
const url = config.serviceNowEndpoint + config.serviceNowChangeUri;
request.post({
url: url,
method: 'POST',
headers: headers,
json: payload
}, function (error, response, body) {
if (!error) {
return resolve(body.change_request.number);
}
else {
return reject(new Error('Returned with status code: ' + response.statusCode));
}
});
});
}
More Edit: As per #Jaromanda X's suggestion, modified the code to:
module.exports = function (payload) {
var headers = config.Headers;
return decrypt(headers.Authorization)
.then(function (auth_token) {
headers.Authorization = 'Basic ' + auth_token;
console.log('dispatch got the key from kms');
return makePostCall(headers, payload);
})
.catch (function (error) {
console.error('Error during dispatch: ' + error);
return error;
});
};
Problem still persists though. Runs fine locally, but async on Lambda
Edit adding decrypt code:
const AWS = require('aws-sdk');
const config = require('../config/config');
module.exports = function(token) {
return new Promise(function (resolve, reject) {
const kms = new AWS.KMS({ region: config.aws_region.region});
const params = {
CiphertextBlob: new Buffer(token, 'base64')
};
kms.decrypt(params, function (err, data) {
if (!err) {
console.log('decrypted successfully');
return resolve(data.Plaintext.toString());
} else {
return reject(`${err.message}`);
}
});
});
};
That's an AWS lambda bug. See the issue at Bluebird's repo and basic reproducing code for the issue.
This issue is not actually related to bluebird but to asynchronous code handling and scheduling on lambda's deployment. https://github.com/nervous-systems/cljs-lambda/issues/62. Here is another issue that reproduces the bug: https://github.com/nervous-systems/cljs-lambda/issues/62.
A user raises a common issue:
Oh my god. Just in case anyone else lands here and makes my mistake: make sure you haven't accidentally deployed AWS.config.update(...) using a localhost endpoint instead of an actual AWS region-specific endpoint. That can present with similar symptoms as above (say, if the only async function you're using is accessing a DynamoDB document client) but for utterly different reasons.

Categories