Resolving async value from JS class - javascript

In short: I need to request a token from Auth0 in order to then retrieve user information (all inside a Vue plugin that is called by Vuex).
I currently have two classes - one for dealing with the token and the other for actually retrieving the profile.
export class Token {
constructor () {
this.options = { method: 'POST',
url: 'https://.auth0.com/oauth/token',
headers: { 'content-type': 'application/json' },
body: '{"client_id":"","client_secret":"","audience":"","grant_type":"client_credentials"}' }
}
async getToken () {
try {
this.request = request(this.options, function (error, response, body) {
if (error) {
console.error(error)
} else {
let parsed = JSON.parse(body)
let token = parsed.access_token
return token
}
})
let blah = await this.request
console.log(blah)
} catch (err) {
console.error(err)
}
}
}
export default class Profile {
constructor () {
const token = new Token()
let result = token.getToken()
//console.log(result)
this.auth0Manage = new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}
getProfile () {
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
this.auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
I am expecting the following flow:
Get token > Get profile. It doesn't have to be in two classes (originally it wasn't but I couldn't instantiate the constructor for Profile without getting the token first, which would have to be its own class (afaik). Thanks!

Looks like you are not awaiting your getToken result in the profile constructor. Problem is, constructors are always synchronous. So in order to await the token, you need to do something like make Profile#auth0manage an async value and await that. Maybe like:
export default class Profile {
constructor () {
let token = new Token()
this.auth0Manage = new Promise((resolve, reject) => {
token.getToken().then((result) => {
return new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}).then(resolve, reject)
})
}
async getProfile () {
let auth0Manage = await this.auth0Manage
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
Caveats of this are that if you get an error in getToken then every call to getProfile will return the same error. So you may want to handle that somehow. Also, you have to remember to await every use of this.auth0Manage. Ideally, you could do something like pass in the auth0Manager to the constructor of Profile, so that you won't event try to make a profile until the token has been fetched. Tends to work out better to do things that way.

Related

How can I "encapsulate" this code into a module so it could become reusable?

I have got this Node.JS snippet and would like to write it as a module, so I can use recaptcha in different parts of my system.
This is how it currently looks like:
app.post('/register_user', (req, res) => {
const secret_key = process.env.RECAPTCHA_SECRET;
const token = req.body.recaptcha;
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
fetch(url, { method: "post",})
.then((response) => response.json())
.then((google_response) => {
if (google_response.success == true) {
res.format({'text/html': () => res.redirect(303, '/register'),})
} else {
return res.send({ response: "Failed" });
}
})
.catch((error) => {
return res.json({ error });
});
})
I have tried to write the following module which works absolutely great, but I have absolute no idea about how to call it from the app.post, since I always get undefined as return:
import fetch from 'node-fetch';
export function fetch_out(url, timeout = 7000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
export async function checkRecaptcha(token, secret_key){
const url = "https://www.google.com/recaptcha/api/siteverify?secret=" + secret_key + "&response=" + token;
try{
const response = await fetch_out(url, 1000);
const google_response = await response.json();
}catch(error){
return error;
}
return google_response;
}
Any help would be appreciated! Thanks!
You could make this method reusable by removing the framework actions that need to happen and only return if the validation was successful or not. This way, it will be reusable in another project that doesn't use a specific framework.
Example module;
export async function checkRecaptcha(token, secret_key) {
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
const response = await fetch(url, { method: "post",});
if (!response.ok) return false;
const json = await response.json();
if (!json.success) return false;
return true;
}
Usage:
import { checkRecaptcha } from "./some-file-name";
app.post('/register_user', async (req, res) => {
const isHuman = await checkRecaptcha(req.body.recaptcha, process.env.RECAPTCHA_SECRET);
if (!isHuman) {
return res.send({ response: "Failed" });
}
return res.format({'text/html': () => res.redirect(303, '/register'),});
});
If you specifically want to call an action after the validation, you can also use successful and error callbacks.

How to add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

Trying to refactor a promisified function in to try-catch block

I am trying to refactor this code using try-catch blocks:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
}).catch(error => console.log(error));
}
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
}).catch(error => console.log(error));
}
However I'm not getting the same result - the asynchronousity seems to be being handled differently. Here is my attempt at refactoring:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
try {
ConnectyCube.login(userCredentials, (error, user) => {
return user;
})
}
catch (error) {
console.log(error)
}
}
const createSession = () => {
try {
ConnectyCube.createSession((error, session) => {
return session.user
})
} catch (error) {
console.log(error);
}
}
Is there any particular part of what I'm wrong? Thanks.
Callback-based APIs don't readily turn into something you can use for async/await (which under the hood uses promises). You'll have to "promisify" them first (i.e. wrap them in promises).
Here's an example of what I'm trying to say:
// Promisify these callback-based APIs.
const login = userCredentials => {
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
})
})
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
})
})
// Then use them in an async function
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
}
try {
await createSession()
return login(userCredentials)
} catch (e) {
console.warn(e)
}
}
Also, async functions return promises, with the resolved value being the return value, and the rejected value being any uncaught error thrown inside. A value wrapped in a promise as return value for an async function is redundant.
If you're using Node 8+, it has a utility called promisify which accepts a callback-based API and returns a promise-returning version of it.

Node.js - Mock result of a promise

I want to mock the result of a function within a node module so that i can run assertions.
Considering the following node module:
const doPostRequest = require('./doPostRequest.js').doPostRequest;
const normalizeSucessResult = require('./normalizer.js').normalizeSucessResult;
const normalizeErrorResult = require('./normalizer.js').normalizeErrorResult;
exports.doPost = (params, postData) => {
return doPostRequest(params, postData).then((res) => {
const normalizedSuccessResult = normalizeSucessResult(res);
return normalizedSuccessResult;
}).catch((err) => {
const normalizedErrorResult = normalizeErrorResult(err);
return normalizedErrorResult;
})
}
The function doPostRequest returns a promise. How can i fake the return value of this promise so that i can assert if normalizeSucessResult has been called?
So for i have tried:
const normalizeSucessResult = require('./normalizer.js');
const doPostRequest = require('./doPostRequests.js');
const doPost = require('./doPost.js');
it('runs a happy flow scenario', async () => {
let normalizeSucessResultStub = sinon.stub(normalizeSucessResult, 'normalizeSucessResult');
let postData = { body: 'Lorum ipsum' };
let params = { host: 'someUrl', port: 433, method: 'POST', path: '/' };
sinon.stub(doPostRequest, 'doPostRequest').resolves("some response data"); //Fake response from doPostRequest
return doPost.doPost(params, postData).then((res) => { //res should be equal to some response data
expect(normalizeSucessResultStub).to.have.been.calledOnce;
expect(normalizeSucessResultStub).to.have.been.with("some response data");
});
});
The doPostRequest module looks like this:
const https = require('https')
module.exports.doPostRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
You can use Promise.resolve to return a promise with any given value.
Promise.resolve(“hello world”);
For stub your func you need to do like this
sinon.stub({doPostRequest}, 'doPostRequest').resolves("some response data")
Okay, i figured it out. The function doPostRequest was loaded using require, on the top of the file using const doPostRequest = require('./doPostRequest.js').doPostRequest;
In order to mock the data that comes back from a function that is loaded using require i had to use a node module called mock-require. There are more modules that can take care of this (proxyquire is a populair one) but i picked mock-require (i did not have a specific reason for choosing mock-require).
For anyone else that is stuck with a similar problem, try mock-require to mock the respose from files that are loaded using require.

Testing HttpClient call with callFake()

I am trying to create a spec to test a method in my Angular service that makes a GET request. The difficulty I am having is mocking the method to get it to return an error instead of the response. If I cannot get it to return an error (such a 400 or 500 for example) I cannot provide full code coverage...
Code being tested:
maingrid.service.ts:
async loadAccountListPromise(id: string) {
let queryParams = `?emailAddress=${id}`;
let promise = new Promise((resolve, reject) => {
this.http.get(`${this.baseUrl}` + queryParams, { responseType: 'json' })
.toPromise()
.then(
(data) => {
this.results = this.formatData(data);
resolve(this.results);
},
(err) => {
this.logService.error('loadAccountListPromise() exception:', err);
this.setError(this.message[0], err);
reject('loadAccountListPromise() exception');
}
);
});
return promise;
}
setError(errorMessage: string, errorCode?: string): void {
this._error.next(new NxpError(errorMessage, 'AccountListService',
errorCode));
}
clearError(): void {
this._error.next(null);
}
This is the spec I have attempted to write to mock the method using callFake():
maingrid.service.spec.ts
it('logs and sets a local error for system errors/exceptions', () => {
let id: string = 'ppandya#pershing.com';
let myUrl = 'https://localhost:9999/...';
let queryParams = `?emailAddress=${id}`;
spyOn(httpClient, 'get').and.callFake( loadAccountListPromise( (response) => {
// need to return error here...somehow
}));
spyOn(logService, 'error');
spyOn(maingridService, 'setError');
maingridService.loadAccountListPromise(id);
let request = httpMock.expectOne(myUrl + queryParams);
expect(request.request.method).toEqual('GET');
httpMock.verify();
expect(logService.error).toHaveBeenCalled();
expect(maingridService.setError).toHaveBeenCalled();
});
I am not sure what I need to do to properly mock the loadAcountListPromise() method so that it enters the error block and calls the setError() and logService.error() methods.
Try to use the 'spyOn()' and return a throw like this:
spyOn(httpClient, 'get').and.returnValue(Observable.throw({status: 404}));
//Observable.throw(new Error(`Error: ${error}`));

Categories