Periodically fetching the auth token while the async function is running - node.js

I have this authFetch function that fetches the token and updates it in mongo and token variable.
const authFetch = async () => {
let authCollection = client.db("WheelPros_data").collection("auth");
TokenFromDB = await authCollection.find({ _id: 1 }).toArray();
({ accessToken: token, time: lastFetchedAuth } = TokenFromDB[0] || [{ accessToken: null, time: 0 }])
// console.log(new Date().getTime() / 1000 - lastFetchedAuth)
let axiosConfig = {
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
};
await axios({
method: "POST",
data: {
userName: process.env.WHEELPROS_USERNAME,
password: process.env.PASSWORD,
},
url: `${process.env.PROD_API_URL}/auth/v1/authorize`,
axiosConfig,
})
.then(async (res) => {
const { accessToken } = res.data;
authCollection.updateOne(
{ _id: 1 },
{
$set: {
accessToken: accessToken,
time: new Date().getTime() / 1000,
Date: new Date(),
},
}, {
upsert: true
}
);
console.log("newAccessToken : ", accessToken)
token = await accessToken
console.log("inside token var = ", token)
console.log("------------------Auth Updated------------------");
})
.catch((err) => {
console.log("AXIOS ERROR: ", err);
});
}
await authFetch()
// console.log("ourside token var = ", token)
setInterval(authFetch, 3590000)
Problem arises when i call another async function to fetch the submodels. After 1 hour the token expires and that is why we have setInterval function to update the token but it does not update the token and i get hit with the 403 error
Below is the modelFetch function
const fetchSubmodels = async () => {
const modelDataFromDB = await collection
.find({ modelData: { $exists: true } })
.toArray();
console.log(modelDataFromDB.length)
modelDataFromDB.forEach(async (modeldata) => {
// await fetchToken().catch(console.error)
let dataSearch = modeldata.modelData.split(';')
await axios
.get(
`${process.env.PROD_API_URL}/${dataSearch[0]}/makes/${dataSearch[1]}/models/${dataSearch[2]}/submodels`,
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
}
)
.then(async function ({ data }) {
await collection.updateOne(
{ modelData: modeldata.modelData },
{
$set: {
submodels: data
},
}
);
// handle success
console.log(`pushing the submodels ${data} to ${modeldata.modelData} `)
})
.catch(async function (error) {
// handle error
console.log(error);
})
})
}
await fetchSubmodels()
clearInterval(authFetch)
Any insights would be helpful!

Like array.map, array.forEach does not await the result of the function that it calls. In your case, this means that the axios.get statements for each entry in modelDataFromDB send out all requests synchronously, while token still has its original value. (Therefore they should never lead to 403, if they reach the backend in time.)
What's more, fetchSubmodels does not await anything and therefore resolves after all these requests have been sent out. At that point in time await fetchSubmodels() is completed, therefore clearInterval(authFetch) is executed almost immediately, and the setInterval has no effect.
While this does not yet explain the 403 responses, it is certainly not what you intended.
If you replace
modelDataFromDB.forEach(async (modeldata) => {
...
})
with
for (var modeldata of modelDataFromDB) {
...
}
the requests will be sent consecutively, and fetchSubmodels will await the response of the last request.

Related

Apollo client does not send headers when used in nodejs environment

I've got a client-side app that uses apollo/client and I'have a few server-side function where also use the client to do some light queries and mutations.
I've recently implemented jwt integration and now my server-side calls fail because of what I presume is missing headers.
here is my client setup:
const wsLink = new GraphQLWsLink(
createClient({
url: process.env.API_URL_WS,
webSocketImpl: isNode ? ws : null,
connectionParams: async () => {
const { id_token } = await auth.getSession();
return {
headers: {
Authorization: `Bearer ${id_token}`,
},
};
},
})
);
const authLink = setContext(async (_, { headers }) => {
const { id_token } = await auth.getSession();
return {
headers: {
...headers,
Authorization: `Bearer ${id_token}`,
},
};
});
const httpLink = new HttpLink({
uri: process.env.API_URL_HTTP,
});
const splitLink = split(
({ query, ...rest }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
authLink.concat(httpLink)
);
export const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
and here is how I am using it in the server-side function to make a mutation
await client
.mutate({
mutation: gql`
mutation InsertSubscriptionId(
$id: uuid!
$stripe_subscription_id: String!
) {
update_user_by_pk(
pk_columns: { id: $id }
_set: { stripe_subscription_id: $stripe_subscription_id }
) {
id
}
}
`,
variables: {
id: session.metadata.hasura_user_id,
stripe_subscription_id: session.subscription,
},
context: {
headers: {
Authorization: req.cookies.access_token,
},
},
})
.then((result) => console.log(result))
.catch(console.log);
this call fails even if I use the users access token or apply an admin token for access to the DB.
As a workaround I've resorted to using fetch for this mutation (which works). Any idea why headers are not respected?
My workaround with admin access secret:
fetch(process.env.API_URL_HTTP, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-hasura-admin-secret": process.env.GATSBY_HASURA_ADMIN_SECRET,
},
body: JSON.stringify({
query: `
mutation InsertSubscriptionId(
$id: uuid!
$stripe_subscription_id: String!
) {
update_user_by_pk(
pk_columns: { id: $id }
_set: { stripe_subscription_id: $stripe_subscription_id }
) {
id
}
}
`,
variables: {
id: session.metadata.hasura_user_id,
stripe_subscription_id: session.subscription,
},
}),
})
.then((res) => res.json())
.then((result) => console.log(result));
In this case the session was lost after a few jumps between my app and payment provider. As user can't be authenticated when session is lost, I've resorted to authenticate the admin user for this ssr function

How to receive re-requested data when data requested by mounted() is re-requested by aixos interceptor

the code below is the first request for get list data
mounted() {
this.getList()
},
methods: {
handleClick(row) {
console.log(row.id)
console.log(row.url)
this.$router.push({ name: 'Main', query: { id: row.id } })
},
getList() {
axios
.get('/api/v1/requests/all', {
params: {
userId: this.$store.state.userInfo.id,
},
})
.then(response => {
let moment = require('moment')
for (var item of response.data.data) {
item.createdAt = moment(item.createdAt).format(
'YYYY-MM-DD HH:mm:ss',
)
}
this.items = response.data.data
})
.catch(error => {
console.log(error)
})
}
my interceptor
axios.interceptors.response.use(
function (response) {
return response
},
async function (error) {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true
sessionStorage.removeItem('access-token')
let headers = {
grant_type: 'refresh_token',
Authorization: sessionStorage.getItem('refresh-token'),
}
axios
.post('/api/v1/users/refresh_token', {}, { headers: headers })
.then(response => {
let token = response.data.data
sessionStorage.setItem('access-token', token)
originalRequest.headers['Authorization'] = token
originalRequest.headers['grant_type'] = 'grant_type'
return axios.request(originalRequest)
})
.catch(error => {
console.log(error)
alert('blablabla.')
})
}
return Promise.reject(error)
},
)
the flow is i understand
1.token expired
2.move to list page
3.mounted hook is request data
4.getList -> axios get('~~request/all')
5.interceptor->axios post('~~~refresh_token')
6.re request with new token(request/all)
7.re request is 200, but not update list page
i'd really appreciate your help :)
Seems like you need to return the second request (await for result and return). Right now the result of second request seems to be ignored
axios.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
error.response.config._retry = true;
sessionStorage.removeItem("access-token");
let headers = {
grant_type: "refresh_token",
Authorization: sessionStorage.getItem("refresh-token"),
};
const [secondError, res] = await axios // await here
.post("/api/v1/users/refresh_token", {}, { headers: headers })
.then(async (response) => {
let token = response.data.data;
sessionStorage.setItem("access-token", token);
originalRequest.headers["Authorization"] = token;
originalRequest.headers["grant_type"] = "grant_type";
return [null, await axios.request(originalRequest)];
})
.catch((err) => {
console.log(err);
alert("blablabla.");
return [err, null];
});
// Handle here
if(secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
return Promise.reject(error);
}
);
The above solution worked for me perfectly. here's the modified code that I used for my requirement.
export default function axiosInterceptor() {
//Add a response interceptor
axios.interceptors.response.use(
(res) => {
// Add configurations here
return res;
},
async function (error) {
const originalRequest = error.config;
let secondError, res
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
[secondError, res] = await axios({
method: "POST",
url: `${baseURL}/account/token/refreshtoken`,
withCredentials: true,
headers: { "Content-Type": "application/json" },
})
.then(async (response) => {
//handle success
return [null, await axios.request(originalRequest)];
}).catch(function (error) {
console.error(error)
});
}
if (secondError) {
return Promise.reject(secondError);
}
return Promise.resolve(res)
}
);
}

i'm getting isssue while calling API in Useeffect in reactJs

I'm calling two methods in useeffect but when api1 get called it get executed till await axios.put and then api2 get called without getting the response from api1. And after getting a reponse from api2 it goes to api1 reponse and gets a reponse as undefined
useEffect(() => {
api1();
api2();
}, [])
const api1 = async () => {
try {
var requestModel = JSON.stringify({ UserId: userid, MenuName: menuname });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.put(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
//setapidata(response.data);
}
});
} catch (error) {
console.error(error.message)
}
}
const api2= async () => {
try {
var requestModel = JSON.stringify({ UserID: userid });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.post(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
setapidata(response.data);
GetApis();
}
});
} catch (error) {
console.error(error.message)
}
}
If you want to wait for the api1 to execute before api2 is called you need to change the code for useEffect as below
useEffect(async() => {
await api1();
await api2();
}, [])
or
useEffect(() => {
api1().then(() => api2());
}, [])
To handle errors properly use try-catch block inside the useEffect if using await or use catch block if using .then
Also, another suggestion, if you are using a function inside useEffect make sure either the function is defined inside the same useEffect block or it is memorized either via useCallback or useMemo

I get undefined when reading my response but there is a response in React.js

I can't figure it out, the answer comes in the network table but when I want to console.log it, this will display undefined. Do you have any idea why? I attach the pictures and the code.
Here is a image with my codes and response
Here is the code - first one is where I send the response. As I said, it's going well on network tab, I get a 200 status.
export const getAccountStatus = async (req, res) => {
const user = await User.findById(req.user._id).exec();
const account = await stripe.accounts.retrieve(user.stripe_account_id);
// console.log("user account retrieve", account);
const updatedUser = await User.findByIdAndUpdate(
user._id,
{
stripe_seller: account
},
{ new: true }
)
.select("-password")
.exec();
console.log(updatedUser);
res.send(updatedUser);
};
Here is the page where i want to console.log it:
const StripeCallback = ({ history }) => {
const { auth } = useSelector(state => ({ ...state }));
const dispatch = useDispatch();
useEffect(() => {
if (auth && auth.token) accountStatus();
}, [auth]);
const accountStatus = async () => {
try {
const res = await getAccountStatus(auth.token);
console.log(res);
} catch (err) {
console.log(err);
}
};
return <div>test</div>;
};
Ang here is the Axios.post (which is working well as I know):
export const getAccountStatus = async token => {
await axios.post(
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};
Thank you!
getAccountStatus doesn't have a return statement, so res in const res = await getAccountStatus(auth.token); will always be undefined.
export const getAccountStatus = async token => {
return axios.post( // <----- added return
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};

API call works with Postman, but does not with Axios

I am using third party API. The way it works is:
I send post request then Token is returned in response.
Then i use that Token to check status. Afterwards, report is returned in response
In postman, i make both calls separately and it is working, but in Axios I have 1 async function and 2 await Promises.
Postman(NodeJs - Axios) looks like this:
For getting Token:
var data = JSON.stringify({
"security": {
"pLogin": "a",
"pPassword": "io"
},
"data": {
"pHead": "005",
"pCode": "00433",
"pLegal": 1,
"pClaimId": "z4LpXRWZKecSnL-FQtgD",
"pReportId": 8,
"pReportFormat": 1
}
});
var config = {
method: 'post',
url: 'http://10.22.50.10/report/',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
For getting Report with the token:
var data = JSON.stringify({
"data": {
"pHead": "005",
"pCode": "00433",
"pToken": "kgqjismxdrpjnjaqnlnbmovcsvnkarfd",
"pClaimId": "z4LpXRWZKecSnL-FQtgD",
"pReportFormat": 1
}
});
var config = {
method: 'post',
url: 'http://10.22.50.10/report/status',
headers: {
'Content-Type': 'application/json'
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
My async function with Axios:
/* 1. Searching for client in database (Full name is used, but can be changed)*/
const client = await client.findOne({
name: body.name,
family_name: body.family_name,
patronymic: body.patronymic
});
if (!client) {
return res.status(401).json({ message: "Client is not registered" });
}
/* 2. If client was found in database, make an API call */
let credit_report;
try{
credit_report = await axios.post(
'http://10.22.50.10/report',
{
security: {
pLogin: 'a',
pPassword: 'io',
},
data: {
pHead: "005",
pCode: "00433",
pLegal: 1,
pClaimId: client.claim_id,
pReportId: 8,
pReportFormat: 1
}
},
{
headers: {
'content-type': 'application/json'
}
}
);
}catch(err){
return res.status(400).json({errorMessage: err.message})
}
// await new Promise(resolve => setTimeout(resolve, 3000));
if(!credit_report.data.data.token) return res.status(400).json({message: credit_report.data});
const credit_report_status = await axios.post(
'http://10.22.50.10/report/status',
{
data: {
pHead: "005",
pCode: "00433",
pToken: credit_report.data.data.token,
pClaimId: client.claim_id,
pReportFormat: 1
}
},
{
headers: {
'content-type': 'application/json'
}
}
);
console.log(credit_report_status)
if(credit_report_status.data.data.result == '05000') return res.status(200).json({ message: 'Client fetched.', clientData64: credit_report_status.data.data.reportBase64});
else return res.status(400).json({message: credit_report_status.data})
When I am using Postman to check my module, it is saying Error 400 Bad Request

Resources