jmeas/redux-resource

View on GitHub
packages/redux-resource/src/request-statuses-plugin/delete.js

Summary

Maintainability
D
3 days
Test Coverage
import reducerGenerator from '../utils/reducer-generator';
import initialResourceMetaState from '../utils/initial-resource-meta-state';
import requestStatuses from '../utils/request-statuses';
import warning from '../utils/warning';

const del = reducerGenerator('delete', requestStatuses.PENDING);
const delFail = reducerGenerator('delete', requestStatuses.FAILED);
const delIdle = reducerGenerator('delete', requestStatuses.IDLE);

function delSucceed(state, action, { initialResourceMeta }) {
  const resources = action.resources;

  let requestKey, requestName;
  if (action.request && typeof action.request === 'string') {
    requestKey = requestName = action.request;
  }
  if (action.requestKey && typeof action.requestKey === 'string') {
    requestKey = action.requestKey;
  }
  if (action.requestName && typeof action.requestName === 'string') {
    requestName = action.requestName;
  }

  if (process.env.NODE_ENV !== 'production') {
    if (!resources) {
      warning(
        `A 'resources' array was not included in a Redux Resource ` +
          `"success" action with type "${action.type}. Without a 'resources' ` +
          `Array, Redux Resource will not be able to track which resources ` +
          `were affected by this CRUD operation. You should check your Action ` +
          `Creators to make sure that they always include a 'resources' array. ` +
          `For more information, refer to the request action documentation at: ` +
          `https://redux-resource.js.org/docs/requests/request-actions.html`,
        'SUCCESS_NO_RESOURCES'
      );
    } else if (!Array.isArray(resources)) {
      warning(
        `A non-array 'resources' value was passed to a Redux Resource ` +
          `"success" action with type "${
            action.type
          }". 'resources' must be an ` +
          `array. If your backend returned a single object, be sure to wrap it ` +
          `inside of an array. If you're using the Redux Resource XHR ` +
          `library, you can do this using the "transformData" option. ` +
          `For more information, refer to the request action documentation at: ` +
          `https://redux-resource.js.org/docs/requests/request-actions.html`,
        'NON_ARRAY_RESOURCES'
      );
    }

    if (action.list) {
      warning(
        `You included a "list" in a delete action. You don't need to do this, ` +
          `because successful deletes remove the deleted resources from all lists. ` +
          `For more information, refer to the documentation on deleting resources at: ` +
          `https://redux-resource.js.org/docs/requests/deleting-resources.html`,
        'DELETE_LISTS'
      );
    }
  }

  // Find the list of IDs affected by this action
  let idList;
  if (resources && resources.map) {
    idList = resources.map(r => {
      if (typeof r === 'object') {
        if (process.env.NODE_ENV !== 'production') {
          if (
            (!r.id && r.id !== 0) ||
            (typeof r.id !== 'string' && typeof r.id !== 'number')
          ) {
            warning(
              `A resource without an ID was passed to an action with type ` +
                `${
                  action.type
                }. Every resource must have an ID that is either ` +
                `a number of a string. You should check your action creators to ` +
                `make sure that an ID is always included in your resources. ` +
                `For more information, refer to the documentation on resource objects at: ` +
                `https://redux-resource.js.org/docs/resources/resource-objects.html`,
              'NO_RESOURCE_ID'
            );
          }
        }
        return r.id;
      } else {
        if (process.env.NODE_ENV !== 'production') {
          if (typeof r !== 'string' && typeof r !== 'number') {
            warning(
              `A resource without an ID was passed to an action with type ` +
                `${
                  action.type
                }. Every resource must have an ID that is either ` +
                `a number of a string. You should check your action creators to ` +
                `make sure that an ID is always included in your resources. ` +
                `For more information, refer to the documentation on resource objects at: ` +
                `https://redux-resource.js.org/docs/resources/resource-objects.html`,
              'NO_RESOURCE_ID'
            );
          }
        }
        return r;
      }
    });
  }

  const hasIds = idList && idList.length;

  // If we have no IDs nor requestKey, then there is nothing to update
  if (!hasIds && !requestKey) {
    return state;
  }

  let newMeta;
  let newLists = {};
  let newRequests = {};
  const meta = state.meta;
  const requests = state.requests;
  const lists = state.lists;

  if (requestKey) {
    const existingRequest = requests[requestKey] || {};
    const newRequest = {
      ...existingRequest,
      ...action.requestProperties,
      requestKey,
      resourceType: action.resourceType || action.resourceName,
      status: requestStatuses.SUCCEEDED,
      ids: idList || [],
    };

    if (requestName) {
      newRequest.requestName = requestName;
    }
    newRequests = {
      ...requests,
      [requestKey]: newRequest,
    };
  } else {
    newRequests = requests;
  }

  for (let resourceList in lists) {
    const existingList = lists[resourceList];

    let newList;
    if (hasIds && existingList) {
      newList = existingList.filter(r => !idList.includes(r));
    } else if (existingList) {
      newList = existingList;
    }

    newLists[resourceList] = newList;
  }

  if (hasIds) {
    const nullMeta = idList.reduce((memo, id) => {
      memo[id] = {
        ...initialResourceMetaState,
        ...initialResourceMeta,
        deleteStatus: requestStatuses.SUCCEEDED,
      };
      return memo;
    }, {});

    newMeta = {
      ...meta,
      ...nullMeta,
    };
  } else {
    newMeta = meta;
  }

  // Shallow clone the existing resource object, nulling any deleted resources
  let newResources = Object.assign({}, state.resources);
  if (hasIds) {
    idList.forEach(id => {
      delete newResources[id];
    });
  }

  return {
    ...state,
    meta: newMeta,
    lists: newLists,
    requests: newRequests,
    resources: newResources,
  };
}

export { del, delFail, delIdle, delSucceed };