Merge branch 'dynamic-binding-scaffold' into 'release'

Dynamic binding scaffold

See merge request theappsmith/internal-tools-client!104
This commit is contained in:
Hetu Nandu 2019-11-01 07:11:32 +00:00
commit ecbd6cd067
24 changed files with 194 additions and 166 deletions

View File

@ -0,0 +1,17 @@
import {
ReduxAction,
ReduxActionTypes,
ReduxActionWithoutPayload,
} from "../constants/ReduxActionConstants";
import { NamePathBindingMap } from "../constants/BindingsConstants";
export const createUpdateBindingsMap = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_INIT,
});
export const bindingsMapSuccess = (
map: NamePathBindingMap,
): ReduxAction<NamePathBindingMap> => ({
type: ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_SUCCESS,
payload: map,
});

View File

@ -0,0 +1,8 @@
import {
ReduxActionTypes,
ReduxActionWithoutPayload,
} from "../constants/ReduxActionConstants";
export const initAppData = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.INIT_APP_DATA,
});

View File

@ -1,5 +1,4 @@
import { FetchPageRequest } from "../api/PageApi"; import { FetchPageRequest } from "../api/PageApi";
import { RenderMode } from "../constants/WidgetConstants";
import { WidgetProps, WidgetOperation } from "../widgets/BaseWidget"; import { WidgetProps, WidgetOperation } from "../widgets/BaseWidget";
import { WidgetType } from "../constants/WidgetConstants"; import { WidgetType } from "../constants/WidgetConstants";
import { import {
@ -11,10 +10,11 @@ import {
} from "../constants/ReduxActionConstants"; } from "../constants/ReduxActionConstants";
import { ContainerWidgetProps } from "../widgets/ContainerWidget"; import { ContainerWidgetProps } from "../widgets/ContainerWidget";
export const fetchPage = ( export const fetchPageList = () => ({
pageId: string, type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
renderMode: RenderMode, });
): ReduxAction<FetchPageRequest> => {
export const fetchPage = (pageId: string): ReduxAction<FetchPageRequest> => {
return { return {
type: ReduxActionTypes.FETCH_PAGE, type: ReduxActionTypes.FETCH_PAGE,
payload: { payload: {
@ -93,9 +93,9 @@ export type WidgetMove = {
topRow: number; topRow: number;
parentId: string; parentId: string;
/* /*
If newParentId is different from what we have in redux store, If newParentId is different from what we have in redux store,
then we have to delete this, then we have to delete this,
as it has been dropped in another container somewhere. as it has been dropped in another container somewhere.
*/ */
newParentId: string; newParentId: string;
}; };

View File

@ -59,7 +59,7 @@ export interface RestAction {
export interface ExecuteActionRequest extends APIRequest { export interface ExecuteActionRequest extends APIRequest {
actionId: string; actionId: string;
dynamicBindingList?: Property[]; params?: Property[];
} }
export interface ExecuteActionResponse extends ApiResponse { export interface ExecuteActionResponse extends ApiResponse {

View File

@ -24,16 +24,6 @@ const ResponseMetaInfo = styled.div`
} }
`; `;
const ResponseBodyWrapper = styled.span`
max-height: 100%;
&&& {
textarea,
pre {
height: 100%;
overflow: auto;
}
}
`;
const StatusCodeText = styled(BaseText)<{ code: string }>` const StatusCodeText = styled(BaseText)<{ code: string }>`
color: ${props => color: ${props =>
props.code.match(/2\d\d/) ? props.theme.colors.primary : "red"}; props.code.match(/2\d\d/) ? props.theme.colors.primary : "red"};
@ -61,10 +51,10 @@ const LoadingScreen = styled.div`
bottom: 0; bottom: 0;
right: 0; right: 0;
left: 0; left: 0;
background-color: rgba(0, 0, 0, 0.6); background-color: rgba(255, 255, 255, 0.6);
pointer-events: none; pointer-events: none;
z-index: 1; z-index: 1;
color: white; color: black;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -138,17 +128,13 @@ const ApiResponseView = (props: Props) => {
key: "body", key: "body",
title: "Response Body", title: "Response Body",
panelComponent: ( panelComponent: (
<ResponseBodyWrapper> <CodeEditor
{response.body && ( height={500}
<CodeEditor language={"json"}
height={500} input={{
language={"json"} value: JSON.stringify(response.body, null, 2),
input={{ }}
value: JSON.stringify(response.body, null, 2), />
}}
/>
)}
</ResponseBodyWrapper>
), ),
}, },
{ {

View File

@ -24,7 +24,6 @@ const CodeEditor = (props: Props) => {
minimap: { enabled: false }, minimap: { enabled: false },
readOnly: !props.input.onChange, readOnly: !props.input.onChange,
}; };
debugger;
return ( return (
<Wrapper height={props.height}> <Wrapper height={props.height}>
<MonacoEditor <MonacoEditor

View File

@ -20,6 +20,8 @@ import { required } from "../../utils/validation/common";
import { apiPathValidation } from "../../utils/validation/ApiForm"; import { apiPathValidation } from "../../utils/validation/ApiForm";
const Form = styled.form` const Form = styled.form`
display: flex;
flex-direction: column;
height: calc(100vh - ${props => props.theme.headerHeight}); height: calc(100vh - ${props => props.theme.headerHeight});
width: 100%; width: 100%;
${FormLabel} { ${FormLabel} {
@ -40,6 +42,7 @@ const Form = styled.form`
const SecondaryWrapper = styled.div` const SecondaryWrapper = styled.div`
display: flex; display: flex;
height: 100%;
border-top: 1px solid #d0d7dd; border-top: 1px solid #d0d7dd;
`; `;

View File

@ -0,0 +1 @@
export type NamePathBindingMap = Record<string, string>;

View File

@ -2,6 +2,7 @@ import { WidgetProps, WidgetCardProps } from "../widgets/BaseWidget";
import { RefObject } from "react"; import { RefObject } from "react";
export const ReduxActionTypes: { [key: string]: string } = { export const ReduxActionTypes: { [key: string]: string } = {
INIT_APP_DATA: "INIT_APP_DATA",
REPORT_ERROR: "REPORT_ERROR", REPORT_ERROR: "REPORT_ERROR",
FLUSH_ERRORS: "FLUSH_ERRORS", FLUSH_ERRORS: "FLUSH_ERRORS",
UPDATE_CANVAS: "UPDATE_CANVAS", UPDATE_CANVAS: "UPDATE_CANVAS",
@ -64,11 +65,14 @@ export const ReduxActionTypes: { [key: string]: string } = {
FETCH_PAGE_LIST_INIT: "FETCH_PAGE_LIST_INIT", FETCH_PAGE_LIST_INIT: "FETCH_PAGE_LIST_INIT",
FETCH_PAGE_LIST_SUCCESS: "FETCH_PAGE_LIST_SUCCESS", FETCH_PAGE_LIST_SUCCESS: "FETCH_PAGE_LIST_SUCCESS",
INITIALIZE_PAGE_VIEWER: "INITIALIZE_PAGE_VIEWER", INITIALIZE_PAGE_VIEWER: "INITIALIZE_PAGE_VIEWER",
CREATE_UPDATE_BINDINGS_MAP_INIT: "CREATE_UPDATE_BINDINGS_MAP_INIT",
CREATE_UPDATE_BINDINGS_MAP_SUCCESS: "CREATE_UPDATE_BINDINGS_MAP_SUCCESS",
}; };
export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes]; export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes];
export const ReduxActionErrorTypes: { [key: string]: string } = { export const ReduxActionErrorTypes: { [key: string]: string } = {
INIT_APP_DATA_ERROR: "INIT_APP_DATA_ERROR",
API_ERROR: "API_ERROR", API_ERROR: "API_ERROR",
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR", WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR", WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR",
@ -101,6 +105,8 @@ export interface ReduxAction<T> {
payload: T; payload: T;
} }
export type ReduxActionWithoutPayload = Pick<ReduxAction<undefined>, "type">;
export interface ReduxActionErrorPayload { export interface ReduxActionErrorPayload {
message: string; message: string;
source?: string; source?: string;

View File

@ -13,8 +13,6 @@ import { AppState } from "../../reducers";
import { RouteComponentProps } from "react-router"; import { RouteComponentProps } from "react-router";
import { API_EDITOR_URL } from "../../constants/routes"; import { API_EDITOR_URL } from "../../constants/routes";
import { API_EDITOR_FORM_NAME } from "../../constants/forms"; import { API_EDITOR_FORM_NAME } from "../../constants/forms";
import { ResourceDataState } from "../../reducers/entityReducers/resourcesReducer";
import { fetchResources } from "../../actions/resourcesActions";
import { FORM_INITIAL_VALUES } from "../../constants/ApiEditorConstants"; import { FORM_INITIAL_VALUES } from "../../constants/ApiEditorConstants";
import { normalizeApiFormData } from "../../normalizers/ApiFormNormalizer"; import { normalizeApiFormData } from "../../normalizers/ApiFormNormalizer";
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer"; import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
@ -22,7 +20,6 @@ import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
interface ReduxStateProps { interface ReduxStateProps {
actions: ActionDataState; actions: ActionDataState;
formData: any; formData: any;
resources: ResourceDataState;
} }
interface ReduxActionProps { interface ReduxActionProps {
submitForm: (name: string) => void; submitForm: (name: string) => void;
@ -32,7 +29,6 @@ interface ReduxActionProps {
updateAction: (data: RestAction) => void; updateAction: (data: RestAction) => void;
initialize: (formName: string, data?: Partial<RestAction>) => void; initialize: (formName: string, data?: Partial<RestAction>) => void;
destroy: (formName: string) => void; destroy: (formName: string) => void;
fetchResources: () => void;
} }
type Props = ReduxActionProps & type Props = ReduxActionProps &
@ -41,9 +37,6 @@ type Props = ReduxActionProps &
class ApiEditor extends React.Component<Props> { class ApiEditor extends React.Component<Props> {
componentDidMount(): void { componentDidMount(): void {
if (!this.props.resources.list.length) {
this.props.fetchResources();
}
const currentId = this.props.match.params.id; const currentId = this.props.match.params.id;
if (!currentId) return; if (!currentId) return;
if (!this.props.actions.data.length) { if (!this.props.actions.data.length) {
@ -111,7 +104,6 @@ class ApiEditor extends React.Component<Props> {
const mapStateToProps = (state: AppState): ReduxStateProps => ({ const mapStateToProps = (state: AppState): ReduxStateProps => ({
actions: state.entities.actions, actions: state.entities.actions,
formData: getFormValues(API_EDITOR_FORM_NAME)(state), formData: getFormValues(API_EDITOR_FORM_NAME)(state),
resources: state.entities.resources,
}); });
const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
@ -123,7 +115,6 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
initialize: (formName: string, data?: Partial<RestAction>) => initialize: (formName: string, data?: Partial<RestAction>) =>
dispatch(initialize(formName, data)), dispatch(initialize(formName, data)),
destroy: (formName: string) => dispatch(destroy(formName)), destroy: (formName: string) => dispatch(destroy(formName)),
fetchResources: () => dispatch(fetchResources()),
}); });
export default connect( export default connect(

View File

@ -3,7 +3,6 @@ import { connect } from "react-redux";
import { RouteComponentProps } from "react-router"; import { RouteComponentProps } from "react-router";
import styled from "styled-components"; import styled from "styled-components";
import { AppState } from "../../reducers"; import { AppState } from "../../reducers";
import { fetchActions } from "../../actions/actionActions";
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer"; import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes"; import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes";
import { BaseButton } from "../../components/blueprint/ButtonComponent"; import { BaseButton } from "../../components/blueprint/ButtonComponent";
@ -86,22 +85,9 @@ interface ReduxStateProps {
actions: ActionDataState; actions: ActionDataState;
} }
interface ReduxActionProps { type Props = ReduxStateProps & RouteComponentProps<{ id: string }>;
fetchActions: () => void;
selectAction: (id: string) => void;
}
type Props = ReduxStateProps &
ReduxActionProps &
RouteComponentProps<{ id: string }>;
class ApiSidebar extends React.Component<Props> { class ApiSidebar extends React.Component<Props> {
componentDidMount(): void {
if (!this.props.actions.data.length) {
this.props.fetchActions();
}
}
handleCreateNew = () => { handleCreateNew = () => {
const { history } = this.props; const { history } = this.props;
history.push(API_EDITOR_URL); history.push(API_EDITOR_URL);
@ -147,11 +133,4 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({
actions: state.entities.actions, actions: state.entities.actions,
}); });
const mapDispatchToProps = (dispatch: any) => ({ export default connect(mapStateToProps)(ApiSidebar);
fetchActions: () => dispatch(fetchActions()),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ApiSidebar);

View File

@ -9,6 +9,7 @@ const Wrapper = styled.div`
grid-template-columns: 1fr 4fr; grid-template-columns: 1fr 4fr;
width: ${props => props.theme.sidebarWidth}; width: ${props => props.theme.sidebarWidth};
box-shadow: 0px 1px 3px ${props => props.theme.colors.paneBG}; box-shadow: 0px 1px 3px ${props => props.theme.colors.paneBG};
z-index: 20;
`; `;
const NavBar = styled.div` const NavBar = styled.div`

View File

@ -11,20 +11,9 @@ import {
} from "../../widgets/BaseWidget"; } from "../../widgets/BaseWidget";
import { ActionPayload } from "../../constants/ActionConstants"; import { ActionPayload } from "../../constants/ActionConstants";
import { executeAction } from "../../actions/widgetActions"; import { executeAction } from "../../actions/widgetActions";
import { fetchPage, savePage, updateWidget } from "../../actions/pageActions"; import { savePage, updateWidget } from "../../actions/pageActions";
import { import { getDenormalizedDSL } from "../../selectors/editorSelectors";
getPropertyPaneConfigsId,
getCurrentLayoutId,
getCurrentPageId,
getDenormalizedDSL,
getCurrentPageName,
} from "../../selectors/editorSelectors";
import { RenderModes } from "../../constants/WidgetConstants";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget"; import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import {
EditorConfigIdsType,
fetchEditorConfigs,
} from "../../actions/configsActions";
import { ReduxActionTypes } from "../../constants/ReduxActionConstants"; import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
import { updateWidgetProperty } from "../../actions/controlActions"; import { updateWidgetProperty } from "../../actions/controlActions";
@ -56,7 +45,6 @@ const CanvasContainer = styled.section`
type EditorProps = { type EditorProps = {
dsl: ContainerWidgetProps<WidgetProps> | any; dsl: ContainerWidgetProps<WidgetProps> | any;
fetchCanvasWidgets: Function;
executeAction: (actionPayloads?: ActionPayload[]) => void; executeAction: (actionPayloads?: ActionPayload[]) => void;
updateWidget: Function; updateWidget: Function;
updateWidgetProperty: ( updateWidgetProperty: (
@ -65,64 +53,39 @@ type EditorProps = {
propertyValue: any, propertyValue: any,
) => void; ) => void;
savePageLayout: Function; savePageLayout: Function;
currentPageName: string;
currentPageId: string;
currentLayoutId: string;
showPropertyPane: ( showPropertyPane: (
widgetId?: string, widgetId?: string,
node?: HTMLDivElement, node?: HTMLDivElement,
toggle?: boolean, toggle?: boolean,
) => void; ) => void;
fetchConfigs: Function;
propertyPaneConfigsId: string;
}; };
export const WidgetFunctionsContext: Context<WidgetFunctions> = createContext( export const WidgetFunctionsContext: Context<WidgetFunctions> = createContext(
{}, {},
); );
class WidgetsEditor extends React.Component<EditorProps> { const WidgetsEditor = (props: EditorProps) => (
componentDidMount() { <WidgetFunctionsContext.Provider
this.props.fetchConfigs({ value={{
propertyPaneConfigsId: this.props.propertyPaneConfigsId, executeAction: props.executeAction,
// widgetCardsPaneId: this.props.widgetCardsPaneId, updateWidget: props.updateWidget,
// widgetConfigsId: this.props.widgetConfigsId, updateWidgetProperty: props.updateWidgetProperty,
}); }}
this.props.fetchCanvasWidgets(this.props.currentPageId); >
} <EditorWrapper>
<CanvasContainer>
render(): React.ReactNode { {props.dsl && (
return ( <Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />
<WidgetFunctionsContext.Provider )}
value={{ </CanvasContainer>
executeAction: this.props.executeAction, <PropertyPane />
updateWidget: this.props.updateWidget, </EditorWrapper>
updateWidgetProperty: this.props.updateWidgetProperty, </WidgetFunctionsContext.Provider>
}} );
>
<EditorWrapper>
<CanvasContainer>
{this.props.dsl && (
<Canvas
dsl={this.props.dsl}
showPropertyPane={this.props.showPropertyPane}
/>
)}
</CanvasContainer>
<PropertyPane />
</EditorWrapper>
</WidgetFunctionsContext.Provider>
);
}
}
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
return { return {
dsl: getDenormalizedDSL(state), dsl: getDenormalizedDSL(state),
currentPageId: getCurrentPageId(state),
currentLayoutId: getCurrentLayoutId(state),
currentPageName: getCurrentPageName(state),
propertyPaneConfigsId: getPropertyPaneConfigsId(state),
}; };
}; };
@ -135,8 +98,6 @@ const mapDispatchToProps = (dispatch: any) => {
) => dispatch(updateWidgetProperty(widgetId, propertyName, propertyValue)), ) => dispatch(updateWidgetProperty(widgetId, propertyName, propertyValue)),
executeAction: (actionPayloads?: ActionPayload[]) => executeAction: (actionPayloads?: ActionPayload[]) =>
dispatch(executeAction(actionPayloads)), dispatch(executeAction(actionPayloads)),
fetchCanvasWidgets: (pageId: string) =>
dispatch(fetchPage(pageId, RenderModes.CANVAS)),
updateWidget: ( updateWidget: (
operation: WidgetOperation, operation: WidgetOperation,
widgetId: string, widgetId: string,
@ -147,8 +108,6 @@ const mapDispatchToProps = (dispatch: any) => {
layoutId: string, layoutId: string,
dsl: ContainerWidgetProps<WidgetProps>, dsl: ContainerWidgetProps<WidgetProps>,
) => dispatch(savePage(pageId, layoutId, dsl)), ) => dispatch(savePage(pageId, layoutId, dsl)),
fetchConfigs: (configsIds: EditorConfigIdsType) =>
dispatch(fetchEditorConfigs(configsIds)),
showPropertyPane: ( showPropertyPane: (
widgetId?: string, widgetId?: string,
node?: HTMLDivElement, node?: HTMLDivElement,

View File

@ -17,6 +17,7 @@ import {
PageListPayload, PageListPayload,
} from "../../constants/ReduxActionConstants"; } from "../../constants/ReduxActionConstants";
import { Dialog, Classes, AnchorButton } from "@blueprintjs/core"; import { Dialog, Classes, AnchorButton } from "@blueprintjs/core";
import { initAppData } from "../../actions/initActions";
type EditorProps = { type EditorProps = {
currentPageName: string; currentPageName: string;
@ -26,8 +27,8 @@ type EditorProps = {
currentPageId: string; currentPageId: string;
publishApplication: Function; publishApplication: Function;
previewPage: Function; previewPage: Function;
initData: Function;
createPage: Function; createPage: Function;
fetchPageList: Function;
pages: PageListPayload; pages: PageListPayload;
switchPage: (pageId: string) => void; switchPage: (pageId: string) => void;
isPublishing: boolean; isPublishing: boolean;
@ -40,7 +41,7 @@ class Editor extends Component<EditorProps> {
}; };
componentDidMount() { componentDidMount() {
this.props.fetchPageList(); this.props.initData();
} }
componentDidUpdate(currently: EditorProps) { componentDidUpdate(currently: EditorProps) {
const previously = this.props; const previously = this.props;
@ -123,6 +124,7 @@ const mapStateToProps = (state: AppState) => ({
const mapDispatchToProps = (dispatch: any) => { const mapDispatchToProps = (dispatch: any) => {
return { return {
initData: () => dispatch(initAppData()),
publishApplication: (applicationId: string) => { publishApplication: (applicationId: string) => {
dispatch({ dispatch({
type: ReduxActionTypes.PUBLISH_APPLICATION_INIT, type: ReduxActionTypes.PUBLISH_APPLICATION_INIT,
@ -149,11 +151,6 @@ const mapDispatchToProps = (dispatch: any) => {
}, },
}); });
}, },
fetchPageList: () => {
dispatch({
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
});
},
switchPage: (pageId: string) => { switchPage: (pageId: string) => {
dispatch({ dispatch({
type: ReduxActionTypes.FETCH_PAGE, type: ReduxActionTypes.FETCH_PAGE,

View File

@ -4,12 +4,9 @@ import {
ReduxAction, ReduxAction,
ReduxActionErrorTypes, ReduxActionErrorTypes,
} from "../../constants/ReduxActionConstants"; } from "../../constants/ReduxActionConstants";
import _ from "lodash";
import { PageAction } from "../../constants/ActionConstants";
import { RestAction } from "../../api/ActionAPI"; import { RestAction } from "../../api/ActionAPI";
const initialState: ActionDataState = { const initialState: ActionDataState = {
list: {},
data: [], data: [],
isFetching: false, isFetching: false,
isRunning: false, isRunning: false,
@ -18,9 +15,6 @@ const initialState: ActionDataState = {
}; };
export interface ActionDataState { export interface ActionDataState {
list: {
[name: string]: PageAction;
};
data: RestAction[]; data: RestAction[];
isFetching: boolean; isFetching: boolean;
isRunning: boolean; isRunning: boolean;
@ -29,15 +23,6 @@ export interface ActionDataState {
} }
const actionsReducer = createReducer(initialState, { const actionsReducer = createReducer(initialState, {
[ReduxActionTypes.LOAD_CANVAS_ACTIONS]: (
state: ActionDataState,
action: ReduxAction<PageAction[]>,
) => {
const actionMap = _.mapKeys(action.payload, (action: PageAction) => {
return action.id;
});
return { ...state, list: { ...actionMap } };
},
[ReduxActionTypes.FETCH_ACTIONS_INIT]: (state: ActionDataState) => ({ [ReduxActionTypes.FETCH_ACTIONS_INIT]: (state: ActionDataState) => ({
...state, ...state,
isFetching: true, isFetching: true,

View File

@ -0,0 +1,19 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxActionTypes,
ReduxAction,
} from "../../constants/ReduxActionConstants";
import { NamePathBindingMap } from "../../constants/BindingsConstants";
export type BindingsDataState = NamePathBindingMap;
const initialState: BindingsDataState = {};
const bindingsReducer = createReducer(initialState, {
[ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_SUCCESS]: (
state: BindingsDataState,
action: ReduxAction<NamePathBindingMap>,
) => action.payload,
});
export default bindingsReducer;

View File

@ -6,6 +6,7 @@ import widgetConfigReducer from "./widgetConfigReducer";
import actionsReducer from "./actionsReducer"; import actionsReducer from "./actionsReducer";
import propertyPaneConfigReducer from "./propertyPaneConfigReducer"; import propertyPaneConfigReducer from "./propertyPaneConfigReducer";
import resourceReducer from "./resourcesReducer"; import resourceReducer from "./resourcesReducer";
import bindingsReducer from "./bindingsReducer";
const entityReducer = combineReducers({ const entityReducer = combineReducers({
canvasWidgets: canvasWidgetsReducer, canvasWidgets: canvasWidgetsReducer,
@ -15,5 +16,6 @@ const entityReducer = combineReducers({
actions: actionsReducer, actions: actionsReducer,
propertyConfig: propertyPaneConfigReducer, propertyConfig: propertyPaneConfigReducer,
resources: resourceReducer, resources: resourceReducer,
nameBindings: bindingsReducer,
}); });
export default entityReducer; export default entityReducer;

View File

@ -14,6 +14,7 @@ import { WidgetConfigReducerState } from "./entityReducers/widgetConfigReducer";
import { WidgetSidebarReduxState } from "./uiReducers/widgetSidebarReducer"; import { WidgetSidebarReduxState } from "./uiReducers/widgetSidebarReducer";
import { ResourceDataState } from "./entityReducers/resourcesReducer"; import { ResourceDataState } from "./entityReducers/resourcesReducer";
import { AppViewReduxState } from "./uiReducers/appViewReducer"; import { AppViewReduxState } from "./uiReducers/appViewReducer";
import { BindingsDataState } from "./entityReducers/bindingsReducer";
const appReducer = combineReducers({ const appReducer = combineReducers({
entities: entityReducer, entities: entityReducer,
@ -39,5 +40,6 @@ export interface AppState {
propertyConfig: PropertyPaneConfigState; propertyConfig: PropertyPaneConfigState;
widgetConfig: WidgetConfigReducerState; widgetConfig: WidgetConfigReducerState;
resources: ResourceDataState; resources: ResourceDataState;
nameBindings: BindingsDataState;
}; };
} }

View File

@ -39,36 +39,42 @@ const getDataTree = (state: AppState) => {
return state.entities; return state.entities;
}; };
const getAction = (state: AppState, actionId: string): PageAction => { const getAction = (
return state.entities.actions.list[actionId]; state: AppState,
actionId: string,
): RestAction | undefined => {
return _.find(state.entities.actions.data, { id: actionId });
}; };
export function* evaluateJSONPathSaga(jsonPath: string): any { export function* evaluateJSONPathSaga(jsonPath: string): any {
const dataTree = yield select(getDataTree); const dataTree = yield select(getDataTree);
return JSONPath({ path: jsonPath, json: dataTree }); const splitPath = jsonPath.split(".");
const bindingPath = dataTree.nameBindings[splitPath[0]];
const fullPath = `${bindingPath}.${splitPath.slice(1).join(".")}`;
return JSONPath({ path: fullPath, json: dataTree });
} }
export function* executeAPIQueryActionSaga(apiAction: ActionPayload) { export function* executeAPIQueryActionSaga(apiAction: { actionId: string }) {
const api: PageAction = yield select(getAction, apiAction.actionId); const api: PageAction = yield select(getAction, apiAction.actionId);
const executeActionRequest: ExecuteActionRequest = { const executeActionRequest: ExecuteActionRequest = {
actionId: apiAction.actionId, actionId: apiAction.actionId,
}; };
if (!_.isNil(api.jsonPathKeys)) { if (!_.isNil(api.jsonPathKeys)) {
const responses: any = yield all( const values: any = _.flatten(
api.jsonPathKeys.map((jsonPath: string) => { yield all(
return call(evaluateJSONPathSaga, jsonPath); api.jsonPathKeys.map((jsonPath: string) => {
}), return call(evaluateJSONPathSaga, jsonPath);
}),
),
); );
const dynamicBindingMap: Record<string, any> = _.keyBy( const dynamicBindings: Record<string, string> = {};
responses, api.jsonPathKeys.forEach((key, i) => {
(response: string, index: number) => { dynamicBindings[key] = values[i];
return api.jsonPathKeys ? api.jsonPathKeys[index] : undefined; });
}, executeActionRequest.params = mapToPropList(dynamicBindings);
);
executeActionRequest.dynamicBindingList = mapToPropList(dynamicBindingMap);
} }
yield ActionAPI.executeAction(executeActionRequest); return yield ActionAPI.executeAction(executeActionRequest);
} }
export function* executeActionSaga(action: ReduxAction<ActionPayload[]>) { export function* executeActionSaga(action: ReduxAction<ActionPayload[]>) {
@ -128,10 +134,9 @@ export function* fetchActionSaga(actionPayload: ReduxAction<{ id: string }>) {
export function* runActionSaga(actionPayload: ReduxAction<{ id: string }>) { export function* runActionSaga(actionPayload: ReduxAction<{ id: string }>) {
const id = actionPayload.payload.id; const id = actionPayload.payload.id;
const response: ActionApiResponse = yield ActionAPI.executeAction({ const response: ActionApiResponse = yield call(executeAPIQueryActionSaga, {
actionId: id, actionId: id,
}); });
let payload = response; let payload = response;
if (response.responseMeta && response.responseMeta.error) { if (response.responseMeta && response.responseMeta.error) {
payload = { payload = {

View File

@ -0,0 +1,26 @@
import { all, select, takeLatest, put } from "redux-saga/effects";
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
import { AppState } from "../reducers";
import { bindingsMapSuccess } from "../actions/bindingActions";
function* createUpdateBindingsMapData() {
const data: AppState = yield select();
const map: Record<string, string> = {};
data.entities.actions.data.forEach(action => {
map[action.name] = `$.apiData.${action.id}`;
});
Object.keys(data.entities.canvasWidgets).forEach(widgetId => {
const name = data.entities.canvasWidgets[widgetId].widgetName;
map[name] = `$.canvasWidgets.${widgetId}`;
});
yield put(bindingsMapSuccess(map));
}
export default function* watchBindingsSagas() {
yield all([
takeLatest(
ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_INIT,
createUpdateBindingsMapData,
),
]);
}

View File

@ -0,0 +1,37 @@
import { all, select, put, takeLatest, take } from "redux-saga/effects";
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
import {
getPropertyPaneConfigsId,
getCurrentPageId,
} from "../selectors/editorSelectors";
import { fetchEditorConfigs } from "../actions/configsActions";
import { fetchPage, fetchPageList } from "../actions/pageActions";
import { fetchActions } from "../actions/actionActions";
import { fetchResources } from "../actions/resourcesActions";
import { createUpdateBindingsMap } from "../actions/bindingActions";
function* fetchAppDataSaga() {
// Step 1: Start getting all the data needed by the app
const propertyPaneConfigsId = yield select(getPropertyPaneConfigsId);
const currentPageId = yield select(getCurrentPageId);
yield all([
put(fetchPageList()),
put(fetchEditorConfigs(propertyPaneConfigsId)),
put(fetchPage(currentPageId)),
put(fetchActions()),
put(fetchResources()),
]);
// Step 2: Wait for all data to be in the state
yield all([
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
take(ReduxActionTypes.UPDATE_CANVAS),
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
take(ReduxActionTypes.FETCH_RESOURCES_SUCCESS),
]);
// Step 3: Create the bindings map;
yield put(createUpdateBindingsMap());
}
export default function* watchInitSagas() {
yield all([takeLatest(ReduxActionTypes.INIT_APP_DATA, fetchAppDataSaga)]);
}

View File

@ -7,8 +7,12 @@ import errorSagas from "./ErrorSagas";
import configsSagas from "./ConfigsSagas"; import configsSagas from "./ConfigsSagas";
import applicationSagas from "./ApplicationSagas"; import applicationSagas from "./ApplicationSagas";
import { watchResourcesSagas } from "./ResourcesSagas"; import { watchResourcesSagas } from "./ResourcesSagas";
import initSagas from "./InitSagas";
import bindingsSagas from "./BindingsSagas";
export function* rootSaga() { export function* rootSaga() {
yield all([ yield all([
spawn(initSagas),
spawn(pageSagas), spawn(pageSagas),
spawn(fetchWidgetCardsSaga), spawn(fetchWidgetCardsSaga),
spawn(watchActionSagas), spawn(watchActionSagas),
@ -17,5 +21,6 @@ export function* rootSaga() {
spawn(configsSagas), spawn(configsSagas),
spawn(watchResourcesSagas), spawn(watchResourcesSagas),
spawn(applicationSagas), spawn(applicationSagas),
spawn(bindingsSagas),
]); ]);
} }

View File

@ -1,2 +1,2 @@
// import * as React from "react"; // import * as React from "react";
declare module 'react-base-table'; declare module "react-base-table";