How Can I Pause & Resume When My Token Needs to Be Refreshed? - node.js

I am posting a large amount of data to an API that uses a token that needs to be refreshed every 30 minutes.
How would I pause my script when the token expires, refresh the token and then resume from when we left off?
I have so far:
const post = await mergedItems.forEach((item, index) => {
return new Promise((resolve, reject) => {
const postBody = `{ "ItemShops": { "ItemShop": [{ "itemShopID": ${item.itemShopID}, "qoh": ${item.qoh} }]}}`
setTimeout(() => {
axios({
url: `${lightspeedApi}/Account/${accountID}/Item/${item.id}.json`,
method: 'put',
headers: authHeader,
data: postBody
}).then(response => {
if (response.status == 401) {
refreshToken()
} else {
console.log(response.data) }
}).catch(err => console.error(err))
}, index * 10000)
})
})
}}

This is quite a common problem, and parallels (ha!) that described here. You're starting multiple requests in parallel, and can expect that as soon as one receives a 401 response the others will get the same shortly thereafter. This means they all start to try to refresh the token, also in parallel.
A rough sketch of how to apply the solution follows:
const makeRequest = async (opts) => {
try {
return axios(opts);
catch (err) {
// note axios throws on >=400 response code, not like fetch
if (err.response && err.response.status === 401) {
await refreshToken();
return makeRequest(opts);
else {
raise err;
}
}
}
Now you can expose a promise of the token getting refreshed, and reuse it to avoid making multiple requests while one is already in-flight:
let refreshPromise;
export const refreshToken = () => {
if (!refreshPromise) {
// start a refresh request only if one isn't in flight
refreshPromise = axios(...).then((res) => {
// ... reset token
refreshPromise = null;
});
}
return refreshPromise;
};

Related

NodeJS: async/await doesn't return data correctly with loops

I'm having these calls to list users with groups from google active directory
let globalGroups = null;
let groupMembers = null;
await GetCustomerId(req, res).then( async () => {
// GetGroups is async function saves groups in `globalGroups` variable
await GetGroups(token).then( () => {
globalGroups.forEach( async (group) => {
// GetGroupMembers is async function saves users in `groupMembers` variable
await GetGroupMembers(group.id, token).then( () => {
groupMembers.forEach( (member) => {
// here I log the `member` and have no issues here
if (usersIds.includes(member.id)) {
let user = users.find( ({ id }) => id === member.id );
user.group_ids.push(group.id);
}
else {
member.group_ids = [];
member.group_ids.push(group.id);
users.push(member);
usersIds.push(member.id);
}
})
})
});
// the issue is here without timeout it returns an empty array because it doesn't wait for the loop to finish
console.log(users);
res.status(200).json({"users": users}).send();
}).catch(function(err) {
console.log(err)
res.status(500).json({"error": err}).send();
});
});
This returns an empty array unless I use timeout to return the response like this
setTimeout( () => {
console.log(users);
res.status(200).json({"users": users, "next_page_link": "notFound"}).send();
}, 1000);
How to make it wait till the whole loop ends to return the response without using timeout?
const GetCustomerId = async (req, res, next) => {
try {
let authorization = req.headers['authorization'].split(' ');
if (authorization[0] !== 'Bearer') {
return res.status(401).send();
} else {
await axios({
url: 'https://admin.googleapis.com/admin/directory/v1/users?domain=&maxResults=1',
method: 'get',
headers: {
'Content-Type': "application/json",
'Authorization': ' Bearer ' + authorization[1]
},
})
.then((response) => {
globalCustomerId = response.data.users[0].customerId
})
.catch(function(err) {
console.log(err);
});
}
} catch (err) {
console.log(err);
}
}
const GetGroups = async (token) => {
try {
await axios({
url: 'https://admin.googleapis.com/admin/directory/v1/groups?customer=' + globalCustomerId,
method: 'get',
headers: {
'Content-Type': "application/json",
'Authorization': ' Bearer ' + token
},
})
.then((response) => {
globalGroups = response.data.groups;
})
.catch(function (err) {
return res.status(500).json({"error": err}).send();
});
} catch (err) {
return res.status(403).json(err).send();
}
}
const GetGroupMembers = async (groupId, token) => {
await axios({
url: "https://admin.googleapis.com/admin/directory/v1/groups/" + groupId + "/members",
method: 'get',
headers: {
'Content-Type': "application/json",
'Authorization': ' Bearer ' + token
},
})
.then((response) => {
groupMembers = null;
groupMembers = response.data.members;
})
.catch(function (err) {
return res.status(500).json({"error": err}).send();
});
}
globalGroups.forEach( async (group) => {
An async method inside .forEach doesn't actually do what you might want it to do.
By essentially doing array.forEach(async method) you're invoking a bunch of async calls, 1 per element in the array. It's not actually processing each call one by one and then finally resolving.
Switch to using a regular for loop with await inside it and it will do what you want.
eg.
for (const group of globalGroups) {
await GetGroupMembers(group.id, token)
groupMembers.forEach.....
}
You could do that to force your code to be more synchronous (or use something like Promise.all to be more efficient while still being synchronous) but another issue with the code is you're stuck in callback hell, which leads to less-readable code.
I'd highly recommend refactoring your Get* methods such that they return the values you need. Then you can do something cleaner and predictable/deterministic like:
const globalCustomerId = await GetCustomerId(req, res);
const globalGroups = await GetGroups(token); //note: Promise.all could help here
for (const group of globalGroups) {
const groupMembers = await GetGroupMembers(group.id, token)
groupMembers.forEach.....
}
console.log(users);
res.status(200).json({"users": users}).send();
You can wrap it in a try/catch to take care of error handling. This leads to much cleaner, more concise, and more predictable order of executions.

React get request without refreshing the page

I have an application where I first make a post request to an endpoint, let say;
ENDPOINT A
I have another endpoint where I make a GET HTTP request, let say;
ENDPOINT B
The issue now is how do I get the current data I posted to ENDPOINT A, from ENDPOINT B without refreshing the page.
Note: Every thing is working fine just that I need to refresh the page to see the current data I posted, which doesn't make it a reactive application.
Here is part of the code
//Get user requests
useEffect(() => {
fetch('http://localhost:3002/request/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': JSON.parse(localStorage.getItem("Some"))
}
}).then(function (res) {
// console.log(res)
return res.json()
}).then(function (datas) {
setState({ datas: datas })
console.log(datas)
}).catch(function (e) {
console.log(e)
})
}, [])
//Create request
const onAdd = (data) => {
fetch('http://localhost:3002/request', {
method: 'POST',
headers: {
'Authorization': JSON.parse(localStorage.getItem("Some"))
},
body: data
}).then(function (res) {
// console.log(res)
return res.json()
}).then(function (data) {
setPost({datas: data})
console.log(data)
}).catch(function (e) {
console.log(e)
})
}
You'll probably need to poll the server on an interval. fetch requests you only get back one response, if the data changes later you won't get any update for that update, meaning you'll need to repeat the request manually.
There are different strategies, probably the most simple one is to poll on an interval:
useEffect(() => {
const performRequest = () => fetch('http://localhost:3002/request/me', ...)
.then(...)
const token = setInterval(performRequest, 5000) // Every 5 seconds?
performRequest(); // Initial request
return () => {
// Don't forget to cleanup the interval when this effect is cleaned up.
clearInterval(token)
}
}, []);
But you can do more fancy stuff if you want more control. You could write this logic in a custom hook with the following API:
const { result, refresh } = useRequestMe()
// When needed, call refresh(). e.g. after a succesful POST request.
const onAdd = (data) => {
fetch('http://localhost:3002/request', ...)
.then(function (res) {
// console.log(res)
return res.json()
}).then(function (data) {
setPost({datas: data})
refresh();
console.log(data)
}).catch(function (e) {
console.log(e)
})
}

I'd like to set a higher timeout value or remove timeout from my node.js async digest request

I am trying to make a request that has a rather large attachment and therefore will take a bit of time, however i keep getting a Server timeout waiting for the HTTP request from the client. error. Setting a timeout value in the connection options has not worked (assuming this is as a result of the call being asynchronous).
My code looks as follows
const RequestDigest = require('request-digest');
const dc = RequestDigest(adminUser, adminPass);
let ocRequest = (url, opts) => {
opts = opts || {};
return new Promise((resolve, reject) => {
let connOptions = {
host: host,
path: url,
port: protoPort,
method: opts.method || 'GET',
timeout: 1200000,
headers: {
"User-Agent": "Node fallback CA",
"Connection": "keep-alive",
"X-Requested-Auth": 'Digest'
}
}
if (opts.form) {
connOptions.formData = opts.form;
}
let req = dc.requestAsync(connOptions)
.then(res => {
try {
return resolve(JSON.parse(res.body));
} catch(e) {
return resolve(res.body);
}
resolve(res.body);
})
.catch(err => {
reject(err)
});
});
};
What is the correct way of setting a high timeout value or telling the request to wait for a response? Thanks.
You can use request, or specifically request-promise.
This might help to explain how you can use the async result.
let request = require('request-promise')
let request_options = {
url : 'http://...',
timeout : 30000 //30 seconds
};
doRequest(request_options)
.then(result => result)
.catch(e => console.error(e))
function doRequest(options){
return request(options)
.then(result => {
try { return JSON.parse(result) }
catch(e) {return Promise.reject({code : 500, message : 'bad json'})}
})
.catch(e => return Promise.reject( e.code + ' happened : ' + e.message))
}

I get error when trying to subscribe to the getStream Websocket

Currently, I am integrating websockets for the feeds using GetStream JS library.
But I can't subscribe to the websocket by following the instruction
I have created Flat Feeds and it's working fine. I can do all the actions with activities. But I need to integrate my app with websocket so that it can update the feed live.
I've followed the steps that described in the feed document
async initializeStream(profile, followings = []) {
try {
const { peerID } = profile;
const response = await getStreamToken(peerID);
const { token } = response;
this.token = token;
this.client = stream.connect(STREAM_API_KEY, token, STREAM_APP_ID);
await this.createUser(profile);
this.feed = this.client.feed('user', peerID, token);
this.globalFeed = this.client.feed('user', 'all', token);
return true;
} catch (err) {
return false;
}
}
This is stream initialization part and it works fine without any flaw.
And below is subscription function:
subscribe (subscribingFunction) {
return this.feed.subscribe(subscribingFunction);
}
And this one is how I am using subscription function to integrate websocket:
StreamClient.subscribe((data) => {
console.log('stream - update received');
console.log(data);
// return emitter({ type: 'STREM/UPDATE_RECEIVED', payload: data });
}).then(response => {
console.log('success', response)
}).catch(response => {
console.log('failure', response)
});
Expected Result:
Websocket subscription is success and I can get the live updates through it.
Result:
I am getting this error when trying to subscribe to websocket:
klass {code: null, params: Array(0), message: "Failed to authenticate. Please check your API Token, App ID and Feed ID."}
Can you point me out what went wrong with this configuration?
Here's code for the getStreamToken function:
export const getStreamToken = (userId) => {
const apiURL = `${tokenServerAPI}/getToken?user_id=${userId}`;
const headers = {
method: 'GET',
headers: {
authorization: `Basic ${base64.encode('ob_stream_user:ob_stream_pwd')}`,
},
};
return fetch(
apiURL,
headers,
).then(response => response.json()).catch(err => {
console.log(err);
});
};

What is the proper way to execute axios / firebase promises in a specific order in a firebase function?

What is the best way to chain axios / firebase promises that must be linked in a specific order and use the returns of previous promises?
I am writing a firebase function that allows me to update a user via a third-party JWT API. So I have to fulfill several promises (I use axios for that) to build the final query with a uid, a token and a refresh token.
These requests must be executed in the right order, each promise waiting for the result of the previous one to be able to execute.
recover the firebase client token to identify the user
search in a collection for the tokens (access & refresh) that were previously stored and associated with the user's uid.
Execute the "me" request on the third-party API to retrieve the user's information and update the user.
My question: What is the most correct way to chase these axios promises?
For the moment, I have managed to achieve this result, by interlocking the calls successively to properly manage the "catch" and by moving in separate functions the calls to make a little more digest the reading of the code.
/* index.js */
const userModule = require('./user');
exports.me = functions.https.onRequest( (request, response) => {
cors(request, response, () => {
let idToken = request.body.data.token;
userModule
.get(idToken)
.then((uid) => {
console.log('User found : ' + uid);
return userModule
.retrieve(uid)
.then((userTokens) => {
console.log('User tokens found : ' + userTokens.token);
return userModule
.me(userTokens.token, uid)
.then((me) => {
return me;
}).catch((error) => {
return response.status(404).json({
data : {
error : 404,
message : 'NO_USER_ON_API'
}
});
})
}).catch((error) => {
console.log(error);
return response.status(404).json({
data : {
error : 404,
message : 'NO_TOKEN_USER_FOUND'
}
});
})
})
.catch((error) => {
console.log(error);
return response.status(500).json({
data : {
error : 500,
message : 'USER_TOKEN_NO_MATCH'
}
});
})
.then((user) => {
if(user.data !== undefined)
{
return response.status(200).json({
data : {
user : user.data
}
});
}
else
{
return response.status(204).json({
data : {
user : null
}
});
}
})
});
});
/* user.js */
exports.get = (firebaseToken) {
return admin.auth().verifyIdToken(firebaseToken)
.then(function(decodedToken) {
return decodedToken.uid;
})
.catch(function(error) {
throw {
code: 500,
body: "INTERNAL_ERROR"
};
});
};
exports.retrieve = (uid) {
return admin.firestore().collection("AccessTokenCollection").doc(uid).get()
.then(function(docRef) {
return docRef.data();
})
.catch(function(error) {
throw {
code: 404,
body: "NO_USER_FOUND"
};
});
};
exports.me = (UserToken, uid) {
let params = {
params: {
},
headers: {
'Authorization': 'Bearer ' + UserToken
}
};
return axiosInstance.instance.get(url + '/users/me', params)
.then(userMe => {
return userMe;
})
.catch(errMe => {
console.log(errMe.response.status);
throw {
code: 401,
body: "EXPIRING_TOKEN"
};
});
};
Etc...
The code works as it is more a theoretical question or optimization!
const userModule = require('./user');
exports.me = functions.https.onRequest((request, response) => {
cors(request, response, async () => {
let idToken = request.body.data.token;
try {
let uid = await userModule.get(idToken);
console.log('User found : ' + uid);
let userTokens = await userModule.retrieve(uid);
console.log('User tokens found : ' + userTokens.token);
let meObj = await userModule.me(userTokens.token, uid);
} catch (error) {
console.log('error', error);
}
});
});
So, here using async-await i have removed then-catch block. await keyword will work as then and will only move forward to second call after first call has been completed. And i have made a common catch block for error handling which you can modified according to your needs
you can use promise.all and async-await instead of then and catch

Resources