client/src/sagas/networkSaga.js
author ymh <ymh.work@gmail.com>
Sun, 30 Jul 2017 01:02:09 +0200
changeset 130 78246db1cbac
parent 129 d48946d164c6
child 134 be36eed5e6e0
permissions -rw-r--r--
make synchronization recurent, improve synchronization status display
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
import * as types from '../constants/actionTypes';
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
import { all, call, fork, race, take, cancelled, put, select } from 'redux-saga/effects'
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
import config from '../config';
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
     4
import * as persistConstants  from 'redux-persist/constants';
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
     5
import jwt_decode from 'jwt-decode';
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
     6
import moment from 'moment';
130
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
     7
import { delay } from 'redux-saga';
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
     8
import { setOnlineStatus } from '../actions/networkActions';
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
     9
import { getToken } from './selectors';
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    11
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
function pingServer(client, token) {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    14
  const decodedToken = jwt_decode(token);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    15
  const currentTs = moment.now()/1000;
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    16
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    17
  const timeout = new Promise((resolve, reject) => {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    18
    setTimeout(reject, config.networkStatusTimeout, 'request timed out');
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    19
  });
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    20
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    21
  if((decodedToken.exp-currentTs) < 300) {
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
    return Promise
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
      .race([timeout, client.post('/api/auth/refresh/', { token })]);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
  } else {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    25
    // We do a GET because a HEAD generate a preflight CORS OPTION request. The GET does not.
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    26
    return Promise
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    27
      .race([timeout, client.get('/api/auth/user/')]);
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
function* pollData(context) {
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    32
  const token = yield select(getToken);
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    33
  // No token : we wait for a login
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    34
  if(!token) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    35
    yield take(types.AUTH_LOGIN_SUCCESS);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    36
  }
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
  try {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
    const res = yield pingServer(context.client, token);
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    39
    yield put(setOnlineStatus(true));
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    40
    if(res.token) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    41
      yield put({
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    42
        type: types.AUTH_STORE_TOKEN_ASYNC,
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    43
        token: res.token,
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    44
      });
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    45
    }
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
  } catch (error) {
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    47
    yield put(setOnlineStatus(false));
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    48
    //TODO: This is ugly...
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    49
    if ((error.non_field_errors &&
88
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    50
      error.non_field_errors &&
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    51
      error.non_field_errors.length &&
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    52
      error.non_field_errors.length > 0 &&
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    53
      ( error.non_field_errors[0] === 'Signature has expired.' ||
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    54
        error.non_field_errors[0] === 'Refresh has expired.' )) ||
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    55
      (error.detail && (
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    56
        error.detail === 'Signature has expired.' ||
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    57
        error.detail=== 'Refresh has expired.'
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    58
      ))
88
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    59
    ) {
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    60
      yield put({
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    61
        type: types.AUTH_DEAUTHENTICATE
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    62
      });
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
  } finally {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    if (yield cancelled()) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
      // pollDate cancelled
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
      // if there is a token : this was a LOGIN, set status to ok
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
      // if there is no token : this was a LOGOUT, set status to ko and wait for login
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    69
      const token = yield select(getToken);
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    70
      yield put(setOnlineStatus(Boolean(token)));
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    71
    }
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    72
  }
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    73
}
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    74
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    75
function* callDelay(context) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    76
  try {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    77
    yield call(delay, config.networkStatusInterval);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    78
  } finally {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    79
    if (yield cancelled()) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    80
      // pollDate cancelled
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    81
      // if there is a token : this was a LOGIN, set status to ok
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    82
      // if there is no token : this was a LOGOUT, set status to ko and wait for login
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    83
      const token = yield select(getToken);
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
    84
      yield put(setOnlineStatus(Boolean(token)));
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
    }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
// Wait for successful response, then fire another request
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
// Cancel polling if user logs out or log in
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
function* watchPollData(context) {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    92
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    93
  //wait for the state to be rehydrated
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    94
  yield take(persistConstants.REHYDRATE);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    95
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
  while (true) {
130
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
    97
    yield race({
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
    98
      poll: all([call(pollData, context), call(callDelay, context)]),
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
    99
      logout: take(types.AUTH_LOGOUT),
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
   100
      login: take(types.AUTH_LOGIN_SUCCESS)
78246db1cbac make synchronization recurent, improve synchronization status display
ymh <ymh.work@gmail.com>
parents: 129
diff changeset
   101
    });
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
// Daemonize tasks in parallel
129
d48946d164c6 Add a first version of synchronisation
ymh <ymh.work@gmail.com>
parents: 97
diff changeset
   106
export default function* root(context) {
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
  yield all([
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
    fork(watchPollData, context)
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
    // other watchers here
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
  ]);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
}