Home Reference Source

src/sagas/actionsImpl.js

import { call, put, select, throttle } from 'redux-saga/effects';
import { getFormValues, initialize } from 'redux-form';
import pathToRegexp from 'path-to-regexp';
import { isFunction } from 'lodash';
import merge from 'deepmerge';

import {
  START_INVOKE,
  SUCCESS_INVOKE,
  FAIL_INVOKE,
} from '../constants/actionImpls';
import { CALL_ACTION_IMPL } from '../constants/toolbar';

import createActionHelper from '../actions/createActionHelper';
import { makeWidgetValidationSelector } from '../selectors/widgets';
import { getModelSelector } from '../selectors/models';

import { validateField } from '../core/validation/createValidator';
import factoryResolver from '../utils/factoryResolver';
import fetchSaga from './fetch.js';
import { FETCH_INVOKE_DATA } from '../core/api.js';
import { getParams } from '../utils/compileUrl';
import { setModel } from '../actions/models';
import { PREFIXES } from '../constants/models';
import { disablePage, enablePage } from '../actions/pages';
import { disableWidgetOnFetch, enableWidget } from '../actions/widgets';

export function* validate(options) {
  const isTouched = true;
  const state = yield select();
  const validationConfig = yield select(
    makeWidgetValidationSelector(options.validatedWidgetId)
  );
  const values = (yield select(getFormValues(options.validatedWidgetId))) || {};
  const notValid =
    options.validate &&
    (yield call(
      validateField(
        validationConfig,
        options.validatedWidgetId,
        state,
        isTouched
      ),
      values,
      options.dispatch
    ));
  return notValid;
}

/**
 * вызов экшена
 */
export function* handleAction(action) {
  const { options, actionSrc } = action.payload;
  try {
    let actionFunc;
    if (isFunction(actionSrc)) {
      actionFunc = actionSrc;
    } else {
      actionFunc = factoryResolver(actionSrc, null, 'function');
    }
    const state = yield select();
    const notValid = yield validate(options);
    if (notValid) {
      console.log(`Форма ${options.validatedWidgetId} не прошла валидацию.`);
    } else {
      yield actionFunc &&
        call(actionFunc, {
          ...options,
          state,
        });
    }
  } catch (err) {
    console.error(err);
  }
}

export function* resolveMapping(dataProvider, state) {
  const pathParams = yield call(getParams, dataProvider.pathMapping, state);
  return pathToRegexp.compile(dataProvider.url)(pathParams);
}

/**
 * Отправка запроса
 * @param dataProvider
 * @param model
 * @returns {IterableIterator<*>}
 */
export function* fetchInvoke(dataProvider, model) {
  const state = yield select();
  const path = yield resolveMapping(dataProvider, state);
  const response = yield call(fetchSaga, FETCH_INVOKE_DATA, {
    basePath: path,
    baseQuery: {},
    baseMethod: dataProvider.method,
    model,
  });
  return response;
}

export function* handleFailInvoke(metaInvokeFail, widgetId, metaResponse) {
  const meta = merge(metaInvokeFail, metaResponse);
  yield put(createActionHelper(FAIL_INVOKE)({ widgetId }, meta));
}

/**
 * вызов экшена
 */
export function* handleInvoke(action) {
  const { modelLink, widgetId, pageId, dataProvider, data } = action.payload;
  try {
    if (!dataProvider) {
      throw new Error('dataProvider is undefined');
    }
    if (pageId) {
      yield put(disablePage(pageId));
    }
    if (widgetId) {
      yield put(disableWidgetOnFetch(widgetId));
    }
    let model = data || {};
    if (modelLink) {
      model = yield select(getModelSelector(modelLink));
    }
    const response = yield call(fetchInvoke, dataProvider, model);

    const meta = merge(action.meta.success || {}, response.meta || {});
    if (!meta.redirect && !meta.closeLastModal) {
      yield put(setModel(PREFIXES.resolve, widgetId, response.data));
    }
    yield put(
      createActionHelper(SUCCESS_INVOKE)(
        { widgetId },
        {
          ...meta,
          withoutSelectedId: true,
        }
      )
    );
  } catch (err) {
    console.error(err);
    yield* handleFailInvoke(
      action.meta.fail || {},
      widgetId,
      err.json && err.json.meta ? err.json.meta : {}
    );
  } finally {
    if (pageId) {
      yield put(enablePage(pageId));
    }
    if (widgetId) {
      yield put(enableWidget(widgetId));
    }
  }
}

export const actionsImplSagas = [
  throttle(500, CALL_ACTION_IMPL, handleAction),
  throttle(500, START_INVOKE, handleInvoke),
];