client/src/sagas/networkSaga.js
author ymh <ymh.work@gmail.com>
Tue, 27 Jun 2017 11:38:26 +0200
changeset 97 69eaef18b01b
parent 88 2a861fed6bde
child 129 d48946d164c6
permissions -rw-r--r--
Improve the network saga. Try to avoid unnecessary token refresh
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';
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
// Utility function to delay effects
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
function delay(millis) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
    const promise = new Promise(resolve => {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
        setTimeout(() => resolve(true), millis)
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
    return promise;
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
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
    17
  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
    18
  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
    19
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    20
  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
    21
    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
    22
  });
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    23
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    24
  if((decodedToken.exp-currentTs) < 300) {
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
    return Promise
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
      .race([timeout, client.post('/api/auth/refresh/', { token })]);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
  } else {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    28
    // 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
    29
    return Promise
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    30
      .race([timeout, client.get('/api/auth/user/')]);
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
function* pollData(context) {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    35
  const token = yield select(state => state.token);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    36
  // 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
    37
  if(!token) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    38
    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
    39
  }
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
  try {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    const res = yield pingServer(context.client, token);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
    yield call(context.callback, true);
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    43
    if(res.token) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    44
      yield put({
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    45
        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
    46
        token: res.token,
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    47
      });
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    48
    }
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
  } catch (error) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
    yield call(context.callback, false);
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    51
    //TODO: This is ugly...
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    52
    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
    53
      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
    54
      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
    55
      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
    56
      ( 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
    57
        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
    58
      (error.detail && (
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    59
        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
    60
        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
    61
      ))
88
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    62
    ) {
2a861fed6bde Deauthenticate user if refresh was not possible (session expired or total delta reached)
ymh <ymh.work@gmail.com>
parents: 87
diff changeset
    63
      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
    64
        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
    65
      });
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
  } finally {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    if (yield cancelled()) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
      // pollDate cancelled
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
      // 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
    71
      // if there is no token : this was a LOGOUT, set status to ko and wait for login
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
      const token = yield select(state => state.token);
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    73
      yield call(context.callback, Boolean(token));
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
  }
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    76
}
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    77
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    78
function* callDelay(context) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    79
  try {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    80
    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
    81
  } finally {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    82
    if (yield cancelled()) {
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    83
      // pollDate cancelled
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    84
      // 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
    85
      // if there is no token : this was a LOGOUT, set status to ko and wait for login
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    86
      const token = yield select(state => state.token);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    87
      yield call(context.callback, Boolean(token));
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
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
// Wait for successful response, then fire another request
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
// Cancel polling if user logs out or log in
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
function* watchPollData(context) {
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    95
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    96
  //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
    97
  yield take(persistConstants.REHYDRATE);
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
    98
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
  while (true) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
    yield race([
97
69eaef18b01b Improve the network saga. Try to avoid unnecessary token refresh
ymh <ymh.work@gmail.com>
parents: 88
diff changeset
   101
      all([call(pollData, context), call(callDelay, context)]),
87
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
      take(types.AUTH_LOGOUT),
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
      take(types.AUTH_LOGIN_SUCCESS)
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
  }
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
}
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
// Daemonize tasks in parallel
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
export default function* root(baseContext) {
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
  const actionRes = yield take(types.OFFLINE_CONFIG_INITIALIZED);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
  const context = {...baseContext, ...actionRes.additionalContext};
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
  yield all([
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
    fork(watchPollData, context)
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
    // other watchers here
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
  ]);
dbcee57de2c6 Add first implementation of network monitor
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
}