diff --git a/app/client/src/api/ActionAPI.tsx b/app/client/src/api/ActionAPI.tsx index ad92d83189..e01aa450c1 100644 --- a/app/client/src/api/ActionAPI.tsx +++ b/app/client/src/api/ActionAPI.tsx @@ -26,6 +26,7 @@ export interface QueryConfig { export interface ActionCreatedResponse extends ApiResponse { actionId: string + dynamicBindingMap: Record } export interface ActionUpdatedResponse extends ActionCreatedResponse { @@ -34,7 +35,7 @@ export interface ActionUpdatedResponse extends ActionCreatedResponse { export interface ExecuteActionRequest extends APIRequest { actionId: string - dynamicBindingMap: Record + dynamicBindingMap: Record } export interface ExecuteActionResponse extends ApiResponse { diff --git a/app/client/src/api/PageApi.tsx b/app/client/src/api/PageApi.tsx index 26fbe75664..3794e5f8d9 100644 --- a/app/client/src/api/PageApi.tsx +++ b/app/client/src/api/PageApi.tsx @@ -13,9 +13,13 @@ export interface SavePageRequest { pageWidget: ContainerWidgetProps; } +export interface PageLayout { + dsl: ContainerWidgetProps + actions: PageAction[] +} + export interface PageResponse extends ApiResponse { - pageWidget: ContainerWidgetProps; - pageActions: PageAction[] + layout: PageLayout } export interface SavePageResponse { diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 4a8e5ff398..0a72579463 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -1,9 +1,10 @@ // import ContainerWidget from "../widgets/ContainerWidget" import { IWidgetProps, WidgetCardProps } from "../widgets/BaseWidget" import { ExecuteActionResponse } from '../api/ActionAPI'; +import { PageAction } from './ActionConstants'; export type ReduxActionType = - | "UPDATE_CANVAS" + | "LOAD_CANVAS_WIDGETS" | "FETCH_CANVAS" | "CLEAR_CANVAS" | "DROP_WIDGET_CANVAS" @@ -23,9 +24,11 @@ export type ReduxActionType = | "LOAD_WIDGET_CONFIG" | "LOAD_API_RESPONSE" | "LOAD_QUERY_RESPONSE" + | "EXECUTE_ACTION" + | "LOAD_CANVAS_ACTIONS" export const ReduxActionTypes: { [id: string]: ReduxActionType } = { - UPDATE_CANVAS: "UPDATE_CANVAS", + LOAD_CANVAS_WIDGETS: "LOAD_CANVAS_WIDGETS", FETCH_CANVAS: "FETCH_CANVAS", CLEAR_CANVAS: "CLEAR_CANVAS", FETCH_PAGE: "FETCH_PAGE", @@ -44,7 +47,9 @@ export const ReduxActionTypes: { [id: string]: ReduxActionType } = { ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET", REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET", LOAD_API_RESPONSE: "LOAD_API_RESPONSE", - LOAD_QUERY_RESPONSE: "LOAD_QUERY_RESPONSE" + LOAD_QUERY_RESPONSE: "LOAD_QUERY_RESPONSE", + EXECUTE_ACTION: "EXECUTE_ACTION", + LOAD_CANVAS_ACTIONS: "LOAD_CANVAS_ACTIONS" } export interface ReduxAction { @@ -52,7 +57,7 @@ export interface ReduxAction { payload: T; } -export interface LoadCanvasPayload { +export interface LoadCanvasWidgetsPayload { pageWidgetId: string; widgets: { [widgetId: string]: IWidgetProps }; } diff --git a/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx b/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx index 50c694f71d..e397536c49 100644 --- a/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx +++ b/app/client/src/normalizers/CanvasWidgetsNormalizer.tsx @@ -9,7 +9,7 @@ widgetSchema.define({ children: [widgetSchema] }); class CanvasWidgetsNormalizer { static normalize(pageResponse: PageResponse): { entities: any, result: any } { - return normalize(pageResponse.pageWidget, widgetSchema) + return normalize(pageResponse.layout.dsl, widgetSchema) } static denormalize(pageWidgetId: string, entities: any): ContainerWidgetProps { diff --git a/app/client/src/reducers/entityReducers/actionsReducer.tsx b/app/client/src/reducers/entityReducers/actionsReducer.tsx new file mode 100644 index 0000000000..4e0bc7d484 --- /dev/null +++ b/app/client/src/reducers/entityReducers/actionsReducer.tsx @@ -0,0 +1,28 @@ +import { createReducer } from "../../utils/AppsmithUtils" +import { + ReduxActionTypes, + ReduxAction, +} from "../../constants/ReduxActionConstants" +import _ from "lodash" +import { ActionCreatedResponse } from '../../api/ActionAPI' +import { PageAction } from '../../constants/ActionConstants'; + +const initialState: ActionDataState = { + +} + +export interface ActionDataState { + [name: string]: ActionCreatedResponse +} + +const actionsReducer = createReducer(initialState, { + [ReduxActionTypes.LOAD_CANVAS_ACTIONS]: ( + state: ActionDataState, + action: ReduxAction + ) => { + const actionMap = _.mapKeys(action.payload, (action: PageAction) => { return action.actionId }) + return { ...state, ...actionMap } + } +}) + +export default actionsReducer diff --git a/app/client/src/reducers/entityReducers/apiDataReducer.tsx b/app/client/src/reducers/entityReducers/apiDataReducer.tsx index 80d539f10e..ccb0d16d20 100644 --- a/app/client/src/reducers/entityReducers/apiDataReducer.tsx +++ b/app/client/src/reducers/entityReducers/apiDataReducer.tsx @@ -6,17 +6,17 @@ import { } from "../../constants/ReduxActionConstants" import { ExecuteActionResponse } from '../../api/ActionAPI' -const initialState: APIDataReducer = { +const initialState: APIDataState = { } -export interface APIDataReducer { +export interface APIDataState { [name: string]: ExecuteActionResponse } const apiDataReducer = createReducer(initialState, { [ReduxActionTypes.LOAD_API_RESPONSE]: ( - state: APIDataReducer, + state: APIDataState, action: ReduxAction ) => { return { ...state, [action.payload.actionId]: action.payload } diff --git a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx index 1d94aef077..c5ec24ed5e 100644 --- a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx +++ b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx @@ -1,7 +1,7 @@ import { createReducer } from "../../utils/AppsmithUtils" import { ReduxActionTypes, - LoadCanvasPayload, + LoadCanvasWidgetsPayload, ReduxAction } from "../../constants/ReduxActionConstants" import { IWidgetProps } from "../../widgets/BaseWidget" @@ -17,7 +17,7 @@ export interface IFlattenedWidgetProps extends IWidgetProps { const canvasWidgetsReducer = createReducer(initialState, { [ReduxActionTypes.UPDATE_CANVAS]: ( state: CanvasWidgetsReduxState, - action: ReduxAction + action: ReduxAction ) => { return { ...action.payload.widgets } }, diff --git a/app/client/src/reducers/entityReducers/index.tsx b/app/client/src/reducers/entityReducers/index.tsx index ad7f0d492c..a87cb372f7 100644 --- a/app/client/src/reducers/entityReducers/index.tsx +++ b/app/client/src/reducers/entityReducers/index.tsx @@ -3,6 +3,13 @@ import canvasWidgetsReducer from "./canvasWidgetsReducer" import apiDataReducer from './apiDataReducer'; import queryDataReducer from './queryDataReducer'; import widgetConfigReducer from './widgetConfigReducer.tsx'; +import actionsReducer from './actionsReducer'; -const entityReducer = combineReducers({ canvasWidgets: canvasWidgetsReducer, apiData: apiDataReducer, queryData: queryDataReducer, widgetConfig: widgetConfigReducer }) +const entityReducer = combineReducers({ + canvasWidgets: canvasWidgetsReducer, + apiData: apiDataReducer, + queryData: queryDataReducer, + widgetConfig: widgetConfigReducer, + actions: actionsReducer +}) export default entityReducer diff --git a/app/client/src/reducers/entityReducers/queryDataReducer.tsx b/app/client/src/reducers/entityReducers/queryDataReducer.tsx index d46dcfb33a..e442724252 100644 --- a/app/client/src/reducers/entityReducers/queryDataReducer.tsx +++ b/app/client/src/reducers/entityReducers/queryDataReducer.tsx @@ -7,17 +7,17 @@ import { } from "../../constants/ReduxActionConstants" import { ExecuteActionResponse } from '../../api/ActionAPI' -const initialState: QueryDataReducer = { +const initialState: QueryDataState = { } -export interface QueryDataReducer { +export interface QueryDataState { [name: string]: ExecuteActionResponse } const queryDataReducer = createReducer(initialState, { [ReduxActionTypes.LOAD_API_RESPONSE]: ( - state: QueryDataReducer, + state: QueryDataState, action: ReduxAction ) => { return { ...state, [action.payload.actionId]: action.payload } diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 4a031876e4..3c9db0c301 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -6,6 +6,9 @@ import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducer" import { WidgetCardsPaneReduxState } from "./uiReducers/widgetCardsPaneReducer" import { EditorHeaderReduxState } from "./uiReducers/editorHeaderReducer" import { EditorReduxState } from "./uiReducers/editorReducer" +import { APIDataState } from './entityReducers/apiDataReducer'; +import { QueryDataState } from './entityReducers/queryDataReducer'; +import { ActionDataState } from './entityReducers/actionsReducer'; const appReducer = combineReducers({ entities: entityReducer, @@ -22,6 +25,9 @@ export interface AppState { editor: EditorReduxState } entities: { - canvasWidgets: CanvasWidgetsReduxState + canvasWidgets: CanvasWidgetsReduxState, + apiData: APIDataState, + queryData: QueryDataState, + actions: ActionDataState } } diff --git a/app/client/src/reducers/uiReducers/canvasReducer.tsx b/app/client/src/reducers/uiReducers/canvasReducer.tsx index e782d05478..6a936523cb 100644 --- a/app/client/src/reducers/uiReducers/canvasReducer.tsx +++ b/app/client/src/reducers/uiReducers/canvasReducer.tsx @@ -1,7 +1,7 @@ import { createReducer } from "../../utils/AppsmithUtils" import { ReduxActionTypes, - LoadCanvasPayload, + LoadCanvasWidgetsPayload, ReduxAction } from "../../constants/ReduxActionConstants" @@ -12,7 +12,7 @@ const initialState: CanvasReduxState = { const canvasReducer = createReducer(initialState, { [ReduxActionTypes.UPDATE_CANVAS]: ( state: CanvasReduxState, - action: ReduxAction + action: ReduxAction ) => { return { pageWidgetId: action.payload.pageWidgetId } } diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx index de36f2bf9b..e98b505e61 100644 --- a/app/client/src/reducers/uiReducers/editorReducer.tsx +++ b/app/client/src/reducers/uiReducers/editorReducer.tsx @@ -2,7 +2,7 @@ import { createReducer } from "../../utils/AppsmithUtils" import { ReduxActionTypes, ReduxAction, - LoadCanvasPayload, + LoadCanvasWidgetsPayload, LoadWidgetCardsPanePayload } from "../../constants/ReduxActionConstants" import { WidgetCardProps, IWidgetProps } from "../../widgets/BaseWidget" @@ -25,7 +25,7 @@ const editorReducer = createReducer(initialState, { }, [ReduxActionTypes.UPDATE_CANVAS]: ( state: EditorReduxState, - action: ReduxAction + action: ReduxAction ) => { return { pageWidgetId: action.payload.pageWidgetId } } diff --git a/app/client/src/sagas/ActionSagas.tsx b/app/client/src/sagas/ActionSagas.tsx new file mode 100644 index 0000000000..418d252b2a --- /dev/null +++ b/app/client/src/sagas/ActionSagas.tsx @@ -0,0 +1,59 @@ +import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer" +import { ReduxActionTypes, ReduxAction } from "../constants/ReduxActionConstants" +import PageApi, { PageResponse, PageRequest } from "../api/PageApi" +import { call, put, takeEvery, select, all } from "redux-saga/effects" +import { RenderModes } from "../constants/WidgetConstants" +import { APIActionPayload, QueryActionPayload, PageAction } from '../constants/ActionConstants'; +import ActionAPI, { ActionCreatedResponse } from '../api/ActionAPI'; +import { AppState } from '../reducers'; +import {JSONPath} from 'jsonpath-plus'; +import _ from "lodash" + +const getDataTree = (state: AppState) => { + return state.entities +} + +const getAction = (state: AppState, actionId: string): ActionCreatedResponse => { + return state.entities.actions[actionId] +} + +export function* evaluateJSONPath(jsonPath: string): any { + const dataTree = yield select(getDataTree) + const result = JSONPath({path: jsonPath, json: dataTree}) + return result +} + +export function* executeAPIAction(apiAction: APIActionPayload) { + const api: PageAction = yield select(getAction, apiAction.apiId) + const responses: any = yield all(api.dynamicBindings.map((jsonPath: string) => { return call(evaluateJSONPath, jsonPath)})) + const dynamicBindingMap: Record = _.keyBy(responses, (response: string, index: number) => { return api.dynamicBindings[index] }) + yield ActionAPI.executeAction({ actionId: apiAction.apiId, dynamicBindingMap: dynamicBindingMap }) +} + +export function* executeQueryAction(queryAction: QueryActionPayload) { + const query: PageAction = yield select(getAction, queryAction.queryId) + const responses: any = yield all(query.dynamicBindings.map((jsonPath: string) => { return call(evaluateJSONPath, jsonPath)})) + const dynamicBindingMap: Record = _.keyBy(responses, (response: string, index: number) => { return query.dynamicBindings[index] }) + yield ActionAPI.executeAction({ actionId: query.actionId, dynamicBindingMap: dynamicBindingMap }) +} + +export function* executeAction(pageRequestAction: ReduxAction) { + const pageRequest = pageRequestAction.payload + try { + const pageResponse: PageResponse = yield call(PageApi.fetchPage, pageRequest) + if (pageRequest.renderMode === RenderModes.CANVAS) { + const normalizedResponse = CanvasWidgetsNormalizer.normalize(pageResponse) + const payload = { + pageWidgetId: normalizedResponse.result, + widgets: normalizedResponse.entities.canvasWidgets + } + yield put({ type: ReduxActionTypes.UPDATE_CANVAS, payload }) + } + } catch(err){ + //TODO(abhinav): REFACTOR THIS + } +} + +export function* watchExecuteAction() { + yield takeEvery(ReduxActionTypes.EXECUTE_ACTION, executeAction) +} diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index 20c22b23b2..da1a589f75 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -1,7 +1,7 @@ import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer" -import { ReduxActionTypes, ReduxAction } from "../constants/ReduxActionConstants" +import { ReduxActionTypes, ReduxAction, LoadCanvasWidgetsPayload } from "../constants/ReduxActionConstants" import PageApi, { PageResponse, PageRequest } from "../api/PageApi" -import { call, put, takeEvery } from "redux-saga/effects" +import { call, put, takeEvery, all } from "redux-saga/effects" import { RenderModes } from "../constants/WidgetConstants" export function* fetchPageSaga(pageRequestAction: ReduxAction) { @@ -10,11 +10,14 @@ export function* fetchPageSaga(pageRequestAction: ReduxAction) { const pageResponse: PageResponse = yield call(PageApi.fetchPage, pageRequest) if (pageRequest.renderMode === RenderModes.CANVAS) { const normalizedResponse = CanvasWidgetsNormalizer.normalize(pageResponse) - const payload = { + const canvasWidgetsPayload: LoadCanvasWidgetsPayload = { pageWidgetId: normalizedResponse.result, widgets: normalizedResponse.entities.canvasWidgets } - yield put({ type: ReduxActionTypes.UPDATE_CANVAS, payload }) + yield all([ + put({ type: ReduxActionTypes.UPDATE_CANVAS, canvasWidgetsPayload }), + put({ type: ReduxActionTypes.LOAD_CANVAS_ACTIONS, payload: pageResponse.layout.actions }) + ]) } } catch(err){ //TODO(abhinav): REFACTOR THIS diff --git a/app/client/src/sagas/index.tsx b/app/client/src/sagas/index.tsx index c86314dc04..cd917cb614 100644 --- a/app/client/src/sagas/index.tsx +++ b/app/client/src/sagas/index.tsx @@ -1,7 +1,8 @@ -import { all } from "redux-saga/effects" +import { all, fork, spawn } from "redux-saga/effects" import { watchFetchPage } from "../sagas/PageSagas" import { fetchWidgetCardsSaga } from './WidgetCardsPaneSagas' +import { watchExecuteAction } from './ActionSagas'; export function* rootSaga() { - yield all([watchFetchPage(), fetchWidgetCardsSaga()]) + yield all([ spawn(watchFetchPage), spawn(fetchWidgetCardsSaga), spawn(watchExecuteAction)]) }