PromucFlow_constructor/app/client/src/sagas/ActionSagas.ts
Hetu Nandu fe43680abc
Action test fixes (#130)
* Fixes for tests
* remove marketplace tests
* fix some other tests
* Simplify update datasource
* Add cypress env variables
2020-07-21 19:31:51 +05:30

499 lines
14 KiB
TypeScript

import {
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
} from "constants/ReduxActionConstants";
import {
all,
call,
put,
select,
takeEvery,
takeLatest,
debounce,
} from "redux-saga/effects";
import ActionAPI, { ActionCreateUpdateResponse, Property } from "api/ActionAPI";
import _ from "lodash";
import { AppToaster } from "components/editorComponents/ToastComponent";
import { GenericApiResponse } from "api/ApiResponses";
import PageApi from "api/PageApi";
import { updateCanvasWithDSL } from "sagas/PageSagas";
import {
copyActionError,
copyActionSuccess,
createActionSuccess,
deleteActionSuccess,
fetchActionsForPage,
fetchActionsForPageSuccess,
FetchActionsPayload,
moveActionError,
moveActionSuccess,
SetActionPropertyPayload,
updateAction,
updateActionProperty,
updateActionSuccess,
} from "actions/actionActions";
import {
isDynamicValue,
removeBindingsFromActionObject,
} from "utils/DynamicBindingUtils";
import { validateResponse } from "./ErrorSagas";
import { transformRestAction } from "transformers/RestActionTransformer";
import {
getCurrentApplicationId,
getCurrentPageId,
} from "selectors/editorSelectors";
import { ToastType } from "react-toastify";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
import { Action, RestAction } from "entities/Action";
import { ActionData } from "reducers/entityReducers/actionsReducer";
import {
getAction,
getCurrentPageNameByActionId,
getPageNameByPageId,
} from "selectors/entitiesSelector";
import { PLUGIN_TYPE_API } from "constants/ApiEditorConstants";
import history from "utils/history";
import { API_EDITOR_URL, QUERIES_EDITOR_URL } from "constants/routes";
import { changeApi } from "actions/apiPaneActions";
import { changeQuery } from "actions/queryPaneActions";
export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
try {
const response: ActionCreateUpdateResponse = yield ActionAPI.createAPI(
actionPayload.payload,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${actionPayload.payload.name} Action created`,
type: ToastType.SUCCESS,
});
const pageName = yield select(
getCurrentPageNameByActionId,
response.data.id,
);
AnalyticsUtil.logEvent("CREATE_API", {
apiId: response.data.id,
apiName: response.data.name,
pageName: pageName,
});
yield put(createActionSuccess(response.data));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.CREATE_ACTION_ERROR,
payload: actionPayload.payload,
});
}
}
export function* fetchActionsSaga(action: ReduxAction<FetchActionsPayload>) {
try {
const { applicationId } = action.payload;
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActions(
applicationId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
payload: response.data,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
payload: { error },
});
}
}
export function* fetchActionsForViewModeSaga(
action: ReduxAction<FetchActionsPayload>,
) {
try {
const { applicationId } = action.payload;
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActionsForViewMode(
applicationId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
payload: response.data,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
payload: { error },
});
}
}
export function* fetchActionsForPageSaga(
action: ReduxAction<{ pageId: string }>,
) {
try {
const { pageId } = action.payload;
const response: GenericApiResponse<RestAction[]> = yield call(
ActionAPI.fetchActionsByPageId,
pageId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put(fetchActionsForPageSuccess(response.data));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_FOR_PAGE_ERROR,
payload: { error },
});
}
}
export function* updateActionSaga(actionPayload: ReduxAction<{ id: string }>) {
try {
let action: Action = yield select(getAction, actionPayload.payload.id);
const isApi = action.pluginType === "API";
const isDB = action.pluginType === "DB";
if (isApi) {
action = transformRestAction(action);
}
if (isApi || isDB) {
action = _.omit(action, "name") as RestAction;
}
const response: GenericApiResponse<RestAction> = yield ActionAPI.updateAPI(
action,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const pageName = yield select(
getCurrentPageNameByActionId,
response.data.id,
);
if (action.pluginType === QUERY_CONSTANT) {
AnalyticsUtil.logEvent("SAVE_QUERY", {
queryName: action.name,
pageName,
});
} else if (action.pluginType === PLUGIN_TYPE_API) {
AnalyticsUtil.logEvent("SAVE_API", {
apiId: response.data.id,
apiName: response.data.name,
pageName: pageName,
});
}
yield put(updateActionSuccess({ data: response.data }));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.UPDATE_ACTION_ERROR,
payload: { error, id: actionPayload.payload.id },
});
}
}
export function* deleteActionSaga(
actionPayload: ReduxAction<{ id: string; name: string }>,
) {
try {
const id = actionPayload.payload.id;
const name = actionPayload.payload.name;
const action = yield select(getAction, id);
const isApi = action.pluginType === PLUGIN_TYPE_API;
const isQuery = action.pluginType === QUERY_CONSTANT;
const response: GenericApiResponse<RestAction> = yield ActionAPI.deleteAction(
id,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${response.data.name} Action deleted`,
type: ToastType.SUCCESS,
});
if (isApi) {
const pageName = yield select(getCurrentPageNameByActionId, id);
AnalyticsUtil.logEvent("DELETE_API", {
apiName: name,
pageName,
apiID: id,
});
}
if (isQuery) {
AnalyticsUtil.logEvent("DELETE_QUERY", {
queryName: action.name,
});
}
yield put(deleteActionSuccess({ id }));
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
if (isApi) {
history.push(API_EDITOR_URL(applicationId, pageId));
}
if (isQuery) {
history.push(QUERIES_EDITOR_URL(applicationId, pageId));
}
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.DELETE_ACTION_ERROR,
payload: { error, id: actionPayload.payload.id },
});
}
}
function* moveActionSaga(
action: ReduxAction<{
id: string;
destinationPageId: string;
originalPageId: string;
name: string;
}>,
) {
const actionObject: RestAction = yield select(getAction, action.payload.id);
const withoutBindings = removeBindingsFromActionObject(actionObject);
try {
const response = yield ActionAPI.moveAction({
action: {
...withoutBindings,
pageId: action.payload.originalPageId,
name: action.payload.name,
},
destinationPageId: action.payload.destinationPageId,
});
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${response.data.name} Action moved`,
type: ToastType.SUCCESS,
});
}
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("MOVE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
yield put(moveActionSuccess(response.data));
} catch (e) {
AppToaster.show({
message: `Error while moving action ${actionObject.name}`,
type: ToastType.ERROR,
});
yield put(
moveActionError({
id: action.payload.id,
originalPageId: action.payload.originalPageId,
}),
);
}
}
function* copyActionSaga(
action: ReduxAction<{ id: string; destinationPageId: string; name: string }>,
) {
let actionObject: RestAction = yield select(getAction, action.payload.id);
if (action.payload.destinationPageId !== actionObject.pageId) {
actionObject = removeBindingsFromActionObject(actionObject);
}
try {
const copyAction = {
...(_.omit(actionObject, "id") as RestAction),
name: action.payload.name,
pageId: action.payload.destinationPageId,
};
const response = yield ActionAPI.createAPI(copyAction);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${actionObject.name} Action copied`,
type: ToastType.SUCCESS,
});
}
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("DUPLICATE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
yield put(copyActionSuccess(response.data));
} catch (e) {
AppToaster.show({
message: `Error while copying action ${actionObject.name}`,
type: ToastType.ERROR,
});
yield put(copyActionError(action.payload));
}
}
export function* refactorActionName(
id: string,
pageId: string,
oldName: string,
newName: string,
) {
// fetch page of the action
const pageResponse = yield call(PageApi.fetchPage, {
pageId: pageId,
});
// check if page request is successful
const isPageRequestSuccessful = yield validateResponse(pageResponse);
if (isPageRequestSuccessful) {
// get the layoutId from the page response
const layoutId = pageResponse.data.layouts[0].id;
// call to refactor action
const refactorResponse = yield ActionAPI.updateActionName({
layoutId,
pageId: pageId,
oldName: oldName,
newName: newName,
});
const isRefactorSuccessful = yield validateResponse(refactorResponse);
const currentPageId = yield select(getCurrentPageId);
if (isRefactorSuccessful) {
yield put({
type: ReduxActionTypes.SAVE_API_NAME_SUCCESS,
payload: {
actionId: id,
},
});
if (currentPageId === pageId) {
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);
} else {
yield put(fetchActionsForPage(pageId));
}
}
}
}
function* saveApiNameSaga(action: ReduxAction<{ id: string; name: string }>) {
// Takes from state, checks if the name isValid, saves
const apiId = action.payload.id;
const api = yield select(state =>
state.entities.actions.find(
(action: ActionData) => action.config.id === apiId,
),
);
try {
yield refactorActionName(
api.config.id,
api.config.pageId,
api.config.name,
action.payload.name,
);
} catch (e) {
yield put({
type: ReduxActionErrorTypes.SAVE_API_NAME_ERROR,
payload: {
actionId: action.payload.id,
oldName: api.config.name,
},
});
AppToaster.show({
message: `Unable to update API name`,
type: ToastType.ERROR,
});
console.error(e);
}
}
function getDynamicBindingsChangesSaga(
action: Action,
value: string,
field: string,
) {
const bindingField = field.replace("actionConfiguration.", "");
const isDynamic = isDynamicValue(value);
let dynamicBindings: Property[] = action.dynamicBindingPathList || [];
const fieldExists = _.some(dynamicBindings, { key: bindingField });
if (!isDynamic && fieldExists) {
dynamicBindings = dynamicBindings.filter(d => d.key !== bindingField);
}
if (isDynamic && !fieldExists) {
dynamicBindings.push({ key: bindingField });
}
if (dynamicBindings !== action.dynamicBindingPathList) {
return dynamicBindings;
}
return action.dynamicBindingPathList;
}
function* setActionPropertySaga(action: ReduxAction<SetActionPropertyPayload>) {
const { actionId, value, propertyName } = action.payload;
if (!actionId) return;
if (propertyName === "name") return;
const actionObj = yield select(getAction, actionId);
const effects: Record<string, any> = {};
// Value change effect
effects[propertyName] = value;
// Bindings change effect
effects.dynamicBindingPathList = getDynamicBindingsChangesSaga(
actionObj,
value,
propertyName,
);
yield all(
Object.keys(effects).map(field =>
put(updateActionProperty({ id: actionId, field, value: effects[field] })),
),
);
yield put(updateAction({ id: actionId }));
}
function* handleMoveOrCopySaga(actionPayload: ReduxAction<{ id: string }>) {
const { id } = actionPayload.payload;
const action = yield select(getAction, id);
const isApi = action.pluginType === PLUGIN_TYPE_API;
const isQuery = action.pluginType === QUERY_CONSTANT;
if (isApi) {
yield put(changeApi(id));
}
if (isQuery) {
yield put(changeQuery(id));
}
}
export function* watchActionSagas() {
yield all([
takeEvery(ReduxActionTypes.SET_ACTION_PROPERTY, setActionPropertySaga),
takeEvery(ReduxActionTypes.FETCH_ACTIONS_INIT, fetchActionsSaga),
takeEvery(
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_INIT,
fetchActionsForViewModeSaga,
),
takeEvery(ReduxActionTypes.CREATE_ACTION_INIT, createActionSaga),
debounce(500, ReduxActionTypes.UPDATE_ACTION_INIT, updateActionSaga),
takeLatest(ReduxActionTypes.DELETE_ACTION_INIT, deleteActionSaga),
takeLatest(ReduxActionTypes.SAVE_API_NAME, saveApiNameSaga),
takeLatest(ReduxActionTypes.MOVE_ACTION_INIT, moveActionSaga),
takeLatest(ReduxActionTypes.COPY_ACTION_INIT, copyActionSaga),
takeLatest(
ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_INIT,
fetchActionsForPageSaga,
),
takeEvery(ReduxActionTypes.MOVE_ACTION_SUCCESS, handleMoveOrCopySaga),
takeEvery(ReduxActionTypes.COPY_ACTION_SUCCESS, handleMoveOrCopySaga),
takeEvery(ReduxActionErrorTypes.MOVE_ACTION_ERROR, handleMoveOrCopySaga),
takeEvery(ReduxActionErrorTypes.COPY_ACTION_ERROR, handleMoveOrCopySaga),
]);
}