PromucFlow_constructor/app/client/src/sagas/ActionSagas.ts

598 lines
17 KiB
TypeScript
Raw Normal View History

2019-10-21 15:12:45 +00:00
import {
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
2019-11-25 05:07:27 +00:00
} from "constants/ReduxActionConstants";
import {
all,
call,
put,
2020-02-18 10:41:52 +00:00
select,
takeEvery,
takeLatest,
} from "redux-saga/effects";
import ActionAPI, { ActionCreateUpdateResponse, Property } from "api/ActionAPI";
2019-10-21 15:12:45 +00:00
import _ from "lodash";
2020-02-03 12:19:10 +00:00
import { AppToaster } from "components/editorComponents/ToastComponent";
2019-11-25 05:07:27 +00:00
import { GenericApiResponse } from "api/ApiResponses";
2020-06-16 10:23:19 +00:00
import PageApi from "api/PageApi";
import { updateCanvasWithDSL } from "sagas/PageSagas";
import {
2020-01-24 09:54:40 +00:00
copyActionError,
copyActionSuccess,
createActionSuccess,
deleteActionSuccess,
fetchActionsForPage,
2020-03-12 20:27:39 +00:00
fetchActionsForPageSuccess,
FetchActionsPayload,
2020-01-24 09:54:40 +00:00
moveActionError,
moveActionSuccess,
SetActionPropertyPayload,
updateAction,
updateActionProperty,
updateActionSuccess,
2019-11-25 05:07:27 +00:00
} from "actions/actionActions";
2019-11-20 10:57:05 +00:00
import {
isDynamicValue,
removeBindingsFromActionObject,
2019-11-25 05:07:27 +00:00
} from "utils/DynamicBindingUtils";
2019-11-13 07:34:59 +00:00
import { validateResponse } from "./ErrorSagas";
2019-12-23 12:12:58 +00:00
import { transformRestAction } from "transformers/RestActionTransformer";
import {
getCurrentApplicationId,
getCurrentPageId,
} from "selectors/editorSelectors";
2020-02-18 10:41:52 +00:00
import { ToastType } from "react-toastify";
2020-03-06 04:59:24 +00:00
import AnalyticsUtil from "utils/AnalyticsUtil";
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
2020-06-25 10:04:57 +00:00
import { Action, RestAction } from "entities/Action";
2020-06-16 10:23:19 +00:00
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,
QUERIES_EDITOR_ID_URL,
API_EDITOR_ID_URL,
} from "constants/routes";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
2019-10-21 15:12:45 +00:00
export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
2019-11-13 07:34:59 +00:00
try {
const response: ActionCreateUpdateResponse = yield ActionAPI.createAPI(
actionPayload.payload,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${actionPayload.payload.name} Action created`,
2020-02-18 10:41:52 +00:00
type: ToastType.SUCCESS,
2019-11-13 07:34:59 +00:00
});
2020-03-06 04:59:24 +00:00
const pageName = yield select(
getCurrentPageNameByActionId,
response.data.id,
);
AnalyticsUtil.logEvent("CREATE_ACTION", {
id: response.data.id,
actionName: response.data.name,
2020-03-06 04:59:24 +00:00
pageName: pageName,
...actionPayload.payload.eventData,
2020-03-06 04:59:24 +00:00
});
2019-11-13 07:34:59 +00:00
yield put(createActionSuccess(response.data));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.CREATE_ACTION_ERROR,
2020-01-24 09:54:40 +00:00
payload: actionPayload.payload,
2019-10-21 15:12:45 +00:00
});
}
}
export function* fetchActionsSaga(action: ReduxAction<FetchActionsPayload>) {
const { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ mode: "EDITOR", appId: applicationId },
);
2019-11-13 07:34:59 +00:00
try {
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActions(
2020-01-24 09:54:40 +00:00
applicationId,
);
2019-11-13 07:34:59 +00:00
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
payload: response.data,
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
);
2019-11-13 07:34:59 +00:00
}
} catch (error) {
2019-10-21 15:12:45 +00:00
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
2019-11-13 07:34:59 +00:00
payload: { error },
2019-10-21 15:12:45 +00:00
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ failed: true },
);
2019-10-21 15:12:45 +00:00
}
}
export function* fetchActionsForViewModeSaga(
action: ReduxAction<FetchActionsPayload>,
) {
const { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ mode: "VIEWER", appId: applicationId },
);
try {
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,
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
);
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
payload: { error },
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_ACTIONS_API,
{ failed: true },
);
}
}
2020-02-21 12:16:49 +00:00
export function* fetchActionsForPageSaga(
action: ReduxAction<{ pageId: string }>,
) {
const { pageId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.FETCH_PAGE_ACTIONS_API,
{ pageId: pageId },
);
2020-02-21 12:16:49 +00:00
try {
const response: GenericApiResponse<RestAction[]> = yield call(
ActionAPI.fetchActionsByPageId,
pageId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put(fetchActionsForPageSuccess(response.data));
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_PAGE_ACTIONS_API,
);
2020-02-21 12:16:49 +00:00
}
} catch (error) {
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.FETCH_PAGE_ACTIONS_API,
{ failed: true },
);
2020-02-21 12:16:49 +00:00
yield put({
type: ReduxActionErrorTypes.FETCH_ACTIONS_FOR_PAGE_ERROR,
payload: { error },
});
}
}
export function* updateActionSaga(actionPayload: ReduxAction<{ id: string }>) {
2019-11-13 07:34:59 +00:00
try {
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.UPDATE_ACTION_API,
{ actionid: actionPayload.payload.id },
);
let action: Action = yield select(getAction, actionPayload.payload.id);
const isApi = action.pluginType === "API";
const isDB = action.pluginType === "DB";
if (isApi) {
action = transformRestAction(action);
2020-06-29 08:23:10 +00:00
}
if (isApi || isDB) {
2020-06-26 11:22:54 +00:00
action = _.omit(action, "name") as RestAction;
}
2020-06-16 10:23:19 +00:00
2019-11-13 07:34:59 +00:00
const response: GenericApiResponse<RestAction> = yield ActionAPI.updateAPI(
2019-12-23 12:12:58 +00:00
action,
2019-11-13 07:34:59 +00:00
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
2020-03-06 04:59:24 +00:00
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,
});
}
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.UPDATE_ACTION_API,
);
2019-11-13 07:34:59 +00:00
yield put(updateActionSuccess({ data: response.data }));
}
} catch (error) {
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.UPDATE_ACTION_API,
{ failed: true },
);
2019-11-13 07:34:59 +00:00
yield put({
type: ReduxActionErrorTypes.UPDATE_ACTION_ERROR,
payload: { error, id: actionPayload.payload.id },
2019-10-21 15:12:45 +00:00
});
}
}
2020-03-06 04:59:24 +00:00
export function* deleteActionSaga(
actionPayload: ReduxAction<{ id: string; name: string }>,
) {
2019-11-13 07:34:59 +00:00
try {
const id = actionPayload.payload.id;
2020-03-06 04:59:24 +00:00
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,
);
2019-11-13 07:34:59 +00:00
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${response.data.name} Action deleted`,
2020-02-18 10:41:52 +00:00
type: ToastType.SUCCESS,
2019-11-13 07:34:59 +00:00
});
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,
});
}
2019-11-13 07:34:59 +00:00
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));
}
2019-11-13 07:34:59 +00:00
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.DELETE_ACTION_ERROR,
2019-12-11 15:14:38 +00:00
payload: { error, id: actionPayload.payload.id },
2019-10-21 15:12:45 +00:00
});
}
}
2020-01-24 09:54:40 +00:00
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);
2020-01-24 09:54:40 +00:00
try {
const response = yield ActionAPI.moveAction({
2020-01-27 13:53:33 +00:00
action: {
...withoutBindings,
pageId: action.payload.originalPageId,
2020-01-27 13:53:33 +00:00
name: action.payload.name,
},
2020-01-24 09:54:40 +00:00
destinationPageId: action.payload.destinationPageId,
});
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({
message: `${response.data.name} Action moved`,
2020-02-18 10:41:52 +00:00
type: ToastType.SUCCESS,
2020-01-24 09:54:40 +00:00
});
}
2020-03-06 04:59:24 +00:00
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("MOVE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
2020-01-27 13:53:33 +00:00
yield put(moveActionSuccess(response.data));
2020-01-24 09:54:40 +00:00
} catch (e) {
AppToaster.show({
message: `Error while moving action ${actionObject.name}`,
2020-02-18 10:41:52 +00:00
type: ToastType.ERROR,
2020-01-24 09:54:40 +00:00
});
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);
2020-01-27 13:53:33 +00:00
if (action.payload.destinationPageId !== actionObject.pageId) {
actionObject = removeBindingsFromActionObject(actionObject);
2020-01-27 13:53:33 +00:00
}
2020-01-24 09:54:40 +00:00
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`,
2020-02-18 10:41:52 +00:00
type: ToastType.SUCCESS,
2020-01-24 09:54:40 +00:00
});
}
2020-03-06 04:59:24 +00:00
const pageName = yield select(getPageNameByPageId, response.data.pageId);
AnalyticsUtil.logEvent("DUPLICATE_API", {
apiName: response.data.name,
pageName: pageName,
apiID: response.data.id,
});
2020-01-24 09:54:40 +00:00
yield put(copyActionSuccess(response.data));
} catch (e) {
AppToaster.show({
message: `Error while copying action ${actionObject.name}`,
2020-02-18 10:41:52 +00:00
type: ToastType.ERROR,
2020-01-24 09:54:40 +00:00
});
yield put(copyActionError(action.payload));
}
}
2020-06-16 10:23:19 +00:00
export function* refactorActionName(
id: string,
pageId: string,
oldName: string,
newName: string,
) {
// fetch page of the action
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.REFACTOR_ACTION_NAME,
{ actionId: id },
);
2020-06-16 10:23:19 +00:00
const pageResponse = yield call(PageApi.fetchPage, {
id: pageId,
2020-06-16 10:23:19 +00:00
});
// 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);
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.REFACTOR_ACTION_NAME,
{ isSuccess: isRefactorSuccessful },
);
2020-06-16 10:23:19 +00:00
if (isRefactorSuccessful) {
2020-06-18 07:46:53 +00:00
yield put({
type: ReduxActionTypes.SAVE_ACTION_NAME_SUCCESS,
2020-06-18 07:46:53 +00:00
payload: {
actionId: id,
},
});
2020-06-16 10:23:19 +00:00
if (currentPageId === pageId) {
yield updateCanvasWithDSL(refactorResponse.data, pageId, layoutId);
} else {
yield put(fetchActionsForPage(pageId));
}
}
}
}
function* saveActionName(action: ReduxAction<{ id: string; name: string }>) {
// Takes from state, checks if the name isValid, saves
2020-06-18 14:16:49 +00:00
const apiId = action.payload.id;
const api = yield select(state =>
state.entities.actions.find(
(action: ActionData) => action.config.id === apiId,
),
);
2020-06-17 13:16:14 +00:00
try {
2020-06-18 07:46:53 +00:00
yield refactorActionName(
api.config.id,
api.config.pageId,
api.config.name,
action.payload.name,
2020-06-17 13:16:14 +00:00
);
2020-06-18 07:46:53 +00:00
} catch (e) {
yield put({
type: ReduxActionErrorTypes.SAVE_ACTION_NAME_ERROR,
2020-06-18 07:46:53 +00:00
payload: {
actionId: action.payload.id,
2020-06-18 14:16:49 +00:00
oldName: api.config.name,
2020-06-18 07:46:53 +00:00
},
});
2020-06-17 13:16:14 +00:00
AppToaster.show({
message: `Unable to update Action name`,
2020-06-17 13:16:14 +00:00
type: ToastType.ERROR,
});
console.error(e);
2020-06-16 10:23:19 +00:00
}
}
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] })),
),
);
if (propertyName === "executeOnLoad") {
yield put({
type: ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT,
payload: {
actionId,
shouldExecute: value,
},
});
return;
}
yield put(updateAction({ id: actionId }));
}
function* toggleActionExecuteOnLoadSaga(
action: ReduxAction<{ actionId: string; shouldExecute: boolean }>,
) {
try {
const response = yield call(
ActionAPI.toggleActionExecuteOnLoad,
action.payload.actionId,
action.payload.shouldExecute,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_SUCCESS,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_ERROR,
payload: error,
});
}
}
function* handleMoveOrCopySaga(actionPayload: ReduxAction<{ id: string }>) {
const { id } = actionPayload.payload;
const action: Action = yield select(getAction, id);
const isApi = action.pluginType === PLUGIN_TYPE_API;
const isQuery = action.pluginType === QUERY_CONSTANT;
const applicationId = yield select(getCurrentApplicationId);
if (isApi) {
history.push(API_EDITOR_ID_URL(applicationId, action.pageId, action.id));
}
if (isQuery) {
history.push(
QUERIES_EDITOR_ID_URL(applicationId, action.pageId, action.id),
);
}
}
2019-10-21 15:12:45 +00:00
export function* watchActionSagas() {
yield all([
takeEvery(ReduxActionTypes.SET_ACTION_PROPERTY, setActionPropertySaga),
2019-10-21 15:12:45 +00:00
takeEvery(ReduxActionTypes.FETCH_ACTIONS_INIT, fetchActionsSaga),
takeEvery(
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_INIT,
fetchActionsForViewModeSaga,
),
2020-04-22 09:15:24 +00:00
takeEvery(ReduxActionTypes.CREATE_ACTION_INIT, createActionSaga),
2020-07-28 10:41:51 +00:00
takeLatest(ReduxActionTypes.UPDATE_ACTION_INIT, updateActionSaga),
takeLatest(ReduxActionTypes.DELETE_ACTION_INIT, deleteActionSaga),
takeLatest(ReduxActionTypes.SAVE_ACTION_NAME_INIT, saveActionName),
2020-01-24 09:54:40 +00:00
takeLatest(ReduxActionTypes.MOVE_ACTION_INIT, moveActionSaga),
takeLatest(ReduxActionTypes.COPY_ACTION_INIT, copyActionSaga),
2020-02-21 12:16:49 +00:00
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),
takeLatest(
ReduxActionTypes.TOGGLE_ACTION_EXECUTE_ON_LOAD_INIT,
toggleActionExecuteOnLoadSaga,
),
2019-10-21 15:12:45 +00:00
]);
}