Merge branch 'feature/actions' into 'release'

Feature/actions

See merge request theappsmith/internal-tools-client!20
This commit is contained in:
Abhinav Jha 2019-09-13 13:30:51 +00:00
commit ebdcfa0819
39 changed files with 491 additions and 146 deletions

5
app/client/.huskyrc Normal file
View File

@ -0,0 +1,5 @@
{
"hooks": {
"pre-commit": "yarn precommit"
}
}

View File

@ -28,6 +28,7 @@
"axios": "^0.18.0",
"flow-bin": "^0.91.0",
"fontfaceobserver": "^2.1.0",
"jsonpath-plus": "^1.0.0",
"husky": "^3.0.5",
"lint-staged": "^9.2.5",
"lodash": "^4.17.11",
@ -56,14 +57,8 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"precommit": "lint-staged",
"flow": "flow"
},
"husky": {
"hooks": {
"pre-commit": "yarn precommit"
}
},
"resolutions": {
"jest": "24.8.0"
},
@ -84,5 +79,10 @@
"eslint-config-react": "^1.1.7",
"eslint-plugin-prettier": "^3.1.0",
"redux-devtools": "^3.5.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}

View File

@ -1,14 +1,17 @@
import { ReduxAction, ActionTypes } from "../constants/ActionConstants";
import { PageRequest } from "../api/PageApi";
import { RenderMode } from "../constants/WidgetConstants";
import { WidgetProps } from "../widgets/BaseWidget";
import {
ReduxActionTypes,
ReduxAction,
} from "../constants/ReduxActionConstants";
export const fetchPage = (
pageId: string,
renderMode: RenderMode,
): ReduxAction<PageRequest> => {
return {
type: ActionTypes.FETCH_PAGE,
type: ReduxActionTypes.FETCH_PAGE,
payload: {
pageId: pageId,
renderMode: renderMode,
@ -21,7 +24,7 @@ export const addWidget = (
widget: WidgetProps,
): ReduxAction<{ pageId: string; widget: WidgetProps }> => {
return {
type: ActionTypes.ADD_PAGE_WIDGET,
type: ReduxActionTypes.ADD_PAGE_WIDGET,
payload: {
pageId,
widget,
@ -34,7 +37,7 @@ export const removeWidget = (
widgetId: string,
): ReduxAction<{ pageId: string; widgetId: string }> => {
return {
type: ActionTypes.REMOVE_PAGE_WIDGET,
type: ReduxActionTypes.REMOVE_PAGE_WIDGET,
payload: {
pageId,
widgetId,

View File

@ -1,15 +1,15 @@
import { ActionTypes } from "../constants/ActionConstants";
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
import { WidgetCardProps } from "../widgets/BaseWidget";
export const fetchWidgetCards = () => {
return {
type: ActionTypes.FETCH_WIDGET_CARDS,
type: ReduxActionTypes.FETCH_WIDGET_CARDS,
};
};
export const errorFetchingWidgetCards = (error: any) => {
return {
type: ActionTypes.ERROR_FETCHING_WIDGET_CARDS,
type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS,
error,
};
};
@ -18,7 +18,7 @@ export const successFetchingWidgetCards = (cards: {
[id: string]: WidgetCardProps[];
}) => {
return {
type: ActionTypes.SUCCESS_FETCHING_WIDGET_CARDS,
type: ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS,
cards,
};
};

View File

@ -0,0 +1,71 @@
import Api, { HttpMethod } from "./Api"
import { ApiResponse } from "./ApiResponses"
import { APIRequest } from './ApiRequests';
export interface CreateActionRequest<T> extends APIRequest {
resourceId: string
actionConfiguration: T
}
export interface UpdateActionRequest<T> extends CreateActionRequest<T> {
actionId: string
}
export interface APIConfig {
requestHeaders: Record<string, string>
method: HttpMethod
path: string
APIName: string
body: JSON
queryParams: Record<string, string>
}
export interface QueryConfig {
queryString: string
}
export interface ActionCreatedResponse extends ApiResponse {
actionId: string
dynamicBindingMap: Record<string, string>
}
export interface ActionUpdatedResponse extends ActionCreatedResponse {
}
export interface ExecuteActionRequest extends APIRequest {
actionId: string
dynamicBindingMap: Record<string, any>
}
export interface ExecuteActionResponse extends ApiResponse {
actionId: string
data: any
}
class ActionAPI extends Api {
static url = "/actions"
static createAPI(createAPI: CreateActionRequest<APIConfig>): Promise<ActionCreatedResponse> {
return Api.post(ActionAPI.url, createAPI)
}
static updateAPI(updateAPI: UpdateActionRequest<APIConfig>): Promise<ActionUpdatedResponse> {
return Api.post(ActionAPI.url, updateAPI)
}
static createQuery(createQuery: CreateActionRequest<QueryConfig>): Promise<ActionCreatedResponse> {
return Api.post(ActionAPI.url, createQuery)
}
static updateQuery(updateQuery: UpdateActionRequest<QueryConfig>): Promise<ActionUpdatedResponse> {
return Api.post(ActionAPI.url, updateQuery)
}
static executeAction(executeAction: ExecuteActionRequest): Promise<ActionUpdatedResponse> {
return Api.post(ActionAPI.url, executeAction)
}
}
export default ActionAPI

View File

@ -41,7 +41,7 @@ axiosInstance.interceptors.response.use(
);
class Api {
static get(url: string, queryParams: any) {
static get(url: string, queryParams?: any) {
return axiosInstance.get(
url + this.convertObjectToQueryParams(queryParams),
);
@ -66,4 +66,6 @@ class Api {
}
}
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
export default Api;

View File

@ -1,9 +1,11 @@
import { ContentType, DataType } from "../constants/ApiConstants";
export interface ApiHeaders {
export interface APIHeaders {
Accept: ContentType;
"Content-Type": ContentType;
dataType: DataType;
}
export {};
export interface APIRequest {
requestId?: string;
}

View File

@ -1,7 +1,8 @@
export type ApiErrorCodes = "INVALID_REQUEST" | "UNKNOWN";
export type APIResponseCode = "SUCCESS" | "UNKNOWN";
export interface ResponseMeta {
errorCode?: ApiErrorCodes;
responseCode: APIResponseCode;
message?: string;
}
export interface ApiResponse {

View File

@ -2,6 +2,7 @@ import Api from "./Api";
import { ContainerWidgetProps } from "../widgets/ContainerWidget";
import { ApiResponse } from "./ApiResponses";
import { RenderMode } from "../constants/WidgetConstants";
import { PageAction } from "../constants/ActionConstants";
export interface PageRequest {
pageId: string;
@ -12,8 +13,13 @@ export interface SavePageRequest {
pageWidget: ContainerWidgetProps<any>;
}
export interface PageLayout {
dsl: ContainerWidgetProps<any>;
actions: PageAction[];
}
export interface PageResponse extends ApiResponse {
pageWidget: ContainerWidgetProps<any>;
layout: PageLayout;
}
export interface SavePageResponse {

View File

@ -9,7 +9,7 @@ export interface WidgetCardsPaneResponse {
class WidgetCardsPaneApi extends Api {
static url = "/widgetCards";
static fetchWidgetCards(): Promise<WidgetCardsPaneResponse> {
return Api.get(WidgetCardsPaneApi.url, {});
return Api.get(WidgetCardsPaneApi.url);
}
}

View File

@ -1,65 +1,75 @@
// import ContainerWidget from "../widgets/ContainerWidget"
import { WidgetProps, WidgetCardProps } from "../widgets/BaseWidget";
import { AlertType, MessageIntent } from "../widgets/AlertWidget";
export type EventType =
| "ON_CLICK"
| "ON_HOVER"
| "ON_TOGGLE"
| "ON_LOAD"
| "ON_TEXT_CHANGE"
| "ON_SUBMIT"
| "ON_CHECK_CHANGE"
| "ON_SELECT"
| "ON_DATE_SELECTED"
| "ON_DATE_RANGE_SELECTED"
export type ActionType =
| "UPDATE_CANVAS"
| "FETCH_CANVAS"
| "CLEAR_CANVAS"
| "DROP_WIDGET_CANVAS"
| "REMOVE_WIDGET_CANVAS"
| "LOAD_WIDGET_PANE"
| "FETCH_PAGE"
| "ZOOM_IN_CANVAS"
| "ZOOM_OUT_CANVAS"
| "PUBLISH"
| "UNDO_CANVAS_ACTION"
| "REDO_CANVAS_ACTION"
| "FETCH_WIDGET_CARDS"
| "SUCCESS_FETCHING_WIDGET_CARDS"
| "ERROR_FETCHING_WIDGET_CARDS"
| "ADD_PAGE_WIDGET"
| "REMOVE_PAGE_WIDGET"
| "LOAD_WIDGET_CONFIG";
| "API"
| "QUERY"
| "NAVIGATION"
| "ALERT"
| "JS_FUNCTION"
| "SET_VALUE"
| "DOWNLOAD"
export const ActionTypes: { [id: string]: ActionType } = {
UPDATE_CANVAS: "UPDATE_CANVAS",
FETCH_CANVAS: "FETCH_CANVAS",
CLEAR_CANVAS: "CLEAR_CANVAS",
FETCH_PAGE: "FETCH_PAGE",
DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS",
REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS",
LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE",
ZOOM_IN_CANVAS: "ZOOM_IN_CANVAS",
ZOOM_OUT_CANVAS: "ZOOM_OUT_CANVAS",
UNDO_CANVAS_ACTION: "UNDO_CANVAS_ACTION",
REDO_CANVAS_ACTION: "REDO_CANVAS_ACTION",
LOAD_WIDGET_CONFIG: "LOAD_WIDGET_CONFIG",
PUBLISH: "PUBLISH",
FETCH_WIDGET_CARDS: "FETCH_WIDGET_CARDS",
SUCCESS_FETCHING_WIDGET_CARDS: "SUCCESS_FETCHING_WIDGET_CARDS",
ERROR_FETCHING_WIDGET_CARDS: "ERROR_FETCHING_WIDGET_CARDS",
ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET",
REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET",
};
export interface ReduxAction<T> {
type: ActionType;
payload: T;
export interface ActionPayload {
actionType: ActionType
contextParams: Record<string, string>
}
export interface LoadCanvasPayload {
pageWidgetId: string;
widgets: { [widgetId: string]: WidgetProps };
export interface APIActionPayload extends ActionPayload {
apiId: string
}
export interface LoadWidgetConfigPayload {
[widgetId: string]: WidgetProps;
export interface QueryActionPayload extends ActionPayload {
queryId: string
}
export interface LoadWidgetPanePayload {
widgets: WidgetProps[];
export type NavigationType = "NEW_TAB" | "INLINE"
export interface NavigateActionPayload extends ActionPayload {
pageUrl: string
navigationType: NavigationType
}
export interface LoadWidgetCardsPanePayload {
cards: { [id: string]: WidgetCardProps[] };
export interface ShowAlertActionPayload extends ActionPayload {
header: string
message: string
alertType: AlertType
intent: MessageIntent
}
export interface SetValueActionPayload extends ActionPayload {
header: string
message: string
alertType: AlertType
intent: MessageIntent
}
export interface ExecuteJSActionPayload extends ActionPayload {
jsFunctionId: string
}
export type DownloadFiletype = "CSV" | "XLS" | "JSON" | "TXT"
export interface DownloadDataActionPayload extends ActionPayload {
data: JSON
fileName: string
fileType: DownloadFiletype
}
export interface PageAction {
actionId: string
actionType: ActionType
actionName: string
dynamicBindings: string[]
}

View File

@ -1,4 +1,4 @@
import { ApiHeaders } from "../api/ApiRequests";
import { APIHeaders } from "../api/ApiRequests";
export type DataType = "json" | "xml";
export type ContentType =
@ -13,7 +13,7 @@ export const STAGE_BASE_URL =
"https://14157cb0-190f-4082-a791-886a8df05930.mock.pstmn.io";
export const BASE_URL = MOCK_BASE_URL;
export const REQUEST_TIMEOUT_MS = 2000;
export const REQUEST_HEADERS: ApiHeaders = {
export const REQUEST_HEADERS: APIHeaders = {
Accept: "application/json",
"Content-Type": "application/json",
dataType: "json",

View File

@ -0,0 +1,78 @@
// import ContainerWidget from "../widgets/ContainerWidget"
import { WidgetProps, WidgetCardProps } from "../widgets/BaseWidget";
import { ExecuteActionResponse } from "../api/ActionAPI";
export type ReduxActionType =
| "LOAD_CANVAS_WIDGETS"
| "FETCH_CANVAS"
| "CLEAR_CANVAS"
| "DROP_WIDGET_CANVAS"
| "REMOVE_WIDGET_CANVAS"
| "LOAD_WIDGET_PANE"
| "FETCH_PAGE"
| "ZOOM_IN_CANVAS"
| "ZOOM_OUT_CANVAS"
| "PUBLISH"
| "UNDO_CANVAS_ACTION"
| "REDO_CANVAS_ACTION"
| "FETCH_WIDGET_CARDS"
| "SUCCESS_FETCHING_WIDGET_CARDS"
| "ERROR_FETCHING_WIDGET_CARDS"
| "ADD_PAGE_WIDGET"
| "REMOVE_PAGE_WIDGET"
| "LOAD_WIDGET_CONFIG"
| "LOAD_API_RESPONSE"
| "LOAD_QUERY_RESPONSE"
| "EXECUTE_ACTION"
| "LOAD_CANVAS_ACTIONS";
export const ReduxActionTypes: { [id: string]: ReduxActionType } = {
LOAD_CANVAS_WIDGETS: "LOAD_CANVAS_WIDGETS",
FETCH_CANVAS: "FETCH_CANVAS",
CLEAR_CANVAS: "CLEAR_CANVAS",
FETCH_PAGE: "FETCH_PAGE",
DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS",
REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS",
LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE",
ZOOM_IN_CANVAS: "ZOOM_IN_CANVAS",
ZOOM_OUT_CANVAS: "ZOOM_OUT_CANVAS",
UNDO_CANVAS_ACTION: "UNDO_CANVAS_ACTION",
REDO_CANVAS_ACTION: "REDO_CANVAS_ACTION",
LOAD_WIDGET_CONFIG: "LOAD_WIDGET_CONFIG",
PUBLISH: "PUBLISH",
FETCH_WIDGET_CARDS: "FETCH_WIDGET_CARDS",
SUCCESS_FETCHING_WIDGET_CARDS: "SUCCESS_FETCHING_WIDGET_CARDS",
ERROR_FETCHING_WIDGET_CARDS: "ERROR_FETCHING_WIDGET_CARDS",
ADD_PAGE_WIDGET: "ADD_PAGE_WIDGET",
REMOVE_PAGE_WIDGET: "REMOVE_PAGE_WIDGET",
LOAD_API_RESPONSE: "LOAD_API_RESPONSE",
LOAD_QUERY_RESPONSE: "LOAD_QUERY_RESPONSE",
EXECUTE_ACTION: "EXECUTE_ACTION",
LOAD_CANVAS_ACTIONS: "LOAD_CANVAS_ACTIONS",
};
export interface ReduxAction<T> {
type: ReduxActionType;
payload: T;
}
export interface LoadCanvasWidgetsPayload {
pageWidgetId: string;
widgets: { [widgetId: string]: WidgetProps };
}
export interface LoadWidgetConfigPayload {
[widgetId: string]: WidgetProps;
}
// export interface LoadAPIResponsePayload extends ExecuteActionResponse {}
// export interface LoadQueryResponsePayload extends ExecuteActionResponse {}
export interface LoadWidgetPanePayload {
widgets: WidgetProps[];
}
export interface LoadWidgetCardsPanePayload {
cards: { [id: string]: WidgetCardProps[] };
}

View File

@ -1,7 +1,6 @@
import * as React from "react";
import { ComponentProps } from "./BaseComponent";
import { Checkbox } from "@blueprintjs/core";
import { Container } from "./ContainerComponent";
class CheckboxComponent extends React.Component<CheckboxComponentProps> {
render() {
return (

View File

@ -1,6 +1,5 @@
import React from "react";
import { ComponentProps } from "./BaseComponent";
import { Radio, RadioGroup, IOptionProps } from "@blueprintjs/core";
import { Container } from "./ContainerComponent";
import { RadioOption } from "../widgets/RadioGroupWidget";
class RadioGroupComponent extends React.Component<RadioGroupComponentProps> {

View File

@ -12,7 +12,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(

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

@ -0,0 +1,23 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxActionTypes,
ReduxAction,
} from "../../constants/ReduxActionConstants";
import { ExecuteActionResponse } from "../../api/ActionAPI";
const initialState: APIDataState = {};
export interface APIDataState {
[name: string]: ExecuteActionResponse;
}
const apiDataReducer = createReducer(initialState, {
[ReduxActionTypes.LOAD_API_RESPONSE]: (
state: APIDataState,
action: ReduxAction<ExecuteActionResponse>,
) => {
return { ...state, [action.payload.actionId]: action.payload };
},
});
export default apiDataReducer;

View File

@ -1,9 +1,9 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ActionTypes,
LoadCanvasPayload,
ReduxActionTypes,
LoadCanvasWidgetsPayload,
ReduxAction,
} from "../../constants/ActionConstants";
} from "../../constants/ReduxActionConstants";
import { WidgetProps } from "../../widgets/BaseWidget";
import CanvasWidgetsNormalizer from "../../normalizers/CanvasWidgetsNormalizer";
@ -14,13 +14,13 @@ export interface FlattenedWidgetProps extends WidgetProps {
}
const canvasWidgetsReducer = createReducer(initialState, {
[ActionTypes.UPDATE_CANVAS]: (
[ReduxActionTypes.UPDATE_CANVAS]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<LoadCanvasPayload>,
action: ReduxAction<LoadCanvasWidgetsPayload>,
) => {
return { ...action.payload.widgets };
},
[ActionTypes.ADD_PAGE_WIDGET]: (
[ReduxActionTypes.ADD_PAGE_WIDGET]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<{ pageId: string; widget: WidgetProps }>,
) => {
@ -32,8 +32,8 @@ const canvasWidgetsReducer = createReducer(initialState, {
children.push(widget);
widgetTree.children = children;
const newState = CanvasWidgetsNormalizer.normalize({
responseMeta: {},
pageWidget: widgetTree,
responseMeta: { responseCode: "SUCCESS" },
layout: { dsl: widgetTree, actions: [] },
}).entities;
return newState.canvasWidgets;
},

View File

@ -1,5 +1,15 @@
import { combineReducers } from "redux";
import canvasWidgetsReducer from "./canvasWidgetsReducer";
import { combineReducers } from "redux"
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 });
export default entityReducer;
const entityReducer = combineReducers({
canvasWidgets: canvasWidgetsReducer,
apiData: apiDataReducer,
queryData: queryDataReducer,
widgetConfig: widgetConfigReducer,
actions: actionsReducer
})
export default entityReducer

View File

@ -0,0 +1,23 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxActionTypes,
ReduxAction,
} from "../../constants/ReduxActionConstants";
import { ExecuteActionResponse } from "../../api/ActionAPI";
const initialState: QueryDataState = {};
export interface QueryDataState {
[name: string]: ExecuteActionResponse;
}
const queryDataReducer = createReducer(initialState, {
[ReduxActionTypes.LOAD_API_RESPONSE]: (
state: QueryDataState,
action: ReduxAction<ExecuteActionResponse>,
) => {
return { ...state, [action.payload.actionId]: action.payload };
},
});
export default queryDataReducer;

View File

@ -1,9 +1,9 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ActionTypes,
ReduxActionTypes,
ReduxAction,
LoadWidgetConfigPayload,
} from "../../constants/ActionConstants";
} from "../../constants/ReduxActionConstants";
import { WidgetProps } from "../../widgets/BaseWidget";
import WidgetConfigResponse from "../../mockResponses/WidgetConfigResponse";
import { ButtonWidgetProps } from "../../widgets/ButtonWidget";
@ -12,7 +12,7 @@ import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import { ImageWidgetProps } from "../../widgets/ImageWidget";
import { InputWidgetProps } from "../../widgets/InputWidget";
import { SwitchWidgetProps } from "../../widgets/SwitchWidget";
import SpinnerWidget from "../../widgets/SpinnerWidget";
import { SpinnerWidgetProps } from "../../widgets/SpinnerWidget";
import { DatePickerWidgetProps } from "../../widgets/DatePickerWidget";
import { TableWidgetProps } from "../../widgets/TableWidget";
import { DropdownWidgetProps } from "../../widgets/DropdownWidget";
@ -35,7 +35,7 @@ export interface WidgetConfigReducerState {
SWITCH_WIDGET: Partial<SwitchWidgetProps> & WidgetConfigProps;
CONTAINER_WIDGET: Partial<ContainerWidgetProps<WidgetProps>> &
WidgetConfigProps;
SPINNER_WIDGET: Partial<SpinnerWidget> & WidgetConfigProps;
SPINNER_WIDGET: Partial<SpinnerWidgetProps> & WidgetConfigProps;
DATE_PICKER_WIDGET: Partial<DatePickerWidgetProps> & WidgetConfigProps;
TABLE_WIDGET: Partial<TableWidgetProps> & WidgetConfigProps;
DROP_DOWN_WIDGET: Partial<DropdownWidgetProps> & WidgetConfigProps;
@ -45,11 +45,11 @@ export interface WidgetConfigReducerState {
}
const widgetConfigReducer = createReducer(initialState, {
[ActionTypes.LOAD_WIDGET_CONFIG]: (
[ReduxActionTypes.LOAD_WIDGET_CONFIG]: (
state: WidgetConfigReducerState,
action: ReduxAction<LoadWidgetConfigPayload>,
) => {
return { ...action.payload.widgets };
return { ...action.payload };
},
});

View File

@ -5,6 +5,9 @@ import { CanvasReduxState } from "./uiReducers/canvasReducer";
import { CanvasWidgetsReduxState } from "./entityReducers/canvasWidgetsReducer";
import { WidgetCardsPaneReduxState } from "./uiReducers/widgetCardsPaneReducer";
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,5 +25,8 @@ export interface AppState {
};
entities: {
canvasWidgets: CanvasWidgetsReduxState;
apiData: APIDataState;
queryData: QueryDataState;
actions: ActionDataState;
};
}

View File

@ -1,18 +1,18 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ActionTypes,
LoadCanvasPayload,
ReduxActionTypes,
LoadCanvasWidgetsPayload,
ReduxAction,
} from "../../constants/ActionConstants";
} from "../../constants/ReduxActionConstants";
const initialState: CanvasReduxState = {
pageWidgetId: "0",
};
const canvasReducer = createReducer(initialState, {
[ActionTypes.UPDATE_CANVAS]: (
[ReduxActionTypes.UPDATE_CANVAS]: (
state: CanvasReduxState,
action: ReduxAction<LoadCanvasPayload>,
action: ReduxAction<LoadCanvasWidgetsPayload>,
) => {
return { pageWidgetId: action.payload.pageWidgetId };
},

View File

@ -1,31 +1,31 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ActionTypes,
ReduxActionTypes,
ReduxAction,
LoadCanvasPayload,
LoadCanvasWidgetsPayload,
LoadWidgetCardsPanePayload,
} from "../../constants/ActionConstants";
import { WidgetCardProps } from "../../widgets/BaseWidget";
} from "../../constants/ReduxActionConstants";
import { WidgetCardProps, WidgetProps } from "../../widgets/BaseWidget";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
const initialState: EditorReduxState = {};
const editorReducer = createReducer(initialState, {
[ActionTypes.SUCCESS_FETCHING_WIDGET_CARDS]: (
[ReduxActionTypes.SUCCESS_FETCHING_WIDGET_CARDS]: (
state: EditorReduxState,
action: ReduxAction<LoadWidgetCardsPanePayload>,
) => {
return { ...state.pageWidget, ...action.payload };
},
[ActionTypes.ADD_PAGE_WIDGET]: (
[ReduxActionTypes.ADD_PAGE_WIDGET]: (
state: EditorReduxState,
// action: ReduxAction<{ pageId: string; widget: WidgetProps }>,
action: ReduxAction<{ pageId: string; widget: WidgetProps }>,
) => {
return state;
},
[ActionTypes.UPDATE_CANVAS]: (
[ReduxActionTypes.UPDATE_CANVAS]: (
state: EditorReduxState,
action: ReduxAction<LoadCanvasPayload>,
action: ReduxAction<LoadCanvasWidgetsPayload>,
) => {
return { pageWidgetId: action.payload.pageWidgetId };
},

View File

@ -1,16 +1,16 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ActionTypes,
ReduxActionTypes,
ReduxAction,
LoadWidgetCardsPanePayload,
} from "../../constants/ActionConstants";
} from "../../constants/ReduxActionConstants";
import { WidgetCardProps } from "../../widgets/BaseWidget";
import WidgetCardsPaneResponse from "../../mockResponses/WidgetCardsPaneResponse";
const initialState: WidgetCardsPaneReduxState = WidgetCardsPaneResponse;
const widgetCardsPaneReducer = createReducer(initialState, {
[ActionTypes.ERROR_FETCHING_WIDGET_CARDS]: (
[ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS]: (
state: WidgetCardsPaneReduxState,
action: ReduxAction<LoadWidgetCardsPanePayload>,
) => {

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,31 +1,29 @@
import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer";
import { ActionTypes, ReduxAction } from "../constants/ActionConstants";
import PageApi, { PageResponse, PageRequest } from "../api/PageApi";
import { call, put, takeEvery } from "redux-saga/effects";
import { RenderModes } from "../constants/WidgetConstants";
import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer"
import { ReduxActionTypes, ReduxAction, LoadCanvasWidgetsPayload } from "../constants/ReduxActionConstants"
import PageApi, { PageResponse, PageRequest } from "../api/PageApi"
import { call, put, takeEvery, all } from "redux-saga/effects"
import { RenderModes } from "../constants/WidgetConstants"
export function* fetchPageSaga(pageRequestAction: ReduxAction<PageRequest>) {
const pageRequest = pageRequestAction.payload;
const pageRequest = pageRequestAction.payload
try {
const pageResponse: PageResponse = yield call(
PageApi.fetchPage,
pageRequest,
);
const pageResponse: PageResponse = yield call(PageApi.fetchPage, pageRequest)
if (pageRequest.renderMode === RenderModes.CANVAS) {
const normalizedResponse = CanvasWidgetsNormalizer.normalize(
pageResponse,
);
const payload = {
const normalizedResponse = CanvasWidgetsNormalizer.normalize(pageResponse)
const canvasWidgetsPayload: LoadCanvasWidgetsPayload = {
pageWidgetId: normalizedResponse.result,
widgets: normalizedResponse.entities.canvasWidgets,
};
yield put({ type: ActionTypes.UPDATE_CANVAS, payload });
widgets: normalizedResponse.entities.canvasWidgets
}
} catch (err) {
yield all([
put({ type: ReduxActionTypes.UPDATE_CANVAS, canvasWidgetsPayload }),
put({ type: ReduxActionTypes.LOAD_CANVAS_ACTIONS, payload: pageResponse.layout.actions })
])
}
} catch(err){
//TODO(abhinav): REFACTOR THIS
}
}
export function* watchFetchPage() {
yield takeEvery(ActionTypes.FETCH_PAGE, fetchPageSaga);
yield takeEvery(ReduxActionTypes.FETCH_PAGE, fetchPageSaga)
}

View File

@ -1,5 +1,8 @@
// import CanvasWidgetsNormalizer from "../normalizers/CanvasWidgetsNormalizer"
import { ActionTypes } from "../constants/ActionConstants";
import {
ReduxActionTypes,
ReduxAction,
} from "../constants/ReduxActionConstants";
import WidgetCardsPaneApi, {
WidgetCardsPaneResponse,
} from "../api/WidgetCardsPaneApi";
@ -13,10 +16,10 @@ export function* fetchWidgetCards() {
);
yield put(successFetchingWidgetCards(widgetCards.cards));
} catch (err) {
yield put({ type: ActionTypes.ERROR_FETCHING_WIDGET_CARDS, err });
yield put({ type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS, err });
}
}
export function* fetchWidgetCardsSaga() {
yield takeLatest(ActionTypes.FETCH_WIDGET_CARDS, fetchWidgetCards);
yield takeLatest(ReduxActionTypes.FETCH_WIDGET_CARDS, fetchWidgetCards);
}

View File

@ -1,7 +1,8 @@
import { all } from "redux-saga/effects";
import { watchFetchPage } from "../sagas/PageSagas";
import { fetchWidgetCardsSaga } from "./WidgetCardsPaneSagas";
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)])
}

View File

@ -1,5 +1,4 @@
import FontFaceObserver from "fontfaceobserver";
import { ReduxAction } from "../constants/ActionConstants";
import { ReduxAction } from "../constants/ReduxActionConstants";
import {
SENTRY_PROD_CONFIG,
SENTRY_STAGE_CONFIG,
@ -9,6 +8,7 @@ import {
import * as Sentry from "@sentry/browser";
import AnalyticsUtil from "./AnalyticsUtil";
import netlifyIdentity from "netlify-identity-widget";
import FontFaceObserver from "fontfaceobserver";
export const createReducer = (
initialState: any,

View File

@ -1,7 +1,7 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import CalloutComponent from "../editorComponents/CalloutComponent";
import { ActionPayload } from "../constants/ActionConstants";
class AlertWidget extends BaseWidget<AlertWidgetProps, WidgetState> {
getPageView() {
@ -21,6 +21,7 @@ export interface AlertWidgetProps extends WidgetProps {
intent: MessageIntent;
header: string;
message: string;
onPrimaryClick: ActionPayload[];
}
export default AlertWidget;

View File

@ -2,6 +2,7 @@ import * as React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import ButtonComponent from "../editorComponents/ButtonComponent";
import { ActionPayload } from "../constants/ActionConstants";
class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
getPageView() {
@ -29,6 +30,7 @@ export type ButtonStyle =
export interface ButtonWidgetProps extends WidgetProps {
text?: string;
buttonStyle?: ButtonStyle;
onClick?: ActionPayload[];
}
export default ButtonWidget;

View File

@ -2,6 +2,7 @@ import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import CheckboxComponent from "../editorComponents/CheckboxComponent";
import { ActionPayload } from "../constants/ActionConstants";
class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
getPageView() {
@ -24,6 +25,7 @@ class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
export interface CheckboxWidgetProps extends WidgetProps {
label: string;
defaultCheckedState: boolean;
onCheckChange?: ActionPayload[];
}
export default CheckboxWidget;

View File

@ -1,7 +1,7 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import { Moment } from "moment";
import { ActionPayload } from "../constants/ActionConstants";
class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
getPageView() {
@ -23,6 +23,8 @@ export interface DatePickerWidgetProps extends WidgetProps {
enableTime: boolean;
label: string;
datePickerType: DatePickerType;
onDateSelected: ActionPayload[];
onDateRangeSelected: ActionPayload[];
}
export default DatePickerWidget;

View File

@ -1,6 +1,7 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import { ActionPayload } from "../constants/ActionConstants";
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
getPageView() {
@ -23,6 +24,7 @@ export interface DropdownWidgetProps extends WidgetProps {
label?: string;
type: SelectionType;
options?: DropdownOption[];
onOptionSelected?: ActionPayload[];
}
export default DropdownWidget;

View File

@ -2,7 +2,7 @@ import * as React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import RadioGroupComponent from "../editorComponents/RadioGroupComponent";
import { IOptionProps } from "@blueprintjs/core";
import { ActionPayload } from "../constants/ActionConstants";
class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
getPageView() {
@ -32,6 +32,7 @@ export interface RadioGroupWidgetProps extends WidgetProps {
label: string;
options: RadioOption[];
defaultOptionValue: string;
onOptionSelected?: ActionPayload[];
}
export default RadioGroupWidget;

View File

@ -1,7 +1,7 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import TextComponent from "../editorComponents/TextComponent";
import { ActionPayload } from "../constants/ActionConstants";
class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
getPageView() {
@ -19,6 +19,9 @@ export interface TableWidgetProps extends WidgetProps {
pageKey?: string;
label: string;
tableData?: object[];
onPageChange?: ActionPayload[];
onRowSelected?: ActionPayload[];
onColumnActionClick?: Record<string, ActionPayload[]>;
}
export default TableWidget;

View File

@ -6773,6 +6773,11 @@ jsonify@~0.0.0:
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
jsonpath-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-1.0.0.tgz#78fa5c4ae62968476268d505d9f78c65469d0e7c"
integrity sha512-CXQJ/tsgFogKYBuCRmnlChIw66JBXp8kAkT+R4mSB2cuzCSBi88lx2A+vHvo27RY4Wtj5xVVGu2/2O7NwZ79mg==
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"