added actions & executor sagas

This commit is contained in:
Nikhil Nandagopal 2019-09-13 15:26:11 +05:30
parent cbc23349c7
commit f905417242
15 changed files with 142 additions and 28 deletions

View File

@ -26,6 +26,7 @@ export interface QueryConfig {
export interface ActionCreatedResponse extends ApiResponse {
actionId: string
dynamicBindingMap: Record<string, string>
}
export interface ActionUpdatedResponse extends ActionCreatedResponse {
@ -34,7 +35,7 @@ export interface ActionUpdatedResponse extends ActionCreatedResponse {
export interface ExecuteActionRequest extends APIRequest {
actionId: string
dynamicBindingMap: Record<string, string>
dynamicBindingMap: Record<string, any>
}
export interface ExecuteActionResponse extends ApiResponse {

View File

@ -13,9 +13,13 @@ export interface SavePageRequest {
pageWidget: ContainerWidgetProps<any>;
}
export interface PageLayout {
dsl: ContainerWidgetProps<any>
actions: PageAction[]
}
export interface PageResponse extends ApiResponse {
pageWidget: ContainerWidgetProps<any>;
pageActions: PageAction[]
layout: PageLayout
}
export interface SavePageResponse {

View File

@ -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<T> {
@ -52,7 +57,7 @@ export interface ReduxAction<T> {
payload: T;
}
export interface LoadCanvasPayload {
export interface LoadCanvasWidgetsPayload {
pageWidgetId: string;
widgets: { [widgetId: string]: IWidgetProps };
}

View File

@ -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<any> {

View File

@ -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<PageAction[]>
) => {
const actionMap = _.mapKeys(action.payload, (action: PageAction) => { return action.actionId })
return { ...state, ...actionMap }
}
})
export default actionsReducer

View File

@ -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<LoadAPIResponsePayload>
) => {
return { ...state, [action.payload.actionId]: action.payload }

View File

@ -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<LoadCanvasPayload>
action: ReduxAction<LoadCanvasWidgetsPayload>
) => {
return { ...action.payload.widgets }
},

View File

@ -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

View File

@ -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<LoadQueryResponsePayload>
) => {
return { ...state, [action.payload.actionId]: action.payload }

View File

@ -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
}
}

View File

@ -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<LoadCanvasPayload>
action: ReduxAction<LoadCanvasWidgetsPayload>
) => {
return { pageWidgetId: action.payload.pageWidgetId }
}

View File

@ -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<LoadCanvasPayload>
action: ReduxAction<LoadCanvasWidgetsPayload>
) => {
return { pageWidgetId: action.payload.pageWidgetId }
}

View File

@ -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<string, any> = _.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<string, any> = _.keyBy(responses, (response: string, index: number) => { return query.dynamicBindings[index] })
yield ActionAPI.executeAction({ actionId: query.actionId, dynamicBindingMap: dynamicBindingMap })
}
export function* executeAction(pageRequestAction: ReduxAction<PageRequest>) {
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)
}

View File

@ -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<PageRequest>) {
@ -10,11 +10,14 @@ export function* fetchPageSaga(pageRequestAction: ReduxAction<PageRequest>) {
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

View File

@ -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)])
}