I would like to authenticate against Azure from my Node backend. The acquireTokenWithClientCredentials helps me out. Unfortunately I have to pass in a callback but I would like to await it and return a Promise with the new fetched token.
First I will show my working code using a callback
#Injectable()
export class AuthenticationsService {
private session: TokenResponse;
private authenticationContext: AuthenticationContext;
// -- setup the AuthenticationContext and configurations in the constructor --
getSession(sessionCallback: Function): void {
if (!this.session || this.session.expiresOn < new Date()) {
this.authenticationContext.acquireTokenWithClientCredentials(resource, clientId, clientSecret, (error: Error, tokenResponse: TokenResponse) => {
if (error) {
throw error;
}
this.session = tokenResponse;
sessionCallback(this.session);
});
} else {
sessionCallback(this.session);
}
}
}
I would like to note that I only fetch a new session if the current one has expired. I would like to await that callback and return a Promise instead. Other functions requesting the session don't have to deal with callbacks then. So my updated code should look like
async getSession(): Promise<TokenResponse> {
if (!this.session || this.session.expiresOn < new Date()) {
try {
this.session = await this.authenticationContext.acquireTokenWithClientCredentials(resource, clientId, clientSecret);
} catch (error) {
throw error;
}
}
return this.session;
}
Is there a way I can await the acquireTokenWithClientCredentials function and don't have to use a callback?
I think I got it working with this code. Basically I'm just returning a new promise
async getSession(): Promise<TokenResponse> {
if (!this.session || this.session.expiresOn < new Date()) {
return new Promise((resolve, reject) => {
this.authenticationContext.acquireTokenWithClientCredentials(resource, clientId, clientSecret, (error: Error, tokenResponse: TokenResponse) => {
if (error) {
return reject(error);
}
this.session = tokenResponse;
return resolve(this.session);
});
});
}
return this.session;
}
Please let me know if this can be simplified :)
Related
I'm working on some code for an express API that essentially reaches out to an external REST service for an authentication token, and then uses that token to do stuff. I'm very new to NodeJS, so I think I'm having a lot of trouble with the whole sync/async thing.
Right now my main problem seems to be that I'm getting ReferenceError: err is not defined, which seems to be related to the line in library.js, but I expect there are a lot of problems here, and will appreciate anything that can get me back on the right track.
index.js
library = require('library.js');
module.exports = async (req,res) => {
// This is a test endpoint for prototyping code and testing calls.
URI = "/some/uri";
method = "GET";
body = "";
try {
restResponse = await library.RESTCall(URI,method,body);
res.send(data);
} catch (e) {
return res.status(500).json({ errors: err});
}
};
library.js
exports.RESTCall = async function(URI,method,body) {
return new Promise((resolve, reject) => {
getAuthToken().then((token) => {
console.log("Token: " + token);
try {
// Do stuff with the token to make another call
resolve(data);
} catch (e) {
reject(e);
}
}).catch((err) => {
reject(err);
});
});
}
exports.getAuthToken = () => {
return new Promise((resolve, reject) => {
try {
// Do stuff to get an authentication token
resolve(authToken);
} catch(e) {
reject("Failed to get Facets Authentication token. Error: " + e);
}
});
}
This looks like just a typo:
return res.status(500).json({ errors: e});
FYI this:
exports.RESTCall = async function(URI,method,body) {
return new Promise((resolve, reject) => {
getAuthToken().then((token) => {
console.log("Token: " + token);
try {
// Do stuff with the token to make another call
resolve(data);
} catch (e) {
reject(e);
}
}).catch((err) => {
reject(err);
});
});
}
Is mostly equivalent, but slightly worse as:
exports.RESTCall = function(URI,method,body) {
return getAuthToken().then((token) => {
console.log("Token: " + token);
// Do stuff with the token to make another call
return data;
}
}
But because you have async/await, can be simplified further:
exports.RESTCall = async function(URI,method,body) {
const token = await getAuthToken();
console.log("Token: " + token);
// Do stuff with the token to make another call
return data;
}
Every time you see yourself type new Promise, consider it a red flag. I'd really suggest you take the time to learn how promises work.
I am currently using the JavaScript AWS SDK for DynamoDB and I am trying to parse the data I get from a DynamoDB call, but it always returns undefined. It does print the result successfully, but trying to store that data in a variable is unsuccessful. Below is how I am attempting to do it.
const AWS = require("aws-sdk");
AWS.config.update({ region: "us-east-1" });
const dynamoDb = new AWS.DynamoDB({ apiVersion: "2012-08-10" });
const promisify = foo =>
new Promise((resolve, reject) => {
foo((error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
const params2 = {
TableName: "Users",
Key: {
userID: { S: "123456789" },
},
};
const test = params => {
dynamoDb.getItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.Item);
return data.Item;
}
});
};
let user = test(params2);
console.log("User:", user);
I believe it has to do with getItem being asynchronous and I have researched how to implement a promise into this code, but I cannot get the correct syntax. Any and all help is greatly appreciated. Thank you.
The problem you have is that you are not returning anything from your test function. So it is expected that you get undefined outside.
Try to do it like this:
// used aws promise api https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
function test(params) {
return dynamoDb.getItem(params).promise();
}
test(params2).then(
user => {
console.log('User:', user);
},
error => {
console.log('Error:', error);
},
);
Some further read:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
I'm trying to better understand error handling while using async/await, third party libs, and external APIs.
Given the following pseudo-code:
const createConnectionRequest = async (args) => {
try {
const { data } = axios.post(url, args);
return data;
} catch (err) {
throw new Error(err);
}
}
My understanding is the throw would occur a result of the axios.post failing rather than an issue with my request.
If the response from my API was 200 but included an error of some sort, eg.
{
status: 200,
error: 'Invalid fields supplied',
}
Would I throw this error in the try block and expect a parent calling function to catch it?
const createConnectionRequest = async (args) => {
try {
const { data } = axios.post(url, args);
if (data.error) {
throw new Error(data.error);
}
return data;
} catch (err) {
throw new Error(err);
}
}
...
const processor = async () => {
try {
await createConnectionRequest(...);
} catch (err) {
// handle error from API response here
throw new Error(err);
}
}
Right now I'm learning promises and want to get a token from a webserver, which also uses a promise. I tried it before without a promise but it's also not working.
This is the first block.
promise = new Promise(resolve => {
let accessToken = helper.getAccessToken(baseUrl);
let userCollection = helper.createCollection("user", db);
let excersizeCollection = helper.createCollection("excercise", db);
resolve({
accessToken: accessToken,
database: {
userCollection: userCollection,
excersizeCollection: excersizeCollection
}
});
});
promise
.then(promises => {
console.log("my token" + promises.accessToken);
new nceDefaultbotCommands(bot, promises.accessToken, baseUrl);
new botComamnds(bot, promises.database);
let userController = new reqUserController(
baseUrl,
0,
promises.accessToken
);
bot.start();
})
.catch(() => {
console.log("error");
});
Only the access token is not working, this is in my helper class and it looks like this.
static getAccessToken(baseUrl) {
let promise = new Promise(resolve => {
request.post(
{
url: baseUrl + "/token",
body: {
credentials: {
user: "USER",
password: "PW"
}
},
json: true //// Automatically parses the JSON string in the response
},
(error, response, body) => {
if (error) console.log("error");
if (!error && response.statusCode === 200) {
resolve({ token: body.token });
}
}
);
});
promise.then(resolve => {
console.log(resolve.token);
return resolve.token;
});
}
I get the access token but normally after the then of the first promise.
Thanks in advance.
You're fulfilling your first promise with an object, so that object is the fulfillment value (the promise isn't magically resolved to the promises that are values of properties on that object).
There's no reason to use new Promise when you have a promise or promises to work with, just chain off them; in this case, via Promise.all:
Promise.all([
helper.getAccessToken(baseUrl),
helper.createCollection("user", db),
helper.createCollection("excercise", db)
])
.then(([accessToken, userCollection, exersizeCollection]) => { // Note the destructuring
console.log("my token" + accessToken);
new nceDefaultbotCommands(bot, accessToken, baseUrl);
new botComamnds(bot, {userCollection, exersizeCollection});
let userController = new reqUserController(baseUrl, 0, accessToken);
bot.start();
})
.catch(/*...*/);
Note I corrected the spelling of "exercise," which may require changes to your botCommands constructor.
You do not return your Promise:
static getAccessToken(baseUrl) {
let promise = new Promise(resolve => {
...
});
return promise.then(resolve => {
console.log(resolve.token);
return resolve.token;
});
}
Then I think you need to handle it like Promise, somewhat like:
promise = new Promise(resolve => {
let accessToken = helper.getAccessToken(baseUrl).then(token => {
let userCollection = helper.createCollection("user", db);
let excersizeCollection = helper.createCollection("excercise", db);
resolve({
accessToken: accessToken,
database: {
userCollection: userCollection,
excersizeCollection: excersizeCollection
}
});
});
});
I am using #angular/http for http calls (Observable) and NativeStorage library for storage mechanism which is Promise. That's why I use FromPromise to convert Promise funtion "NativeStorage.getItem("xxx")" to Observable.
I am even not sure if this is a good practice and the chain is broken at the line "console.log("HIT SUCCESSFULLY");" and stops executing the code.
Since there is no item called "externalAccessToken" in the storage, it is normal to catch the exception null in Promise but I don't understand why it stops executing after that.
Till now, I have tried to return something else other than null and using "Promise.reject()" which caused "Unhandled Promise rejection" error.
How can I keep the the code executing and hit the catch function of Observable
public getExternalAccessTokenFromStorage(): Observable<any> {
let externalAccessTokenPromise = NativeStorage.getItem('externalAccessToken');
let getExternalAccessTokenFromStorage: Observable<any> = Observable.fromPromise(externalAccessTokenPromise.then(x => x)
.catch(() => {
console.log("HIT SUCCESSFULLY");
return null
}));
return getExternalAccessTokenFromStorage.map(x => {
console.log("NOT HIT AT ALL");
return x;
}).catch(() => {
console.log("NOT HIT AT ALL");
return null;
});
}
public getUserInfo(): Observable<StoredUserModel> {
//Get External Access Token From LocalStorage
return this.getExternalAccessTokenFromStorage().flatMap((x: IExternalAccessTokenBindingModel) => {
return this.getAccessTokenFromStorage().flatMap((accessToken: AccessTokenModel) => {
console.log("NOT HIT AT ALL");
let headers = new Headers();
headers.append("Authorization", "Bearer " + accessToken.access_token);
headers.append("Content-Type", "application/json");
let options = new RequestOptions({ headers: headers });
var externalBindingModel = JSON.stringify(x);
return this.http.post(this.baseUrl + '/api/Account/ExternalUserInfo', externalBindingModel, options).map((res: Response) => {
//ADD USER INTO NATIVESTORAGE
this.addUserIntoStorage(res.json());
return res.json();
});
});
}).catch(x => {
return this.getAccessTokenFromStorage().flatMap((accessToken: AccessTokenModel) => {
console.log("NOT HIT AT ALL");
let headers = new Headers();
headers.append("Authorization", "Bearer " + accessToken.access_token);
let options = new RequestOptions({ headers: headers });
return this.http.get(this.baseUrl + '/api/Account/UserInfo', options).map((res: Response) => {
//ADD USER INTO NATIVESTORAGE
let user: StoredUserModel = res.json();
this.addUserIntoStorage(res.json());
return user;
});
}).catch(error => {
return null;
});
});
}
UPDATED QUESTION:
I have removed Promise.catch and keep Observable.catch in order to catch unhandled exception in Observable;
public getExternalAccessTokenFromStorage(): Observable<any> {
let externalAccessTokenPromise = NativeStorage.getItem('externalAccessToken');
let getExternalAccessTokenFromStorage: Observable<any> = Observable.fromPromise(externalAccessTokenPromise);
return getExternalAccessTokenFromStorage.map(x => {
return x;
}).catch(() => {
return null;
});
}
And I get the following error;
Catch works exactly the same as the try / catch clasues in programming.
Give the following example:
try {
throw new Error('bang');
} catch(ex) {
// do nothing
}
console.log('I'm still reachable');
We can reproduce the above with observable like this:
let o = Observable.create((observer)=>{
observer.error(new Error('bang'));
}).catch(()=>{
// do nothing
});
o.subscribe(()=>{
console.log('I'm still reachable');
});
If you want to catch and process an error, but then prevent code below from executing using try / catch you would do this:
try {
throw new Error('bang');
} catch(ex) {
// do some logic here
throw ex;
}
console.log('I cannot be reached');
It's the same in observables. You have to re-throw the error or yield an observable that also fails.
let o = Observable.create((observer)=>{
observer.error(new Error('bang'));
}).catch((ex)=>{
// do some logic here
return Observable.throw(ex);
});
o.subscribe(()=>{
console.log('I cannot be reached');
});
The problem is that you're catching, but not handling the error. You will want to throw the error as an Observable.
public getExternalAccessTokenFromStorage(): Observable<any> {
let externalAccessTokenPromise = NativeStorage.getItem('externalAccessToken');
let getExternalAccessTokenFromStorage: Observable<any> = Observable.fromPromise(externalAccessTokenPromise);
return getExternalAccessTokenFromStorage.map(x => {
return x;
}).catch((error: any) =>
Observable.throw(error.json().error || 'Server error');
);
}
You can then handle your response and error in the form of a promise:
this.getExternalAccessTokenFromStorage().subscribe(
res => console.log(res),
error => console.log(error));