I'm new to react,I'm revamping my web application from angular to react. I've got stuck at this particular point. My application requires a token to perform API calls. when a token expires on a particular API call,I watch for the failure case and run a refresh token call,On the success of refresh token call i rerun that particular API. it was simple to implement in angular but i just can not figure out in react redux.
here is my angular code
var fetchAccessToken = function(successCallback,failureCallback){
if($cookies.get('aid')){
accountGetATAPI.save({
a_id : $cookies.get('aid'),
d_id : window.fingerprint.md5hash,
at : getAt(),
cl : "W",
rt : getRefreshToken(),
k_ey : getSecret()
},function (res) {
var at = res.data.at;
//console.log("server received token: " + at);
$cookies.put('at',res.data.at);
successCallback(res);
},function (res) {
failureCallback(res);
});
}
};
var fetchProfiles = function(successCallback,failureCallback,currentTry,maxTry) {
var aid = data["Account"]["_id"];
accountProfileFetchAPI.save({
a_id : getAccountId(),
tokn : getAt()
},function(res){
data["Profiles"] = res["data"]["Profile"];
//200
successCallback(res);
},function(res){
//not 200
if(res.status == error_constants.TOKEN_EXPIRE_CODE) {
fetchAccessToken(function(){
if(currentTry < maxTry) {
fetchProfiles(successCallback, failureCallback, ++currentTry, maxTry);
} else {
console.log("maximum tries exceeded");
}
},function(res){
if(res.status == (error_constants.TOKEN_EXPIRE_CODE || error_constants.TOKEN_ERROR_CODE)){
failureCallback(error_constants.LOGOUT_USER,res);
}
});
} else {
failureCallback(null,res);
}
});
};
I'm trying to do something similar in react code. till now i only have reached till here. i know the code is crap. but im still figuring out!
export function fetchProfiles(currentTry,maxTry) {
return function(dispatch) {
axios.post("/profile/find",{
a_id : cookies.load("aid"),
tokn : cookies.load("at")
}).then((response) => {
console.log(response);
dispatch({type: "FETCH_PROFILES_FULFILLED", payload: response.data.data})
})
.catch((err) => {
console.log(err)
if(err.response.status == 498){
fetchAccessToken((res) =>{
if(currentTry < maxTry) {
console.log("at successcall ",currentTry,maxTry);
fetchProfiles(++currentTry, maxTry);
} else {
console.log("maximum tries exceeded");
}
console.log(res);
},(err) =>{
console.log(err);
});
return;
}
dispatch({type: "FETCH_PROFILES_REJECTED", payload: err})
})
}
}
export function fetchAccessToken(successCallback,failureCallback){
axios.post("/account/g_at_w",{
a_id : cookies.load('aid'),
d_id : window.fingerprint.md5hash,
at : cookies.load('at'),
cl : "W",
rt : cookies.load('rt'),
k_ey : cookies.load('secret')
}).then((response) => {
console.log(response);
successCallback(response);
// dispatch({type: "FETCH_PROFILES_FULFILLED", payload: response.data.data})
})
.catch((err) => {
console.log(err.response);
failureCallback(err);
// dispatch({type: "FETCH_PROFILES_REJECTED", payload: err})
})
}
im using redux thunk and axios to perform my api calls. any help is very much appreciated.
Related
I try to explain the problem.in App.js I have Function getUser .when call this function.in first request get 401 error . For this reason in axios.interceptors.response I receive error 401.At this time, I receive a token and repeat my request again.And it is done successfully.But not return response in Function getUser.
I have hook for authentication and send request.
import React from "react";
import axios from "axios";
const API_URL = "http://127.0.0.1:4000/api/";
function useJWT() {
axios.interceptors.request.use(
(request) => {
request.headers.common["Accept"] = "application/json";
console.log("request Send ");
return request;
},
(error) => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log("answer = ", response);
return response;
},
(error) => {
if (error?.response?.status) {
switch (error.response.status) {
case 401:
refreshToken().then((responseTwo) => {
return
sendPostRequest(
error.response.config.url
.split("/")
.findLast((item) => true)
.toString(),
error.response.config.data
);
});
break;
case 500:
// Actions for Error 500
throw error;
default:
console.error("from hook interceptor => ", error);
throw error;
}
} else {
// Occurs for axios error.message = 'Network Error'
throw error;
}
}
);
const refreshToken = () => {
const token = localStorage.getItem("refresh");
return axios
.post(API_URL + "token", {
token,
})
.then((response) => {
if (response.data.access) {
localStorage.setItem("access", response.data.access);
}
if (response.data.refresh) {
localStorage.setItem("refresh", response.data.refresh);
}
return response.data;
});
};
function login(email, password) {
return axios
.post(API_URL + "login", {
email,
password,
})
.then((response) => {
if (response.data.access) {
localStorage.setItem("access", response.data.access);
}
if (response.data.refresh) {
localStorage.setItem("refresh", response.data.refresh);
}
return response.data;
});
}
const sendPostRequest = (url, data) => {
console.log(300);
const token = localStorage.getItem("access");
axios.defaults.headers.common["jwt"] = token;
return axios.post(API_URL + url, {
data,
});
};
const logout = () => {
const token = localStorage.getItem("refresh");
return axios
.delete(API_URL + "logout", {
token,
})
.then((response) => {
localStorage.removeItem("access");
localStorage.removeItem("refresh");
});
};
return {
login,
logout,
refreshToken,
sendPostRequest,
};
}
export default useJWT;
In App.js ,I want to repeat the same request again if a 401 error is issued when I read the user information.
The request is successfully repeated but does not return the value.
When first request fail response is return equals null . and in catch when receive 401 error i am send second request but not return response.
I send request below code .
const getUser = () => {
console.log(12);
return sendPostRequest("user");
};
useEffect(() => {
let token = localStorage.getItem("access");
console.log("token = ", token);
if (token != null) {
//Here I have done simulation for 401 error
localStorage.setItem("access", "");
getUser()
.then((response) => {
console.log("response 1= ", response);
})
.catch((exception) => {
console.log("exception = ", exception.toString());
})
.then((response) => {
console.log("response 2= ", response);
});
} else {
navigate("/login");
}
}, []);
Best regards.
I didn't fully understand what exactly you want to do here.
But if you are looking to retry when 401 happens, you could use axios-retry to do it for you.
I'll pass the basics, but you can look more into what this does.
// First you need to create an axios instance
const axiosClient = axios.create({
baseURL: 'API_URL',
// not needed
timeout: 30000
});
// Then you need to add this to the axiosRetry lib
axiosRetry(axiosClient, {
retries: 3,
// Doesn't need to be this, it can be a number in ms
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
// You could do this way or try to implement your own
return error.response.status > 400
// something like this works too.
// error.response.status === 401 || error.response.status >= 500;
}
});
Just like in your code, we need to use interceptors if you want to avoid breaking your page, otherwise you can use try catch to catch any errors that may happen in a request.
// It could be something like this, like I said, it's not really needed.
axiosClient.interceptors.response.use(
(success) => success,
(err) => err
);
And finally, you could use the axiosClient directly since it now has your API_URL, calling it like this axiosClient.post('/user').
More or less that's it, you should just debug this code and see what is causing the return value to be null.
I would change these then/catch to be an async/await function, it would be more readable making your debugging easier.
axios-retry example if you didn't understand my explanation.
I find anwser for this question.
When error 401 occurs then create new Promise
I Wrote this code.
case 401:
return new Promise((resolve, reject) => {
refreshToken().then((responseTwo) => {
resolve(
sendPostRequest(
error.response.config.url
.split("/")
.findLast((item) => true)
.toString(),
error.response.config.data
)
);
});
});
The below angular code switchMap is not working, I'm not sure what error I made. Under the switchMap second API call not triggering '/upload/file'
zip(
this.service.submitForm(formValue),
this.service.upload(fData)
).subscribe(
([submitForm, upload]) => {
if (submitForm === 'success' && upload === 'Ok') {
//Redirect confirmation page
}
},
(err) => {
console.log(err, 'ERORORO');
}
)
//Service code
upload(formData): Observable <any> {
return this.sessionService.keepAlive().pipe(
switchMap(data => {
let token = data.jwtToken;
console.log(token, 'TOKEN SESSION');
// getting output as Bearer xyz
// with formData as req
const request_config = {
headers: {
"Authorization": token
}
};
console.log("REQUEST CONFIG", request_config); // getting output
return this.http.post < any > (
'/upload/file',
formData,
request_config
).pipe( // this is not working
map((res) => {
console.log(res, 'RESPONSE');
return res.status;
}),
catchError((error: HttpErrorResponse) => {
throw error;
})
)
})
)
}
I have a nodeJS app in which I designed it to run three API calls based on the name of the business and return results. What i want to do is run this app in a CSV file that I have. Below is my code. Any ideas on how to do this? Below is a sample of my code. Im trying to run the name column, pass it inside my functions and return the results inside the CSV file.
function fetchYelp() {
let token = '<Token>';
axios.get('https://api.yelp.com/v3/businesses/search?term=Grace Christian Fellowship&location=Mounds, il 62964',{
headers: {
Authorization: 'Bearer ' + token
}
})
.then(res => {
if(res.data.businesses.length < 1){
fetchWhitePages();
}else{
console.log(res.data.businesses);
console.log('running YelpAPI')
}
})
.catch(err => {
console.log(err)
})
}
fetchYelp();
function fetchWhitePages() {
axios.get('https://proapi.whitepages.com/3.0/business?api_key=<key>&address.city=Mounds&address.country_code=US&address.postal_code=62964&address.state_code=il&name=Grace Christian Fellowship')
.then(res => {
if(!res.data.business.length < 1){
fetchGooglePlace()
}else{
console.log(res.data);
console.log('running whitePagesAPi')
}
})
.catch(err => {
console.log(err)
});
}
function fetchGooglePlace(){
axios.get('https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=Grace Christian Fellowship, mounds il 62964&inputtype=textquery&fields=photos,formatted_address,name,rating,opening_hours,geometry,place_id&key=<APIKEY>')
.then(res => {
if(res.data.candidates.length < 1){
console.log('Manual Search')
}else{
console.log(res.data.candidates[0].place_id);
console.log('Passing Place ID to fetchGoogleTel')
fetchGoogleTel()
}
})
.catch(err => {
console.log(err)
});
}
function fetchGoogleTel ()
I have tried several ways for mocking this unit of my code but still, it doesn't work. I'm using create-react-app and jest for testing.
I have a function in admin adminSignup.js for sending data to my server(Node.js and Mongoose) for creating account:
/* eslint-disable no-undef */
function signup(user, cb) {
return fetch(`signup`, {
headers: {"Content-Type": "application/json"},
method: "POST",
body:JSON.stringify({
username: user.username,
email: user.email,
password: user.password,
picode: user.pincode,
building: user.building,
city: user.city,
state: user.state
}),
})
.then(checkStatus)
.then(parseJSON)
.then(cb)
.catch(err => console.log(err));
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(`HTTP Error ${response.statusText}`);
error.status = response.statusText;
error.response = response;
console.log(error); // eslint-disable-line no-console
throw error;
}
function parseJSON(response) {
return response.json();
}
const adminSignup = { signup };
export default adminSignup;
and I have called this in my component(RegisterPage.jsx) :
adminSignup.signup( user, response => {
this.setState({response: response});
console.log(response);
});
Now I want to write a mock for my signup call(adminSignup.js). But just wonder how can I do this?
I have tried Jest Fetch Mock for mock testing(it doesnt need to create mock file) and it's working but I'm not quite sure is it correct or no :
describe('testing api', () => {
beforeEach(() => {
fetch.resetMocks();
});
it('calls signup and returns message to me', () => {
expect.assertions(1);
fetch.mockResponseOnce(JSON.stringify('Account Created Successfully,Please Check Your Email For Account Confirmation.' ));
//assert on the response
adminSignup.signup({
"email" : "sample#yahoo.com",
"password" : "$2a$0yuImLGh1NIoJoRe8VKmoRkLbuH8SU6o2a",
"username" : "username",
"pincode" : "1",
"city" : "Sydney",
"building" : "1",
"state" : "NSW"
}).then(res => {
expect(res).toEqual('Account Created Successfully,Please Check Your Email For Account Confirmation.');
});
//assert on the times called and arguments given to fetch
expect(fetch.mock.calls.length).toEqual(1);
});
});
I really like to create a mock file and test with that but reading jest website is not working for me.
Thanks in advance.
I have found this way(using mock-http-server) for another POST request and it works for me:
userList.js:
async function getUser (id, cb) {
const response = await fetch(`/getUserById/${id}`, {
// headers: {"Content-Type": "application/json"},
method: "POST",
body:JSON.stringify({
id : id
}),
})
.then(checkStatus)
.then(parseJSON)
.then(cb)
.catch(err => console.log(err));
const user = response.json();
return user;
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(`HTTP Error ${response.statusText}`);
error.status = response.statusText;
error.response = response;
console.log(error); // eslint-disable-line no-console
throw error;
}
function parseJSON(response) {
return response.json();
}
}
userList.test.js:
import ServerMock from "mock-http-server";
import userLists from '../components/UserList/userLists';
describe('Test with mock-http-server', function() {
// Run an HTTP server on localhost:3000
var server = new ServerMock({ host: "localhost", port: 3000 });
beforeEach(function(done) {
server.start(done);
});
afterEach(function(done) {
server.stop(done);
});
it('should do something', function(done) {
var id = 4;
server.on({
method: 'POST',
path: `/getUserById/${id}`,
reply: {
status: 200,
headers: { "content-type": "application/json" },
body: JSON.stringify({ id: 4 })
}
});
// Now the server mock will handle a GET http://localhost:3000//getUserById/${id}
// and will reply with 200 `{"id": 4}`
function cb(data) {
console.log(data);
expect(data).toBe({name:'Bob'});
done();
}
const response = userLists.getUser(4, cb);
console.log(response);
done();
});
I use axios with vue.js to fetch data as unlimited pagination. It works fine expect when there is no data to render:
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response =>
//how to exist if there is no data in the response?
this.jokes = response.data).catch(function (error) {
console.log(error);
});
I'm wondering how to stop rendering when we reached to the last page and there is no more data to display?
I've looked at the docs but could not find my answer.
Maybe introducing some basic flag logics. I have taken the freedom to assume, but you can always define your logic
fetchData() {
this.loading = true;
if (this.page > 0) {
axios.get(this.BASE_URL + '/api/jokes/page=' + this.page)
.then(response => {
const isDataAvailable = response.data && response.data.length;
this.jokes = isDataAvailable ? response.data : [];
this.page = isDataAvailable ? (this.page + 1) : 0;
})
.catch(function(error) {
console.error(error);
});
}
}
You could throw a custom error that will be catch by the catch method.
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response => {
//how to exist if there is no data in the response?
if (!response.data || response.data.length == 0) {
const emptyDataError = new Error('Invalid data');
emptyDataError.statusCode = 500;
throw emptyDataError;
}
this.jokes = response.data;
}).catch(function (error) {
console.log(error);
});