import * as types from '../constants/actionTypes';
import { all, call, fork, race, take, cancelled, put, select } from 'redux-saga/effects'
import config from '../config';
import jwt_decode from 'jwt-decode';
import moment from 'moment';
import { delay } from 'redux-saga';
import { setOnlineStatus } from '../actions/networkActions';
import { getToken } from '../selectors/authSelectors';
function pingServer(client, token) {
const decodedToken = jwt_decode(token);
const currentTs = moment.now()/1000;
const timeout = new Promise((resolve, reject) => {
setTimeout(reject, config.networkStatusTimeout, 'request timed out');
});
if((decodedToken.exp-currentTs) < 300) {
return Promise
.race([timeout, client.post('/api/auth/refresh/', { token })]);
} else {
// We do a GET because a HEAD generate a preflight CORS OPTION request. The GET does not.
return Promise
.race([timeout, client.get('/api/auth/user/')]);
}
}
function* pollData(context) {
const token = yield select(getToken);
// No token : we wait for a login
if(!token) {
yield take(types.AUTH_LOGIN_SUCCESS);
}
try {
const res = yield pingServer(context.client, token);
yield put(setOnlineStatus(true));
if(res.token) {
yield put({
type: types.AUTH_STORE_TOKEN_ASYNC,
token: res.token,
});
}
} catch (error) {
yield put(setOnlineStatus(false));
//TODO: This is ugly...
const data = error.data;
if ((data && data.non_field_errors &&
data.non_field_errors &&
data.non_field_errors.length &&
data.non_field_errors.length > 0 &&
( data.non_field_errors[0] === 'Signature has expired.' ||
data.non_field_errors[0] === 'Refresh has expired.' )) ||
(error.detail && (
error.detail === 'Signature has expired.' ||
error.detail=== 'Refresh has expired.'
))
) {
yield put({
type: types.AUTH_DEAUTHENTICATE
});
}
} finally {
if (yield cancelled()) {
// pollDate cancelled
// if there is a token : this was a LOGIN, set status to ok
// if there is no token : this was a LOGOUT, set status to ko and wait for login
const token = yield select(getToken);
yield put(setOnlineStatus(Boolean(token)));
}
}
}
function* callDelay(context) {
try {
yield call(delay, config.networkStatusInterval);
} finally {
if (yield cancelled()) {
// pollDate cancelled
// if there is a token : this was a LOGIN, set status to ok
// if there is no token : this was a LOGOUT, set status to ko and wait for login
const token = yield select(getToken);
yield put(setOnlineStatus(Boolean(token)));
}
}
}
// Wait for successful response, then fire another request
// Cancel polling if user logs out or log in
function* watchPollData(context) {
while (true) {
yield race({
poll: all([call(pollData, context), call(callDelay, context)]),
logout: take(types.AUTH_LOGOUT),
login: take(types.AUTH_LOGIN_SUCCESS)
});
}
}
// Daemonize tasks in parallel
export default function* root(context) {
yield all([
fork(watchPollData, context)
// other watchers here
]);
}