frontend/src/actions/api.js
// ids is a list of id's of resources that need to be updated, guaranteed
ids is a list of id's of resources that need to be updated, guaranteed
ids is a list of id ' s of resources that need to be updated guaranteed
function callAPI({
  url, filters, method = 'GET', body, type, meta, storage,
  loaders, ids, deps, onSuccess, onFailure,
}) {
  if (typeof type !== 'string' || !type.length) {
    throw new Error('expected type to be a non-empty string');
  }
  return (dispatch, getState) => {
    const headers = {};
    const token = getState().session.get('token');
    if (token) {
      headers.Authorization = token;
    }
    if (body) {
      headers['Content-Type'] = 'application/json';
    }
    dispatch({
      type: `${type}__REQUEST`,
      meta,
    });
    dispatch({ type: 'LOADING' });
    fetch(`${API_PREFIX}${url}${query(filters)}`, {
      method,
      headers,
      body: JSON.stringify(body),
    })
      .then(checkStatusParseJSON)
      .then((json) => {
        dispatch({
          type: `${type}__SUCCESS`,
          storage,
          meta,
          payload: json,
        });
        if (Array.isArray(ids) && ids.length) {
          const freshIds = json.reduce((r, x) => r.concat(x.id), []);
          ids.forEach((id) => {
            if (!freshIds.includes(id)) dispatch(loaders._self(id, deps));
          });
        }
        (() => {
          const depLoaders = Object.entries(pick(loaders, deps));
          if (!Object.keys(depLoaders).length) return;
          let data = json;
          if (!Array.isArray(data)) data = [data];
          data.forEach((x) => {
            depLoaders.forEach(([dep, loader]) => {
              const id = x[dep];
              if (id === undefined || id === null) return;
              dispatch(loader(id, undefined, x));
            });
          });
        })();
        typeof onSuccess === 'function' && onSuccess(json, dispatch, getState);
        dispatch({ type: 'LOADED' });
      }, (err) => {
        if (!Array.isArray(err)) throw err; 
        const [message, response] = err;
        const isUnauthorized = response.status === 401;
        const isExpired = isUnauthorized && message === 'session expired';
        const isBadToken = isUnauthorized && message === 'bad token';
        if (isExpired || isBadToken) dispatch({ type: 'CLEAR_SESSION' });
        dispatch({
          type: `${type}__FAILURE`,
          storage,
          meta,
          isNotFound: response.status === 404,
        });
        if (typeof onFailure === 'function') {
          onFailure(message, dispatch, getState);
        }
        dispatch(messagePush(message, true, response.status, url));
        dispatch({ type: 'LOADED' });
      });
  };
}
function callAPI ( { url , filters , method = 'GET' , body , type , meta , storage , loaders , ids , deps , onSuccess , onFailure , } ) { if ( typeof type !== 'string' || ! type . length ) { throw new Error ( 'expected type to be a non-empty string' ) ; } return ( dispatch , getState ) => { const headers = { } ; const token = getState ( ) . session . get ( 'token' ) ; if ( token ) { headers . Authorization = token ; } if ( body ) { headers [ 'Content-Type' ] = 'application/json' ; } dispatch ( { type : ` ${ type } ` , meta , } ) ; dispatch ( { type : 'LOADING' } ) ; fetch ( ` ${ API_PREFIX } ${ url } ${ query ( filters ) } ` , { method , headers , body : JSON . stringify ( body ) , } ) . then ( checkStatusParseJSON ) . then ( ( json ) => { dispatch ( { type : ` ${ type } ` , storage , meta , payload : json , } ) ; if ( Array . isArray ( ids ) && ids . length ) { const freshIds = json . reduce ( ( r , x ) => r . concat ( x . id ) , [ ] ) ; ids . forEach ( ( id ) => { if ( ! freshIds . includes ( id ) ) dispatch ( loaders . _self ( id , deps ) ) ; } ) ; } ( ( ) => { const depLoaders = Object . entries ( pick ( loaders , deps ) ) ; if ( ! Object . keys ( depLoaders ) . length ) return ; let data = json ; if ( ! Array . isArray ( data ) ) data = [ data ] ; data . forEach ( ( x ) => { depLoaders . forEach ( ( [ dep , loader ] ) => { const id = x [ dep ] ; if ( id === undefined || id === null ) return ; dispatch ( loader ( id , undefined , x ) ) ; } ) ; } ) ; } ) ( ) ; typeof onSuccess === 'function' && onSuccess ( json , dispatch , getState ) ; dispatch ( { type : 'LOADED' } ) ; } , ( err ) => { if ( ! Array . isArray ( err ) ) throw err ; const [ message , response ] = err ; const isUnauthorized = response . status === 401 ; const isExpired = isUnauthorized && message === 'session expired' ; const isBadToken = isUnauthorized && message === 'bad token' ; if ( isExpired || isBadToken ) dispatch ( { type : 'CLEAR_SESSION' } ) ; dispatch ( { type : ` ${ type } ` , storage , meta , isNotFound : response . status === 404 , } ) ; if ( typeof onFailure === 'function' ) { onFailure ( message , dispatch , getState ) ; } dispatch ( messagePush ( message , true , response . status , url ) ) ; dispatch ( { type : 'LOADED' } ) ; } ) ; } ; }
ids is a list of id's of resources that need to be updated, guaranteed
ids is a list of id ' s of resources that need to be updated guaranteed
// TODO: accept '*' deps // this is safe because pick always returns an object // some nasty logical error // LOADED action comes after everything else as to prevent some containers, // like Game, from flashing NotFound for a split second, before showing an // successfully loaded resource (because they utilize some simple heuristics // to find out whether a resource is available or not, which involves // checking isLoading).