diff --git a/app/client/cypress/fixtures/executeAction.json b/app/client/cypress/fixtures/executeAction.json new file mode 100644 index 0000000000..6be478e7e8 --- /dev/null +++ b/app/client/cypress/fixtures/executeAction.json @@ -0,0 +1,651 @@ +{ + "clientSchemaVersion": 1, + "serverSchemaVersion": 4, + "exportedApplication": { + "name": "Execute Action Test", + "isPublic": false, + "appIsExample": false, + "unreadCommentThreads": 0, + "color": "#D9E7FF", + "icon": "camera", + "slug": "execute-action-test", + "evaluationVersion": 2, + "applicationVersion": 2, + "isManualUpdate": false, + "new": true + }, + "datasourceList": [], + "pageList": [ + { + "userPermissions": [ + "read:pages", + "manage:pages" + ], + "gitSyncId": "62987c9ec43c3d0bd6572220_62987c9ec43c3d0bd6572222", + "unpublishedPage": { + "name": "Page1", + "slug": "page1", + "layouts": [ + { + "id": "Page1", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896.0, + "snapColumns": 64.0, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0.0, + "bottomRow": 1290.0, + "containerStyle": "none", + "snapRows": 125.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 59.0, + "minHeight": 1292.0, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1.0, + "dynamicBindingPathList": [], + "leftColumn": 0.0, + "children": [ + { + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 26.0, + "bottomRow": 36.0, + "parentRowSpace": 10.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 20.0625, + "dynamicTriggerPathList": [], + "leftColumn": 23.0, + "dynamicBindingPathList": [ + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "#FFC13D", + "text": "User count :{{Api1.data.users.length}}", + "key": "i4b3tj04fv", + "isDeprecated": false, + "rightColumn": 38.0, + "textAlign": "LEFT", + "widgetId": "feinx8nce5", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "fontSize": "1rem" + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "Page1_Api1", + "name": "Api1", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ] + ], + "new": false + } + ], + "userPermissions": [] + }, + "publishedPage": { + "name": "Page1", + "slug": "page1", + "layouts": [ + { + "id": "Page1", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896.0, + "snapColumns": 64.0, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0.0, + "bottomRow": 1290.0, + "containerStyle": "none", + "snapRows": 125.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 59.0, + "minHeight": 1292.0, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1.0, + "dynamicBindingPathList": [], + "leftColumn": 0.0, + "children": [ + { + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 26.0, + "bottomRow": 36.0, + "parentRowSpace": 10.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 20.0625, + "dynamicTriggerPathList": [], + "leftColumn": 23.0, + "dynamicBindingPathList": [ + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "#FFC13D", + "text": "User count :{{Api1.data.users.length}}", + "key": "i4b3tj04fv", + "isDeprecated": false, + "rightColumn": 38.0, + "textAlign": "LEFT", + "widgetId": "feinx8nce5", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "fontSize": "1rem" + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "Page1_Api1", + "name": "Api1", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ] + ], + "new": false + } + ], + "userPermissions": [] + }, + "new": true + }, + { + "userPermissions": [ + "read:pages", + "manage:pages" + ], + "gitSyncId": "62987c9ec43c3d0bd6572220_62987cdac43c3d0bd6572227", + "unpublishedPage": { + "name": "Page2", + "slug": "page2", + "layouts": [ + { + "id": "Page2", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224.0, + "snapColumns": 64.0, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0.0, + "bottomRow": 730.0, + "containerStyle": "none", + "snapRows": 70.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 59.0, + "minHeight": 710.0, + "parentColumnSpace": 1.0, + "dynamicBindingPathList": [], + "leftColumn": 0.0, + "children": [ + { + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 31.0, + "bottomRow": 41.0, + "parentRowSpace": 10.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 20.0625, + "dynamicTriggerPathList": [], + "leftColumn": 23.0, + "dynamicBindingPathList": [ + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "#FFC13D", + "text": "User count :{{Api1.data.users.length}}", + "key": "i4b3tj04fv", + "isDeprecated": false, + "rightColumn": 38.0, + "textAlign": "LEFT", + "widgetId": "31eat3ae9u", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "fontSize": "1rem" + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "Page2_Api1", + "name": "Api1", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ] + ], + "new": false + } + ], + "userPermissions": [] + }, + "publishedPage": { + "name": "Page2", + "slug": "page2", + "layouts": [ + { + "id": "Page2", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224.0, + "snapColumns": 64.0, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0.0, + "bottomRow": 730.0, + "containerStyle": "none", + "snapRows": 70.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 59.0, + "minHeight": 710.0, + "parentColumnSpace": 1.0, + "dynamicBindingPathList": [], + "leftColumn": 0.0, + "children": [ + { + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b52.svg", + "topRow": 31.0, + "bottomRow": 41.0, + "parentRowSpace": 10.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 20.0625, + "dynamicTriggerPathList": [], + "leftColumn": 23.0, + "dynamicBindingPathList": [ + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "#FFC13D", + "text": "User count :{{Api1.data.users.length}}", + "key": "i4b3tj04fv", + "isDeprecated": false, + "rightColumn": 38.0, + "textAlign": "LEFT", + "widgetId": "31eat3ae9u", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "fontSize": "1rem" + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "Page2_Api1", + "name": "Api1", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ] + ], + "new": false + } + ], + "userPermissions": [] + }, + "new": true + } + ], + "pageOrder": [ + "Page1", + "Page2" + ], + "publishedPageOrder": [ + "Page1", + "Page2" + ], + "publishedDefaultPageName": "Page1", + "unpublishedDefaultPageName": "Page1", + "actionList": [ + { + "id": "Page1_Api1", + "userPermissions": [ + "read:actions", + "execute:actions", + "manage:actions" + ], + "gitSyncId": "62987c9ec43c3d0bd6572220_62987cafc43c3d0bd6572224", + "pluginType": "API", + "pluginId": "restapi-plugin", + "unpublishedAction": { + "name": "Api1", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "https://mock-api.appsmith.com" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Page1", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "/users", + "headers": [], + "encodeParamsToggle": true, + "queryParameters": [ + { + "key": "pageSize", + "value": "5" + } + ], + "bodyFormData": [], + "httpMethod": "GET", + "pluginSpecifiedTemplates": [ + { + "value": true + } + ], + "formData": { + "apiContentType": "none" + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "Api1" + }, + "publishedAction": { + "name": "Api1", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "https://mock-api.appsmith.com" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Page1", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "/users", + "headers": [], + "encodeParamsToggle": true, + "queryParameters": [ + { + "key": "pageSize", + "value": "5" + } + ], + "bodyFormData": [], + "httpMethod": "GET", + "pluginSpecifiedTemplates": [ + { + "value": true + } + ], + "formData": { + "apiContentType": "none" + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "Api1" + }, + "new": false + }, + { + "id": "Page2_Api1", + "userPermissions": [ + "read:actions", + "execute:actions", + "manage:actions" + ], + "gitSyncId": "62987c9ec43c3d0bd6572220_62987ce3c43c3d0bd657222a", + "pluginType": "API", + "pluginId": "restapi-plugin", + "unpublishedAction": { + "name": "Api1", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "https://mock-api.appsmith.com" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Page2", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "/users", + "headers": [], + "encodeParamsToggle": true, + "queryParameters": [ + { + "key": "pageSize", + "value": "10" + } + ], + "bodyFormData": [], + "httpMethod": "GET", + "pluginSpecifiedTemplates": [ + { + "value": true + } + ], + "formData": { + "apiContentType": "none" + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "Api1" + }, + "publishedAction": { + "name": "Api1", + "datasource": { + "userPermissions": [], + "name": "DEFAULT_REST_DATASOURCE", + "pluginId": "restapi-plugin", + "datasourceConfiguration": { + "url": "https://mock-api.appsmith.com" + }, + "invalids": [], + "messages": [], + "isValid": true, + "new": true + }, + "pageId": "Page2", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "path": "/users", + "headers": [], + "encodeParamsToggle": true, + "queryParameters": [ + { + "key": "pageSize", + "value": "10" + } + ], + "bodyFormData": [], + "httpMethod": "GET", + "pluginSpecifiedTemplates": [ + { + "value": true + } + ], + "formData": { + "apiContentType": "none" + } + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "confirmBeforeExecute": false, + "userPermissions": [], + "validName": "Api1" + }, + "new": false + } + ], + "actionCollectionList": [], + "invisibleActionFields": { + "Page1_Api1": { + "unpublishedUserSetOnLoad": true, + "publishedUserSetOnLoad": true + }, + "Page2_Api1": { + "unpublishedUserSetOnLoad": true, + "publishedUserSetOnLoad": true + } + }, + "editModeTheme": { + "name": "Classic", + "displayName": "Classic", + "new": true, + "isSystemTheme": true + }, + "publishedTheme": { + "name": "Classic", + "displayName": "Classic", + "new": true, + "isSystemTheme": true + }, + "publishedLayoutmongoEscapedWidgets": {}, + "unpublishedLayoutmongoEscapedWidgets": {} +} \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/ExecuteAction_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/ExecuteAction_spec.js new file mode 100644 index 0000000000..c68fd35850 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PreviewMode/ExecuteAction_spec.js @@ -0,0 +1,54 @@ +import homePage from "../../../../locators/HomePage"; + +describe("Execute Action Functionality", function() { + // before(() => { + // cy.get(homePage.homeIcon).click(); + // cy.get(homePage.optionsIcon) + // .first() + // .click(); + // // Importing the App from the sample application + // cy.get(homePage.orgImportAppOption).click({ force: true }); + // cy.get(homePage.orgImportAppModal).should("be.visible"); + // cy.xpath(homePage.uploadLogo).attachFile("executeAction.json"); + // cy.get(homePage.importAppProgressWrapper).should("be.visible"); + // }); + + it.skip("checks whether execute action is getting called on page load only once", function() { + // Open deployed version + cy.get(homePage.deployPopupOptionTrigger).click({ force: true }); + cy.get(homePage.currentDeployedPreviewBtn) + .invoke("removeAttr", "target") + .click(); + + let completedIds = []; + + cy.get("@postExecute.all") + .then((respBody) => { + const totalRequests = [ + ...new Set(respBody.map((req) => req.browserRequestId)), + ]; + completedIds = totalRequests; + return totalRequests; + }) + .should("have.length", 1); + + cy.wait(500); + + cy.get(".t--page-switch-tab") + .contains("Page2") + .click({ force: true }); + + cy.wait(1000); + + cy.get("@postExecute.all") + .then((respBody) => { + const totalRequests = [ + ...new Set(respBody.map((req) => req.browserRequestId)), + ]; + return totalRequests.filter((reqId) => !completedIds.includes(reqId)); + }) + .should("have.length", 1); + + cy.wait(2000); + }); +}); diff --git a/app/client/src/actions/evaluationActions.ts b/app/client/src/actions/evaluationActions.ts index 9bc36cf226..8493ca3fd3 100644 --- a/app/client/src/actions/evaluationActions.ts +++ b/app/client/src/actions/evaluationActions.ts @@ -11,8 +11,9 @@ import { QueryActionConfig } from "entities/Action"; export const FIRST_EVAL_REDUX_ACTIONS = [ // Pages - ReduxActionTypes.FETCH_PAGE_SUCCESS, - ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS, + // ReduxActionTypes.FETCH_PAGE_SUCCESS, + // ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS, + ReduxActionTypes.FETCH_ALL_PAGE_ENTITY_COMPLETION, ]; export const EVALUATE_REDUX_ACTIONS = [ ...FIRST_EVAL_REDUX_ACTIONS, diff --git a/app/client/src/actions/jsActionActions.ts b/app/client/src/actions/jsActionActions.ts index d6c045fd8e..c83a7de4ad 100644 --- a/app/client/src/actions/jsActionActions.ts +++ b/app/client/src/actions/jsActionActions.ts @@ -134,6 +134,12 @@ export const fetchJSCollectionsForPageSuccess = (actions: JSCollection[]) => { }; }; +export const fetchJSCollectionsForPageError = () => { + return { + type: ReduxActionErrorTypes.FETCH_JS_ACTIONS_FOR_PAGE_ERROR, + }; +}; + export const fetchJSCollectionsForView = ({ applicationId, }: { diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 8416b466aa..0eb0de5d97 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -5,9 +5,9 @@ import { ReduxActionTypes, UpdateCanvasPayload, ReduxActionErrorTypes, - AnyReduxAction, WidgetReduxActionTypes, ReplayReduxActionTypes, + AnyReduxAction, } from "@appsmith/constants/ReduxActionConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { WidgetOperation } from "widgets/BaseWidget"; @@ -62,20 +62,29 @@ export const fetchPublishedPage = ( }, }); -export const fetchPageSuccess = ( - postEvalActions: Array, -): EvaluationReduxAction => { +export const fetchPageSuccess = (): EvaluationReduxAction => { return { type: ReduxActionTypes.FETCH_PAGE_SUCCESS, - postEvalActions, payload: undefined, }; }; -export const fetchPublishedPageSuccess = ( - postEvalActions: Array, -): EvaluationReduxAction => ({ +export const fetchPublishedPageSuccess = (): EvaluationReduxAction => ({ type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS, + payload: undefined, +}); + +/** + * After all page entities are fetched like DSL, actions and JsObjects, + * we trigger evaluation using this redux action, here we supply postEvalActions + * to trigger action after evaluation has been completed like executeOnPageLoadAction + * + * @param {Array} postEvalActions + */ +export const fetchAllPageEntityCompletion = ( + postEvalActions: Array, +) => ({ + type: ReduxActionTypes.FETCH_ALL_PAGE_ENTITY_COMPLETION, postEvalActions, payload: undefined, }); diff --git a/app/client/src/actions/pluginActionActions.ts b/app/client/src/actions/pluginActionActions.ts index 80811ce30c..30c8a547d3 100644 --- a/app/client/src/actions/pluginActionActions.ts +++ b/app/client/src/actions/pluginActionActions.ts @@ -54,23 +54,25 @@ export const fetchActionsForView = ({ export const fetchActionsForPage = ( pageId: string, - postEvalActions: Array = [], ): EvaluationReduxAction => { return { type: ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_INIT, payload: { pageId }, - postEvalActions, }; }; export const fetchActionsForPageSuccess = ( actions: Action[], - postEvalActions?: Array, ): EvaluationReduxAction => { return { type: ReduxActionTypes.FETCH_ACTIONS_FOR_PAGE_SUCCESS, payload: actions, - postEvalActions, + }; +}; + +export const fetchActionsForPageError = () => { + return { + type: ReduxActionErrorTypes.FETCH_ACTIONS_FOR_PAGE_ERROR, }; }; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index aa704a5522..ebab26e24c 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -207,6 +207,7 @@ export const ReduxActionTypes = { CLEAR_CANVAS: "CLEAR_CANVAS", FETCH_PAGE_INIT: "FETCH_PAGE_INIT", FETCH_PAGE_SUCCESS: "FETCH_PAGE_SUCCESS", + FETCH_ALL_PAGE_ENTITY_COMPLETION: "FETCH_ALL_PAGE_ENTITY_COMPLETION", DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS", REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS", LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE", diff --git a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx index 04c8a1c6d0..63181830ed 100644 --- a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx +++ b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useMemo } from "react"; +import React, { useMemo } from "react"; import { Link, RouteComponentProps, withRouter } from "react-router-dom"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { getIsFetchingPage } from "selectors/appViewSelectors"; import styled from "styled-components"; import { AppViewerRouteParams } from "constants/routes"; @@ -19,7 +19,6 @@ import { isPermitted, PERMISSION_TYPE, } from "../Applications/permissionHelpers"; -import { fetchPublishedPage } from "actions/pageActions"; import { builderURL } from "RouteBuilder"; const Section = styled.section<{ @@ -36,19 +35,13 @@ const Section = styled.section<{ type AppViewerPageContainerProps = RouteComponentProps; function AppViewerPageContainer(props: AppViewerPageContainerProps) { - const dispatch = useDispatch(); const currentPageName = useSelector(getCurrentPageName); const widgets = useSelector(getCanvasWidgetDsl); const isFetchingPage = useSelector(getIsFetchingPage); const currentApplication = useSelector(getCurrentApplication); const { match } = props; - const { pageId } = match.params; const { applicationSlug, pageSlug } = useSelector(selectURLSlugs); - useEffect(() => { - pageId && dispatch(fetchPublishedPage(pageId, true)); - }, [pageId, location.pathname]); - // get appsmith editr link const appsmithEditorLink = useMemo(() => { if ( diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index ce2b3f30a4..30e74fe541 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -285,9 +285,8 @@ export function* fetchActionsForPageSaga( ); const isValidResponse = yield validateResponse(response); if (isValidResponse) { - yield put( - fetchActionsForPageSuccess(response.data, action.postEvalActions), - ); + yield put(fetchActionsForPageSuccess(response.data)); + // wait for success of PerformanceTracker.stopAsyncTracking( PerformanceTransactionName.FETCH_PAGE_ACTIONS_API, ); diff --git a/app/client/src/sagas/EvaluationsSaga.ts b/app/client/src/sagas/EvaluationsSaga.ts index a762978a03..0ce22349ef 100644 --- a/app/client/src/sagas/EvaluationsSaga.ts +++ b/app/client/src/sagas/EvaluationsSaga.ts @@ -127,7 +127,7 @@ function* evaluateTreeSaga( dataTree, dependencies, errors, - evalMetaUpdates, + evalMetaUpdates = [], evaluationOrder, jsUpdates, logs, diff --git a/app/client/src/sagas/InitSagas.ts b/app/client/src/sagas/InitSagas.ts index 5316ba4a70..8e49e446c8 100644 --- a/app/client/src/sagas/InitSagas.ts +++ b/app/client/src/sagas/InitSagas.ts @@ -20,6 +20,7 @@ import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import { fetchPage, + fetchPageSuccess, fetchPublishedPage, fetchPublishedPageSuccess, resetApplicationWidgets, @@ -87,6 +88,7 @@ import { isURLDeprecated, getUpdatedRoute } from "utils/helpers"; import { fillPathname, viewerURL, builderURL } from "RouteBuilder"; import { enableGuidedTour } from "actions/onboardingActions"; import { setPreviewModeAction } from "actions/editorActions"; +import { fetchAllPageEntityCompletion } from "actions/pageActions"; import { fetchSelectedAppThemeAction, fetchAppThemesAction, @@ -97,17 +99,9 @@ export function* failFastApiCalls( 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); + yield all(triggerActions.map((triggerAction) => put(triggerAction))); const effectRaceResult = yield race({ - success: all(successEffects), + success: all(successActions.map((successAction) => take(successAction))), failure: take(failureActions), }); if (effectRaceResult.failure) { @@ -230,19 +224,12 @@ function* initiateEditorApplicationAndPages(payload: InitializeEditorPayload) { 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) { +function* initiateEditorActions(toLoadPageId: string, applicationId: string) { const initActionsCalls = [ + fetchPage(toLoadPageId, true), fetchActions({ applicationId }, []), fetchJSCollections({ applicationId }), fetchSelectedAppThemeAction(applicationId), @@ -254,12 +241,14 @@ function* initiateEditorActions(applicationId: string) { ReduxActionTypes.FETCH_ACTIONS_SUCCESS, ReduxActionTypes.FETCH_APP_THEMES_SUCCESS, ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS, + fetchPageSuccess().type, ]; const failureActionEffects = [ ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR, ReduxActionErrorTypes.FETCH_ACTIONS_ERROR, ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR, ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR, + ReduxActionErrorTypes.FETCH_PAGE_ERROR, ]; const allActionCalls: boolean = yield failFastApiCalls( initActionsCalls, @@ -273,7 +262,7 @@ function* initiateEditorActions(applicationId: string) { yield put({ type: ReduxActionTypes.FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS, }); - yield put(executePageLoadActions()); + yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); } } @@ -337,7 +326,8 @@ function* initializeEditorSaga( PerformanceTransactionName.INIT_EDIT_APP, ); - yield call(initiateEditorApplicationAndPages, payload); + const toLoadPageId = yield call(initiateEditorApplicationAndPages, payload); + if (!toLoadPageId) return; const { id: applicationId, name }: ApplicationPayload = yield select( getCurrentApplication, @@ -348,7 +338,7 @@ function* initializeEditorSaga( ); yield all([ - call(initiateEditorActions, applicationId), + call(initiateEditorActions, toLoadPageId, applicationId), call(initiatePluginsAndDatasources), call(populatePageDSLsSaga), ]); @@ -430,15 +420,16 @@ export function* initializeAppViewerSaga( [ fetchActionsForView({ applicationId }), fetchJSCollectionsForView({ applicationId }), - fetchPublishedPage(toLoadPageId, true, true), fetchSelectedAppThemeAction(applicationId), fetchAppThemesAction(applicationId), + fetchPublishedPage(toLoadPageId, true, true), ], [ ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS, ReduxActionTypes.FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS, ReduxActionTypes.FETCH_APP_THEMES_SUCCESS, ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS, + fetchPublishedPageSuccess().type, ], [ ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR, @@ -451,34 +442,7 @@ export function* initializeAppViewerSaga( 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(fetchAllPageEntityCompletion([executePageLoadActions()])); yield put(fetchCommentThreadsInit()); diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index d337f1faf3..d093efd39b 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -24,6 +24,9 @@ import { setLastUpdatedTime, ClonePageActionPayload, CreatePageActionPayload, + generateTemplateError, + generateTemplateSuccess, + fetchAllPageEntityCompletion, } from "actions/pageActions"; import PageApi, { ClonePageRequest, @@ -76,6 +79,8 @@ import { fetchActionsForPage, setActionsToExecuteOnPageLoad, setJSActionsToExecuteOnPageLoad, + fetchActionsForPageSuccess, + fetchActionsForPageError, } from "actions/pluginActionActions"; import { UrlDataState } from "reducers/entityReducers/appReducer"; import { APP_MODE } from "entities/App"; @@ -93,10 +98,7 @@ import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import DEFAULT_TEMPLATE from "templates/default"; import { GenerateTemplatePageRequest } from "api/PageApi"; -import { - generateTemplateError, - generateTemplateSuccess, -} from "actions/pageActions"; + import { getAppMode } from "selectors/applicationSelectors"; import { setCrudInfoModalData } from "actions/crudInfoModalActions"; import { selectMultipleWidgetsAction } from "actions/widgetSelectionActions"; @@ -105,11 +107,16 @@ import { getFirstTimeUserOnboardingApplicationId, inGuidedTour, } from "selectors/onboardingSelectors"; -import { fetchJSCollectionsForPage } from "actions/jsActionActions"; +import { + fetchJSCollectionsForPage, + fetchJSCollectionsForPageSuccess, + fetchJSCollectionsForPageError, +} from "actions/jsActionActions"; import WidgetFactory from "utils/WidgetFactory"; import { toggleShowDeviationDialog } from "actions/onboardingActions"; import { builderURL, generateTemplateURL } from "RouteBuilder"; +import { failFastApiCalls } from "./InitSagas"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -223,12 +230,15 @@ export function* handleFetchedPage({ // set current page yield put(updateCurrentPage(pageId, pageSlug)); // dispatch fetch page success - yield put( - fetchPageSuccess( - // Execute page load actions post page load - isFirstLoad ? [] : [executePageLoadActions()], - ), - ); + yield put(fetchPageSuccess()); + + /* Currently, All Actions are fetched in initSagas and on pageSwitch we only fetch page + */ + // Hence, if is not isFirstLoad then trigger evaluation with execute pageLoad action + if (!isFirstLoad) { + yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); + } + // Sets last updated time yield put(setLastUpdatedTime(lastUpdatedTime)); const extractedDSL = extractCurrentDSL(fetchPageResponse); @@ -321,15 +331,16 @@ export function* fetchPublishedPageSaga( // set current page yield put(updateCurrentPage(pageId, response.data.slug)); + // dispatch fetch page success + yield put(fetchPublishedPageSuccess()); + + /* Currently, All Actions are fetched in initSagas and on pageSwitch we only fetch page + */ + // Hence, if is not isFirstLoad then trigger evaluation with execute pageLoad action if (!firstLoad) { - // dispatch fetch page success - yield put( - fetchPublishedPageSuccess( - // Execute page load actions post published page eval - [executePageLoadActions()], - ), - ); + yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); } + PerformanceTracker.stopAsyncTracking( PerformanceTransactionName.FETCH_PAGE_API, ); @@ -977,6 +988,15 @@ export function* generateTemplatePageSaga( const isValidResponse: boolean = yield validateResponse(response); if (isValidResponse) { const pageId = response.data.page.id; + + yield put( + generateTemplateSuccess({ + page: response.data.page, + isNewPage: !request.pageId, + // if pageId if not defined, that means a new page is generated. + }), + ); + yield handleFetchedPage({ fetchPageResponse: { data: response.data.page, @@ -986,16 +1006,30 @@ export function* generateTemplatePageSaga( isFirstLoad: true, }); - // TODO : Add this to onSuccess (Redux Action) - yield put( - generateTemplateSuccess({ - page: response.data.page, - isNewPage: !request.pageId, // if pageId if not defined, that means a new page is generated. - }), + // trigger evaluation after completion of page success & fetch actions for page + fetch jsobject for page + + const triggersAfterPageFetch = [ + fetchActionsForPage(pageId), + fetchJSCollectionsForPage(pageId), + ]; + + const afterActionsFetch = yield failFastApiCalls( + triggersAfterPageFetch, + [ + fetchActionsForPageSuccess([]).type, + fetchJSCollectionsForPageSuccess([]).type, + ], + [ + fetchActionsForPageError().type, + fetchJSCollectionsForPageError().type, + ], ); - // TODO : Add this to onSuccess (Redux Action) - yield put(fetchActionsForPage(pageId, [executePageLoadActions()])); - // TODO : Add it to onSuccessCallback + + if (!afterActionsFetch) { + throw new Error("Failed generating template"); + } + yield put(fetchAllPageEntityCompletion([executePageLoadActions()])); + history.replace( builderURL({ pageSlug: response.data.page.slug, diff --git a/app/client/src/workers/evaluation.worker.ts b/app/client/src/workers/evaluation.worker.ts index 49559af3ab..bbf9685935 100644 --- a/app/client/src/workers/evaluation.worker.ts +++ b/app/client/src/workers/evaluation.worker.ts @@ -149,7 +149,6 @@ ctx.addEventListener( evaluationOrder = dataTreeEvaluator.sortedDependencies; dataTree = dataTreeResponse.evalTree; jsUpdates = dataTreeResponse.jsUpdates; - evalMetaUpdates = dataTreeResponse.evalMetaUpdates; dataTree = dataTree && JSON.parse(JSON.stringify(dataTree)); } else { if (dataTreeEvaluator && !isEmpty(allActionValidationConfig)) { @@ -166,7 +165,11 @@ ctx.addEventListener( unEvalUpdates = updateResponse.unEvalUpdates; dataTree = JSON.parse(JSON.stringify(dataTreeEvaluator.evalTree)); jsUpdates = updateResponse.jsUpdates; - evalMetaUpdates = updateResponse.evalMetaUpdates; + // evalMetaUpdates can have moment object as value which will cause DataCloneError + // hence, stringify and parse to avoid such errors + evalMetaUpdates = JSON.parse( + JSON.stringify(updateResponse.evalMetaUpdates), + ); } dependencies = dataTreeEvaluator.inverseDependencyMap; errors = dataTreeEvaluator.errors;