Improve app initialisation for timeouts (#1412)
Fixes: #1510 Co-authored-by: Arpit Mohan <mohanarpit@users.noreply.github.com>
This commit is contained in:
parent
8ae77f7764
commit
20ef86f118
|
|
@ -1,5 +1,4 @@
|
|||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { EditorModes } from "../components/editorComponents/CodeEditor/EditorConfig";
|
||||
import { APP_MODE } from "../reducers/entityReducers/appReducer";
|
||||
import { UpdateApplicationPayload } from "api/ApplicationApi";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
ReduxActionTypes,
|
||||
ReduxAction,
|
||||
InitializeEditorPayload,
|
||||
ReduxActionErrorTypes,
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
export const initEditor = (
|
||||
|
|
@ -14,3 +15,17 @@ export const initEditor = (
|
|||
pageId,
|
||||
},
|
||||
});
|
||||
|
||||
export const initEditorError = (): ReduxAction<{ show: false }> => ({
|
||||
type: ReduxActionErrorTypes.INITIALIZE_EDITOR_ERROR,
|
||||
payload: {
|
||||
show: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const initViewerError = (): ReduxAction<{ show: false }> => ({
|
||||
type: ReduxActionErrorTypes.INITIALIZE_PAGE_VIEWER_ERROR,
|
||||
payload: {
|
||||
show: false,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@ import {
|
|||
API_REQUEST_HEADERS,
|
||||
} from "constants/ApiConstants";
|
||||
import { ActionApiResponse } from "./ActionAPI";
|
||||
import {
|
||||
AUTH_LOGIN_URL,
|
||||
PAGE_NOT_FOUND_URL,
|
||||
SERVER_ERROR_URL,
|
||||
} from "constants/routes";
|
||||
import { AUTH_LOGIN_URL, PAGE_NOT_FOUND_URL } from "constants/routes";
|
||||
import history from "utils/history";
|
||||
import { convertObjectToQueryParams } from "utils/AppsmithUtils";
|
||||
import { SERVER_API_TIMEOUT_ERROR } from "../constants/messages";
|
||||
|
||||
//TODO(abhinav): Refactor this to make more composable.
|
||||
export const apiRequestConfig = {
|
||||
|
|
@ -22,8 +19,10 @@ export const apiRequestConfig = {
|
|||
|
||||
const axiosInstance: AxiosInstance = axios.create();
|
||||
|
||||
export const axiosConnectionAbortedCode = "ECONNABORTED";
|
||||
const executeActionRegex = /actions\/execute/;
|
||||
const currentUserRegex = /\/me$/;
|
||||
const timeoutErrorRegex = /timeout of (\d+)ms exceeded/;
|
||||
|
||||
axiosInstance.interceptors.request.use((config: any) => {
|
||||
return { ...config, timer: performance.now() };
|
||||
});
|
||||
|
|
@ -50,20 +49,25 @@ axiosInstance.interceptors.response.use(
|
|||
return response.data;
|
||||
},
|
||||
function(error: any) {
|
||||
// Return if the call was cancelled via cancel token
|
||||
if (axios.isCancel(error)) {
|
||||
return;
|
||||
}
|
||||
if (error.code === "ECONNABORTED") {
|
||||
if (error.config && error.config.url.match(currentUserRegex)) {
|
||||
history.replace({ pathname: SERVER_ERROR_URL });
|
||||
}
|
||||
return Promise.reject({
|
||||
message: "Please check your internet connection",
|
||||
});
|
||||
}
|
||||
// Return modified response if action execution failed
|
||||
if (error.config && error.config.url.match(executeActionRegex)) {
|
||||
return makeExecuteActionResponse(error.response);
|
||||
}
|
||||
// Return error if any timeout happened in other api calls
|
||||
if (
|
||||
error.code === axiosConnectionAbortedCode &&
|
||||
error.message &&
|
||||
error.message.match(timeoutErrorRegex)
|
||||
) {
|
||||
return Promise.reject({
|
||||
...error,
|
||||
message: SERVER_API_TIMEOUT_ERROR,
|
||||
});
|
||||
}
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
|
|
|
|||
BIN
app/client/src/assets/images/timeout-image.png
Normal file
BIN
app/client/src/assets/images/timeout-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
|
|
@ -8,3 +8,20 @@ const APP_STORE_NAMESPACE = "APPSMITH_LOCAL_STORE";
|
|||
|
||||
export const getAppStoreName = (appId: string) =>
|
||||
`${APP_STORE_NAMESPACE}-${appId}`;
|
||||
|
||||
export const getAppStore = (appId: string) => {
|
||||
const appStoreName = getAppStoreName(appId);
|
||||
let storeString = "{}";
|
||||
// Check if localStorage exists
|
||||
if (localStorage) {
|
||||
const appStore = localStorage.getItem(appStoreName);
|
||||
if (appStore) storeString = appStore;
|
||||
}
|
||||
let store;
|
||||
try {
|
||||
store = JSON.parse(storeString);
|
||||
} catch (e) {
|
||||
store = {};
|
||||
}
|
||||
return store;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -296,6 +296,7 @@ export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTy
|
|||
|
||||
export const ReduxActionErrorTypes: { [key: string]: string } = {
|
||||
INITIALIZE_EDITOR_ERROR: "INITIALIZE_EDITOR_ERROR",
|
||||
INITIALIZE_PAGE_VIEWER_ERROR: "INITIALIZE_PAGE_VIEWER_ERROR",
|
||||
API_ERROR: "API_ERROR",
|
||||
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
|
||||
UPDATE_APPLICATION_ERROR: "UPDATE_APPLICATION_ERROR",
|
||||
|
|
|
|||
|
|
@ -168,3 +168,6 @@ export const GOOGLE_RECAPTCHA_KEY_ERROR =
|
|||
"Google Re-Captcha Token Generation failed! Please check the Re-captcha Site Key.";
|
||||
export const GOOGLE_RECAPTCHA_DOMAIN_ERROR =
|
||||
"Google Re-Captcha Token Generation failed! Please check the allowed domains.";
|
||||
|
||||
export const SERVER_API_TIMEOUT_ERROR =
|
||||
"Appsmith server is taking too long to respond. Please try again after some time";
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ import {
|
|||
getApplicationViewerPageURL,
|
||||
} from "constants/routes";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { getIsInitialized } from "selectors/appViewSelectors";
|
||||
import {
|
||||
getIsInitialized,
|
||||
getIsInitializeError,
|
||||
} from "selectors/appViewSelectors";
|
||||
import { executeAction } from "actions/widgetActions";
|
||||
import { ExecuteActionPayload } from "constants/ActionConstants";
|
||||
import { updateWidgetPropertyRequest } from "actions/controlActions";
|
||||
|
|
@ -23,6 +26,8 @@ import {
|
|||
} from "actions/metaActions";
|
||||
import { editorInitializer } from "utils/EditorUtils";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import log from "loglevel";
|
||||
import ServerTimeout from "../common/ServerTimeout";
|
||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||
|
||||
const AppViewerBody = styled.section`
|
||||
|
|
@ -36,6 +41,7 @@ const AppViewerBody = styled.section`
|
|||
export type AppViewerProps = {
|
||||
initializeAppViewer: (applicationId: string, pageId?: string) => void;
|
||||
isInitialized: boolean;
|
||||
isInitializeError: boolean;
|
||||
executeAction: (actionPayload: ExecuteActionPayload) => void;
|
||||
updateWidgetProperty: (
|
||||
widgetId: string,
|
||||
|
|
@ -62,7 +68,7 @@ class AppViewer extends Component<
|
|||
this.setState({ registered: true });
|
||||
});
|
||||
const { applicationId, pageId } = this.props.match.params;
|
||||
console.log({ applicationId, pageId });
|
||||
log.debug({ applicationId, pageId });
|
||||
if (applicationId) {
|
||||
this.props.initializeAppViewer(applicationId, pageId);
|
||||
}
|
||||
|
|
@ -73,7 +79,10 @@ class AppViewer extends Component<
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { isInitialized } = this.props;
|
||||
const { isInitialized, isInitializeError } = this.props;
|
||||
if (isInitializeError) {
|
||||
return <ServerTimeout />;
|
||||
}
|
||||
return (
|
||||
<EditorContext.Provider
|
||||
value={{
|
||||
|
|
@ -100,6 +109,7 @@ class AppViewer extends Component<
|
|||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
isInitialized: getIsInitialized(state),
|
||||
isInitializeError: getIsInitializeError(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter, RouteComponentProps } from "react-router-dom";
|
||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import {
|
||||
BuilderRouteParams,
|
||||
getApplicationViewerPageURL,
|
||||
|
|
@ -13,15 +13,16 @@ import TouchBackend from "react-dnd-touch-backend";
|
|||
import {
|
||||
getCurrentApplicationId,
|
||||
getCurrentPageId,
|
||||
getPublishingError,
|
||||
getIsEditorLoading,
|
||||
getIsEditorInitialized,
|
||||
getIsEditorInitializeError,
|
||||
getIsEditorLoading,
|
||||
getIsPublishingApplication,
|
||||
getPublishingError,
|
||||
} from "selectors/editorSelectors";
|
||||
import {
|
||||
Dialog,
|
||||
Classes,
|
||||
AnchorButton,
|
||||
Classes,
|
||||
Dialog,
|
||||
Hotkey,
|
||||
Hotkeys,
|
||||
Spinner,
|
||||
|
|
@ -40,11 +41,12 @@ import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
|
|||
import * as Sentry from "@sentry/react";
|
||||
import {
|
||||
copyWidget,
|
||||
pasteWidget,
|
||||
deleteSelectedWidget,
|
||||
cutWidget,
|
||||
deleteSelectedWidget,
|
||||
pasteWidget,
|
||||
} from "actions/widgetActions";
|
||||
import { isMac } from "utils/helpers";
|
||||
import ServerTimeout from "../common/ServerTimeout";
|
||||
|
||||
type EditorProps = {
|
||||
currentApplicationId?: string;
|
||||
|
|
@ -53,6 +55,7 @@ type EditorProps = {
|
|||
isPublishing: boolean;
|
||||
isEditorLoading: boolean;
|
||||
isEditorInitialized: boolean;
|
||||
isEditorInitializeError: boolean;
|
||||
errorPublishing: boolean;
|
||||
copySelectedWidget: () => void;
|
||||
pasteCopiedWidget: () => void;
|
||||
|
|
@ -189,6 +192,8 @@ class Editor extends Component<Props> {
|
|||
nextProps.isPublishing !== this.props.isPublishing ||
|
||||
nextProps.isEditorLoading !== this.props.isEditorLoading ||
|
||||
nextProps.errorPublishing !== this.props.errorPublishing ||
|
||||
nextProps.isEditorInitializeError !==
|
||||
this.props.isEditorInitializeError ||
|
||||
nextState.isDialogOpen !== this.state.isDialogOpen ||
|
||||
nextState.registered !== this.state.registered
|
||||
);
|
||||
|
|
@ -200,6 +205,9 @@ class Editor extends Component<Props> {
|
|||
});
|
||||
};
|
||||
public render() {
|
||||
if (this.props.isEditorInitializeError) {
|
||||
return <ServerTimeout />;
|
||||
}
|
||||
if (!this.props.isEditorInitialized || !this.state.registered) {
|
||||
return (
|
||||
<CenteredWrapper style={{ height: "calc(100vh - 48px)" }}>
|
||||
|
|
@ -260,6 +268,7 @@ const mapStateToProps = (state: AppState) => ({
|
|||
isPublishing: getIsPublishingApplication(state),
|
||||
isEditorLoading: getIsEditorLoading(state),
|
||||
isEditorInitialized: getIsEditorInitialized(state),
|
||||
isEditorInitializeError: getIsEditorInitializeError(state),
|
||||
user: getCurrentUser(state),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { RouterProps } from "react-router";
|
||||
import { RouteComponentProps, withRouter } from "react-router";
|
||||
import styled from "styled-components";
|
||||
import Button from "components/editorComponents/Button";
|
||||
import PageUnavailableImage from "assets/images/404-image.png";
|
||||
|
|
@ -20,7 +20,7 @@ const Wrapper = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
class PageNotFound extends React.PureComponent<RouterProps> {
|
||||
class PageNotFound extends React.PureComponent<RouteComponentProps> {
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -54,4 +54,4 @@ class PageNotFound extends React.PureComponent<RouterProps> {
|
|||
}
|
||||
}
|
||||
|
||||
export default PageNotFound;
|
||||
export default withRouter(PageNotFound);
|
||||
|
|
|
|||
56
app/client/src/pages/common/ServerTimeout.tsx
Normal file
56
app/client/src/pages/common/ServerTimeout.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import AppTimeoutImage from "assets/images/timeout-image.png";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
height: calc(100vh - ${props => props.theme.headerHeight});
|
||||
.bold-text {
|
||||
font-weight: ${props => props.theme.fontWeights[3]};
|
||||
font-size: 24px;
|
||||
}
|
||||
.page-unavailable-img {
|
||||
width: 35%;
|
||||
}
|
||||
.button-position {
|
||||
margin: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const RetryButton = styled.button`
|
||||
background-color: #f3672a;
|
||||
color: white;
|
||||
height: 40px;
|
||||
width: 300px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 17px;
|
||||
`;
|
||||
|
||||
const ServerTimeout = () => {
|
||||
return (
|
||||
<Wrapper>
|
||||
<img
|
||||
src={AppTimeoutImage}
|
||||
alt="Page Unavailable"
|
||||
className="page-unavailable-img"
|
||||
/>
|
||||
<div>
|
||||
<p className="bold-text">
|
||||
Appsmith server is taking too long to respond
|
||||
</p>
|
||||
<p>Please retry after some time</p>
|
||||
<RetryButton onClick={() => window.location.reload()}>
|
||||
{"Retry"}
|
||||
</RetryButton>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServerTimeout;
|
||||
|
|
@ -5,12 +5,14 @@ import {
|
|||
ReduxAction,
|
||||
ReduxActionTypes,
|
||||
PageListPayload,
|
||||
ReduxActionErrorTypes,
|
||||
} from "constants/ReduxActionConstants";
|
||||
import { FetchPublishedPageSuccessPayload } from "actions/pageActions";
|
||||
|
||||
const initialState: AppViewReduxState = {
|
||||
isFetchingPage: false,
|
||||
initialized: false,
|
||||
initializeError: false,
|
||||
pages: [],
|
||||
pageWidgetId: "0",
|
||||
};
|
||||
|
|
@ -24,6 +26,11 @@ const appViewReducer = createReducer(initialState, {
|
|||
) => {
|
||||
return { ...state, initialized: true };
|
||||
},
|
||||
[ReduxActionErrorTypes.INITIALIZE_PAGE_VIEWER_ERROR]: (
|
||||
state: AppViewReduxState,
|
||||
) => {
|
||||
return { ...state, initializeError: true };
|
||||
},
|
||||
[ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => {
|
||||
return { ...state, dsl: undefined, isFetchingPage: true };
|
||||
},
|
||||
|
|
@ -45,6 +52,7 @@ const appViewReducer = createReducer(initialState, {
|
|||
|
||||
export interface AppViewReduxState {
|
||||
initialized: boolean;
|
||||
initializeError: boolean;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
isFetchingPage: boolean;
|
||||
currentLayoutId?: string;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ import {
|
|||
ReduxActionTypes,
|
||||
ReduxActionErrorTypes,
|
||||
} from "constants/ReduxActionConstants";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import moment from "moment";
|
||||
|
||||
const initialState: EditorReduxState = {
|
||||
initialized: false,
|
||||
initializationError: false,
|
||||
loadingStates: {
|
||||
publishing: false,
|
||||
publishingError: false,
|
||||
|
|
@ -68,11 +67,8 @@ const editorReducer = createReducer(initialState, {
|
|||
) => {
|
||||
return {
|
||||
...state,
|
||||
loadingStates: {
|
||||
...state.loadingStates,
|
||||
loading: false,
|
||||
loadingError: true,
|
||||
},
|
||||
initializationError: true,
|
||||
initialized: false,
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => {
|
||||
|
|
@ -178,7 +174,7 @@ const editorReducer = createReducer(initialState, {
|
|||
|
||||
export interface EditorReduxState {
|
||||
initialized: boolean;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
initializationError: boolean;
|
||||
pageWidgetId?: string;
|
||||
currentLayoutId?: string;
|
||||
currentPageName?: string;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { put, takeLatest, call } from "redux-saga/effects";
|
|||
import { ERROR_401, ERROR_500, ERROR_0 } from "constants/messages";
|
||||
import { ToastType } from "react-toastify";
|
||||
import log from "loglevel";
|
||||
import { axiosConnectionAbortedCode } from "../api/Api";
|
||||
|
||||
export function* callAPI(apiCall: any, requestPayload: any) {
|
||||
try {
|
||||
|
|
@ -60,7 +61,7 @@ export function getResponseErrorMessage(response: ApiResponse) {
|
|||
: undefined;
|
||||
}
|
||||
|
||||
type ErrorPayloadType = { code?: number; message?: string };
|
||||
type ErrorPayloadType = { code?: number | string; message?: string };
|
||||
let ActionErrorDisplayMap: {
|
||||
[key: string]: (error: ErrorPayloadType) => string;
|
||||
} = {};
|
||||
|
|
@ -92,6 +93,7 @@ export function* errorSaga(
|
|||
type,
|
||||
payload: { error, show = true },
|
||||
} = errorAction;
|
||||
|
||||
const message =
|
||||
error && error.message ? error.message : ActionErrorDisplayMap[type](error);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import { all, call, put, select, take, takeLatest } from "redux-saga/effects";
|
||||
import {
|
||||
all,
|
||||
call,
|
||||
put,
|
||||
select,
|
||||
take,
|
||||
takeLatest,
|
||||
race,
|
||||
} from "redux-saga/effects";
|
||||
import {
|
||||
InitializeEditorPayload,
|
||||
Page,
|
||||
ReduxAction,
|
||||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
|
|
@ -21,126 +28,86 @@ import { fetchActions, fetchActionsForView } from "actions/actionActions";
|
|||
import { fetchApplication } from "actions/applicationActions";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||
import { AppState } from "reducers";
|
||||
import PageApi, { FetchPageResponse } from "api/PageApi";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
import { extractCurrentDSL } from "utils/WidgetPropsUtils";
|
||||
import { APP_MODE } from "reducers/entityReducers/appReducer";
|
||||
import { getAppStoreName } from "constants/AppConstants";
|
||||
import { getAppStore } from "constants/AppConstants";
|
||||
import { getDefaultPageId } from "./selectors";
|
||||
|
||||
const getAppStore = (appId: string) => {
|
||||
const appStoreName = getAppStoreName(appId);
|
||||
const storeString = localStorage.getItem(appStoreName) || "{}";
|
||||
let store;
|
||||
try {
|
||||
store = JSON.parse(storeString);
|
||||
} catch (e) {
|
||||
store = {};
|
||||
}
|
||||
return store;
|
||||
};
|
||||
import { populatePageDSLsSaga } from "./PageSagas";
|
||||
import { initEditorError, initViewerError } from "../actions/initActions";
|
||||
|
||||
function* initializeEditorSaga(
|
||||
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
|
||||
) {
|
||||
const { applicationId, pageId } = initializeEditorAction.payload;
|
||||
// Step 1: Set App Mode. Start getting all the data needed
|
||||
yield put(setAppMode(APP_MODE.EDIT));
|
||||
yield put({ type: ReduxActionTypes.START_EVALUATION });
|
||||
yield all([
|
||||
put(fetchPageList(applicationId, APP_MODE.EDIT)),
|
||||
put(fetchEditorConfigs()),
|
||||
put(fetchActions(applicationId)),
|
||||
put(fetchPage(pageId)),
|
||||
put(fetchApplication(applicationId, APP_MODE.EDIT)),
|
||||
]);
|
||||
// Step 2: Wait for all data to be in the state
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_PAGE_SUCCESS),
|
||||
take(ReduxActionTypes.SWITCH_CURRENT_PAGE_ID),
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||
]);
|
||||
|
||||
// Step 3: Call all the APIs which needs Organization Id from PageList API response.
|
||||
yield all([put(fetchPlugins()), put(fetchDatasources())]);
|
||||
|
||||
// Step 4: Wait for all data to be in the state
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
||||
]);
|
||||
|
||||
// Step 5: Set app store
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
|
||||
const currentApplication = yield select(getCurrentApplication);
|
||||
|
||||
const appName = currentApplication ? currentApplication.name : "";
|
||||
const appId = currentApplication ? currentApplication.id : "";
|
||||
|
||||
AnalyticsUtil.logEvent("EDITOR_OPEN", {
|
||||
appId: appId,
|
||||
appName: appName,
|
||||
});
|
||||
|
||||
// Step 6: Notify UI that the editor is ready to go
|
||||
yield put({
|
||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||
});
|
||||
yield call(populatePageDSLsSaga);
|
||||
}
|
||||
|
||||
function* fetchPageDSLSaga(pageId: string) {
|
||||
try {
|
||||
const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, {
|
||||
id: pageId,
|
||||
yield put(setAppMode(APP_MODE.EDIT));
|
||||
yield put({ type: ReduxActionTypes.START_EVALUATION });
|
||||
yield all([
|
||||
put(fetchPageList(applicationId, APP_MODE.EDIT)),
|
||||
put(fetchEditorConfigs()),
|
||||
put(fetchActions(applicationId)),
|
||||
put(fetchPage(pageId)),
|
||||
put(fetchApplication(applicationId, APP_MODE.EDIT)),
|
||||
]);
|
||||
|
||||
const resultOfPrimaryCalls = yield race({
|
||||
success: all([
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_PAGE_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||
]),
|
||||
failure: take([
|
||||
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
|
||||
]),
|
||||
});
|
||||
const isValidResponse = yield validateResponse(fetchPageResponse);
|
||||
if (isValidResponse) {
|
||||
return {
|
||||
pageId: pageId,
|
||||
dsl: extractCurrentDSL(fetchPageResponse),
|
||||
};
|
||||
|
||||
if (resultOfPrimaryCalls.failure) {
|
||||
yield put(initEditorError());
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSL_ERROR,
|
||||
payload: {
|
||||
pageId: pageId,
|
||||
error,
|
||||
show: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* populatePageDSLsSaga() {
|
||||
try {
|
||||
yield put({
|
||||
type: ReduxActionTypes.POPULATE_PAGEDSLS_INIT,
|
||||
yield all([put(fetchPlugins()), put(fetchDatasources())]);
|
||||
|
||||
const resultOfSecondaryCalls = yield race({
|
||||
success: all([
|
||||
take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
||||
]),
|
||||
failure: take([
|
||||
ReduxActionErrorTypes.FETCH_PLUGINS_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR,
|
||||
]),
|
||||
});
|
||||
const pageIds: string[] = yield select((state: AppState) =>
|
||||
state.entities.pageList.pages.map((page: Page) => page.pageId),
|
||||
);
|
||||
const pageDSLs = yield all(
|
||||
pageIds.map((pageId: string) => {
|
||||
return call(fetchPageDSLSaga, pageId);
|
||||
}),
|
||||
);
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
||||
payload: pageDSLs,
|
||||
|
||||
if (resultOfSecondaryCalls.failure) {
|
||||
yield put(initEditorError());
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
|
||||
const currentApplication = yield select(getCurrentApplication);
|
||||
|
||||
const appName = currentApplication ? currentApplication.name : "";
|
||||
const appId = currentApplication ? currentApplication.id : "";
|
||||
|
||||
AnalyticsUtil.logEvent("EDITOR_OPEN", {
|
||||
appId: appId,
|
||||
appName: appName,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.POPULATE_PAGEDSLS_ERROR,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||
});
|
||||
} catch (e) {
|
||||
yield put(initEditorError());
|
||||
return;
|
||||
}
|
||||
|
||||
yield call(populatePageDSLsSaga);
|
||||
}
|
||||
|
||||
export function* initializeAppViewerSaga(
|
||||
|
|
@ -156,11 +123,23 @@ export function* initializeAppViewerSaga(
|
|||
put(fetchApplication(applicationId, APP_MODE.PUBLISHED)),
|
||||
]);
|
||||
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
||||
]);
|
||||
const resultOfPrimaryCalls = yield race({
|
||||
success: all([
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
||||
]),
|
||||
failure: take([
|
||||
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
|
||||
]),
|
||||
});
|
||||
|
||||
if (resultOfPrimaryCalls.failure) {
|
||||
yield put(initViewerError());
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
const defaultPageId = yield select(getDefaultPageId);
|
||||
|
|
@ -168,7 +147,15 @@ export function* initializeAppViewerSaga(
|
|||
|
||||
if (toLoadPageId) {
|
||||
yield put(fetchPublishedPage(toLoadPageId, true));
|
||||
yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS);
|
||||
|
||||
const resultOfFetchPage = yield race({
|
||||
success: take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS),
|
||||
failure: take(ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR),
|
||||
});
|
||||
if (resultOfFetchPage.failure) {
|
||||
yield put(initViewerError());
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(setAppMode(APP_MODE.PUBLISHED));
|
||||
yield put(updateAppStore(getAppStore(applicationId)));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
||||
import { AppState } from "reducers";
|
||||
import {
|
||||
Page,
|
||||
PageListPayload,
|
||||
ReduxAction,
|
||||
ReduxActionErrorTypes,
|
||||
|
|
@ -43,7 +44,6 @@ import {
|
|||
select,
|
||||
takeLatest,
|
||||
takeLeading,
|
||||
take,
|
||||
} from "redux-saga/effects";
|
||||
import history from "utils/history";
|
||||
import { BUILDER_PAGE_URL } from "constants/routes";
|
||||
|
|
@ -117,6 +117,16 @@ export function* fetchPageListSaga(
|
|||
PerformanceTracker.stopAsyncTracking(
|
||||
PerformanceTransactionName.FETCH_PAGE_LIST_API,
|
||||
);
|
||||
} else {
|
||||
PerformanceTracker.stopAsyncTracking(
|
||||
PerformanceTransactionName.FETCH_PAGE_LIST_API,
|
||||
);
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||
payload: {
|
||||
error: response.responseMeta.error,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
PerformanceTracker.stopAsyncTracking(
|
||||
|
|
@ -608,6 +618,57 @@ export function* setDataUrl() {
|
|||
yield put(setUrlData(urlData));
|
||||
}
|
||||
|
||||
function* fetchPageDSLSaga(pageId: string) {
|
||||
try {
|
||||
const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, {
|
||||
id: pageId,
|
||||
});
|
||||
const isValidResponse = yield validateResponse(fetchPageResponse);
|
||||
if (isValidResponse) {
|
||||
return {
|
||||
pageId: pageId,
|
||||
dsl: extractCurrentDSL(fetchPageResponse),
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSL_ERROR,
|
||||
payload: {
|
||||
pageId: pageId,
|
||||
error,
|
||||
show: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* populatePageDSLsSaga() {
|
||||
try {
|
||||
yield put({
|
||||
type: ReduxActionTypes.POPULATE_PAGEDSLS_INIT,
|
||||
});
|
||||
const pageIds: string[] = yield select((state: AppState) =>
|
||||
state.entities.pageList.pages.map((page: Page) => page.pageId),
|
||||
);
|
||||
const pageDSLs = yield all(
|
||||
pageIds.map((pageId: string) => {
|
||||
return call(fetchPageDSLSaga, pageId);
|
||||
}),
|
||||
);
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
||||
payload: pageDSLs,
|
||||
});
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.POPULATE_PAGEDSLS_ERROR,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default function* pageSagas() {
|
||||
yield all([
|
||||
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ export const getIsInitialized = createSelector(
|
|||
(view: AppViewReduxState) => view.initialized,
|
||||
);
|
||||
|
||||
export const getIsInitializeError = createSelector(
|
||||
getAppViewState,
|
||||
(view: AppViewReduxState) => view.initializeError,
|
||||
);
|
||||
|
||||
export const getCurrentDSLPageId = createSelector(
|
||||
getPageListState,
|
||||
(pageList: PageListReduxState) => pageList.currentPageId,
|
||||
|
|
|
|||
|
|
@ -20,14 +20,12 @@ import { getDataTree } from "selectors/dataTreeSelectors";
|
|||
import _ from "lodash";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import { DataTreeWidget, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||
import { getActions, getWidgetsMeta } from "sagas/selectors";
|
||||
import { getActions } from "sagas/selectors";
|
||||
|
||||
import * as log from "loglevel";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import { getCanvasWidgets } from "./entitiesSelector";
|
||||
import { MetaState } from "../reducers/entityReducers/metaReducer";
|
||||
import { WidgetTypes } from "../constants/WidgetConstants";
|
||||
|
||||
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
||||
|
|
@ -45,6 +43,9 @@ const getWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
|||
export const getIsEditorInitialized = (state: AppState) =>
|
||||
state.ui.editor.initialized;
|
||||
|
||||
export const getIsEditorInitializeError = (state: AppState): boolean =>
|
||||
state.ui.editor.initializationError;
|
||||
|
||||
export const getIsEditorLoading = (state: AppState) =>
|
||||
state.ui.editor.loadingStates.loading;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user