PromucFlow_constructor/app/client/src/sagas/InitSagas.ts
Ankita Kinger fa45282b0a
fix: alignment of the image seen on no search results on application page (#13950)
* fix: 12861 trim unnecessary re-render in select widget in server side rendering (#12865)

* add debounce to search and remove state var

* increase debounce time

* fix: updated the condition to show expiry key (#13092)

* Add index for git (#13133)

* fix: Fixed compile time errors

* fix: Unable to see apps in home page after git connect fails (#13387)

* Fix Redis installation in Dockerfile (#13428) (#13430)

Conflicts:
	Dockerfile

* fix: only execute pageload actions after successfully fetching actions and jscollections in view mode (#13521)

* prevent execution of onPageLoad actions before sucessful actions fetch

* Perform url update before fetching actions and jscollections

(cherry picked from commit 7261834fe5)

* fix: NPE check when datasource createdAt is null

(cherry picked from commit beafb371b24180dce2e351f1dea6d9d34db2a204)

* fixed image alignment for no search results on applications page

* deleted duplicate file

* reload to Refresh script update

* Adding sleep - script fix

Co-authored-by: Preet Sidhu <preetsidhu.bits@gmail.com>
Co-authored-by: Aman Agarwal <aman@appsmith.com>
Co-authored-by: Abhijeet <41686026+abhvsn@users.noreply.github.com>
Co-authored-by: Nidhi <nidhi.nair93@gmail.com>
Co-authored-by: Somangshu Goswami <somangshu.goswami1508@gmail.com>
Co-authored-by: Rimil Dey <rimildeyjsr@gmail.com>
Co-authored-by: Anagh Hegde <anagh@appsmith.com>
Co-authored-by: Shrikant Sharat Kandula <shrikant@appsmith.com>
Co-authored-by: Arpit Mohan <mohanarpit@users.noreply.github.com>
Co-authored-by: Favour Ohanekwu <fohanekwu@gmail.com>
Co-authored-by: Trisha Anand <trisha@appsmith.com>
Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
2022-05-26 10:05:35 +05:30

534 lines
15 KiB
TypeScript

import { get } from "lodash";
import {
all,
call,
put,
race,
select,
take,
takeLatest,
} from "redux-saga/effects";
import {
ApplicationPayload,
Page,
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
ReduxActionWithoutPayload,
} from "@appsmith/constants/ReduxActionConstants";
import { ERROR_CODES } from "@appsmith/constants/ApiConstants";
import {
fetchPage,
fetchPublishedPage,
fetchPublishedPageSuccess,
resetApplicationWidgets,
resetPageList,
setAppMode,
updateAppPersistentStore,
} from "actions/pageActions";
import {
fetchDatasources,
fetchMockDatasources,
} from "actions/datasourceActions";
import { fetchPluginFormConfigs, fetchPlugins } from "actions/pluginActions";
import { fetchJSCollections } from "actions/jsActionActions";
import {
executePageLoadActions,
fetchActions,
fetchActionsForView,
} from "actions/pluginActionActions";
import {
ApplicationVersion,
fetchApplication,
resetCurrentApplication,
} from "actions/applicationActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { getCurrentApplication } from "selectors/applicationSelectors";
import { APP_MODE } from "entities/App";
import { getPersistentAppStore } from "constants/AppConstants";
import { getDefaultPageId } from "./selectors";
import { populatePageDSLsSaga } from "./PageSagas";
import log from "loglevel";
import * as Sentry from "@sentry/react";
import {
resetRecentEntities,
restoreRecentEntitiesRequest,
} from "actions/globalSearchActions";
import {
InitializeEditorPayload,
resetEditorSuccess,
} from "actions/initActions";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
import {
getCurrentApplicationId,
getIsEditorInitialized,
getPageById,
} from "selectors/editorSelectors";
import { getIsInitialized as getIsViewerInitialized } from "selectors/appViewSelectors";
import { fetchCommentThreadsInit } from "actions/commentActions";
import { fetchJSCollectionsForView } from "actions/jsActionActions";
import {
addBranchParam,
PLACEHOLDER_APP_SLUG,
PLACEHOLDER_PAGE_SLUG,
} from "constants/routes";
import history from "utils/history";
import {
fetchGitStatusInit,
remoteUrlInputValue,
resetPullMergeStatus,
updateBranchLocally,
} from "actions/gitSyncActions";
import { getCurrentGitBranch } from "selectors/gitSyncSelectors";
import { isURLDeprecated, getUpdatedRoute } from "utils/helpers";
import { fillPathname, viewerURL, builderURL } from "RouteBuilder";
import { enableGuidedTour } from "actions/onboardingActions";
import { setPreviewModeAction } from "actions/editorActions";
import {
fetchSelectedAppThemeAction,
fetchAppThemesAction,
} from "actions/appThemingActions";
export function* failFastApiCalls(
triggerActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
successActions: string[],
failureActions: string[],
) {
const triggerEffects = [];
for (const triggerAction of triggerActions) {
triggerEffects.push(put(triggerAction));
}
const successEffects = [];
for (const successAction of successActions) {
successEffects.push(take(successAction));
}
yield all(triggerEffects);
const effectRaceResult = yield race({
success: all(successEffects),
failure: take(failureActions),
});
if (effectRaceResult.failure) {
yield put({
type: ReduxActionTypes.SAFE_CRASH_APPSMITH_REQUEST,
payload: {
code: get(
effectRaceResult,
"failure.payload.error.code",
ERROR_CODES.SERVER_ERROR,
),
},
});
return false;
}
return true;
}
/**
* this saga is called once then application is loaded.
* It will hold the editor in uninitialized till all the apis/actions are completed
*
* @param initializeEditorAction
* @returns
*/
function* bootstrapEditor(payload: InitializeEditorPayload) {
const { branch } = payload;
yield put(resetEditorSuccess());
yield put(updateBranchLocally(branch || ""));
yield put(setAppMode(APP_MODE.EDIT));
yield put({ type: ReduxActionTypes.START_EVALUATION });
}
function* initiateURLUpdate(
pageId: string,
appMode: APP_MODE,
pageIdInUrl?: string,
) {
try {
const currentApplication: ApplicationPayload = yield select(
getCurrentApplication,
);
const applicationSlug = currentApplication.slug || PLACEHOLDER_APP_SLUG;
const currentPage: Page = yield select(getPageById(pageId));
const pageSlug = currentPage?.slug || PLACEHOLDER_PAGE_SLUG;
let originalUrl = "";
const { hash, pathname, search } = window.location;
// For switching new URLs to old.
if (currentApplication.applicationVersion < ApplicationVersion.SLUG_URL) {
if (!isURLDeprecated(pathname)) {
// We do not allow downgrading application version but,
// when switch from a branch with updated URL to another one with legacy URLs,
// we need to compute the legacy url
// This scenario can happen only in edit mode.
originalUrl =
builderURL({
applicationId: currentApplication.id,
pageId: pageId,
}) + hash;
history.replace(originalUrl);
}
} else {
// For updated apps,
// Check if the the current route is a deprecated URL or if pageId is missing (bookmarked urls) and
// generate a new route with the v2 structure.
if (isURLDeprecated(pathname) || !pageIdInUrl) {
if (appMode === APP_MODE.EDIT) {
// If edit mode, replace /applications/appId/pages/pageId with /appSlug/pageSlug-pageId,
// to not affect the rest of the url. eg. /api/apiId
originalUrl =
fillPathname(pathname, currentApplication, currentPage) +
search +
hash;
} else {
// View Mode - generate a new viewer URL - auto updates query params
originalUrl = viewerURL({ applicationSlug, pageSlug, pageId }) + hash;
}
} else {
// For urls which has pageId in it,
// replace the placeholder values of application slug and page slug with real slug names.
originalUrl =
getUpdatedRoute(pathname, {
applicationSlug,
pageSlug,
pageId,
}) +
search +
hash;
}
history.replace(originalUrl);
}
} catch (e) {
log.error(e);
}
}
function* initiateEditorApplicationAndPages(payload: InitializeEditorPayload) {
const pageId = payload.pageId;
const applicationId = payload.applicationId;
const applicationCall: boolean = yield failFastApiCalls(
[fetchApplication({ pageId, applicationId, mode: APP_MODE.EDIT })],
[
ReduxActionTypes.FETCH_APPLICATION_SUCCESS,
ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
],
);
if (!applicationCall) return;
let toLoadPageId = pageId;
const defaultPageId: string = yield select(getDefaultPageId);
toLoadPageId = toLoadPageId || defaultPageId;
yield call(initiateURLUpdate, toLoadPageId, APP_MODE.EDIT, payload.pageId);
const fetchPageCallResult: boolean = yield failFastApiCalls(
[fetchPage(toLoadPageId, true)],
[ReduxActionTypes.FETCH_PAGE_SUCCESS],
[ReduxActionErrorTypes.FETCH_PAGE_ERROR],
);
if (!fetchPageCallResult) return;
return toLoadPageId;
}
function* initiateEditorActions(applicationId: string) {
const initActionsCalls = [
fetchActions({ applicationId }, []),
fetchJSCollections({ applicationId }),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
];
const successActionEffects = [
ReduxActionTypes.FETCH_JS_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
];
const failureActionEffects = [
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
];
const allActionCalls: boolean = yield failFastApiCalls(
initActionsCalls,
successActionEffects,
failureActionEffects,
);
if (!allActionCalls) {
return;
} else {
yield put({
type: ReduxActionTypes.FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS,
});
yield put(executePageLoadActions());
}
}
function* initiatePluginsAndDatasources() {
const pluginsAndDatasourcesCalls: boolean = yield failFastApiCalls(
[fetchPlugins(), fetchDatasources(), fetchMockDatasources()],
[
ReduxActionTypes.FETCH_PLUGINS_SUCCESS,
ReduxActionTypes.FETCH_DATASOURCES_SUCCESS,
ReduxActionTypes.FETCH_MOCK_DATASOURCES_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_PLUGINS_ERROR,
ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR,
ReduxActionErrorTypes.FETCH_MOCK_DATASOURCES_ERROR,
],
);
if (!pluginsAndDatasourcesCalls) return;
const pluginFormCall: boolean = yield failFastApiCalls(
[fetchPluginFormConfigs()],
[ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS],
[ReduxActionErrorTypes.FETCH_PLUGIN_FORM_CONFIGS_ERROR],
);
if (!pluginFormCall) return;
}
function* initiateGit(applicationId: string) {
const branchInStore: string = yield select(getCurrentGitBranch);
yield put(
restoreRecentEntitiesRequest({
applicationId,
branch: branchInStore,
}),
);
// init of temporay remote url from old application
yield put(remoteUrlInputValue({ tempRemoteUrl: "" }));
// add branch query to path and fetch status
if (branchInStore) {
history.replace(addBranchParam(branchInStore));
yield put(fetchGitStatusInit());
}
yield put(resetPullMergeStatus());
}
function* initializeEditorSaga(
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
) {
try {
const { payload } = initializeEditorAction;
const { branch } = payload;
yield call(bootstrapEditor, payload);
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.INIT_EDIT_APP,
);
yield call(initiateEditorApplicationAndPages, payload);
const { id: applicationId, name }: ApplicationPayload = yield select(
getCurrentApplication,
);
yield put(
updateAppPersistentStore(getPersistentAppStore(applicationId, branch)),
);
yield all([
call(initiateEditorActions, applicationId),
call(initiatePluginsAndDatasources),
call(populatePageDSLsSaga),
]);
AnalyticsUtil.logEvent("EDITOR_OPEN", {
appId: applicationId,
appName: name,
});
yield call(initiateGit, applicationId);
yield put(fetchCommentThreadsInit());
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.INIT_EDIT_APP,
);
} catch (e) {
log.error(e);
Sentry.captureException(e);
yield put({
type: ReduxActionTypes.SAFE_CRASH_APPSMITH_REQUEST,
payload: {
code: ERROR_CODES.SERVER_ERROR,
},
});
return;
}
}
export function* initializeAppViewerSaga(
action: ReduxAction<{
branch: string;
pageId: string;
applicationId: string;
}>,
) {
const { branch, pageId } = action.payload;
let { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.INIT_VIEW_APP,
);
yield put(updateBranchLocally(branch || ""));
yield put(setAppMode(APP_MODE.PUBLISHED));
const applicationCall: boolean = yield failFastApiCalls(
[fetchApplication({ applicationId, pageId, mode: APP_MODE.PUBLISHED })],
[
ReduxActionTypes.FETCH_APPLICATION_SUCCESS,
ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
],
);
if (!applicationCall) return;
applicationId = applicationId || (yield select(getCurrentApplicationId));
yield put(
updateAppPersistentStore(getPersistentAppStore(applicationId, branch)),
);
yield put({ type: ReduxActionTypes.START_EVALUATION });
const defaultPageId: string = yield select(getDefaultPageId);
const toLoadPageId: string = pageId || defaultPageId;
yield call(initiateURLUpdate, toLoadPageId, APP_MODE.PUBLISHED, pageId);
const resultOfPrimaryCalls: boolean = yield failFastApiCalls(
[
fetchActionsForView({ applicationId }),
fetchJSCollectionsForView({ applicationId }),
fetchPublishedPage(toLoadPageId, true, true),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
],
[
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR,
],
);
if (!resultOfPrimaryCalls) return;
//Delay page load actions till all actions are retrieved.
yield put(fetchPublishedPageSuccess([executePageLoadActions()]));
if (toLoadPageId) {
yield put(fetchPublishedPage(toLoadPageId, true));
const resultOfFetchPage: {
success: boolean;
failure: boolean;
} = yield race({
success: take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS),
failure: take(ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR),
});
if (resultOfFetchPage.failure) {
yield put({
type: ReduxActionTypes.SAFE_CRASH_APPSMITH_REQUEST,
payload: {
code: get(
resultOfFetchPage,
"failure.payload.error.code",
ERROR_CODES.SERVER_ERROR,
),
},
});
return;
}
}
yield put(fetchCommentThreadsInit());
yield put({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
});
PerformanceTracker.stopAsyncTracking(
PerformanceTransactionName.INIT_VIEW_APP,
);
if ("serviceWorker" in navigator) {
yield put({
type: ReduxActionTypes.FETCH_ALL_PUBLISHED_PAGES,
});
}
}
function* resetEditorSaga() {
yield put(resetCurrentApplication());
yield put(resetPageList());
yield put(resetApplicationWidgets());
yield put(resetRecentEntities());
// End guided tour once user exits editor
yield put(enableGuidedTour(false));
// Reset to edit mode once user exits editor
// Without doing this if the user creates a new app they
// might end up in preview mode if they were in preview mode
// previously
yield put(setPreviewModeAction(false));
yield put(resetEditorSuccess());
}
export function* waitForInit() {
const isEditorInitialised: boolean = yield select(getIsEditorInitialized);
const isViewerInitialized: boolean = yield select(getIsViewerInitialized);
if (!isEditorInitialised && !isViewerInitialized) {
yield take([
ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
]);
}
}
export default function* watchInitSagas() {
yield all([
takeLatest(ReduxActionTypes.INITIALIZE_EDITOR, initializeEditorSaga),
takeLatest(
ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
initializeAppViewerSaga,
),
takeLatest(ReduxActionTypes.RESET_EDITOR_REQUEST, resetEditorSaga),
]);
}