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: {

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,8 +128,6 @@ const ApiResponseView = (props: Props) => {
key: "body", key: "body",
title: "Response Body", title: "Response Body",
panelComponent: ( panelComponent: (
<ResponseBodyWrapper>
{response.body && (
<CodeEditor <CodeEditor
height={500} height={500}
language={"json"} language={"json"}
@ -147,8 +135,6 @@ const ApiResponseView = (props: Props) => {
value: JSON.stringify(response.body, null, 2), 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() {
this.props.fetchConfigs({
propertyPaneConfigsId: this.props.propertyPaneConfigsId,
// widgetCardsPaneId: this.props.widgetCardsPaneId,
// widgetConfigsId: this.props.widgetConfigsId,
});
this.props.fetchCanvasWidgets(this.props.currentPageId);
}
render(): React.ReactNode {
return (
<WidgetFunctionsContext.Provider <WidgetFunctionsContext.Provider
value={{ value={{
executeAction: this.props.executeAction, executeAction: props.executeAction,
updateWidget: this.props.updateWidget, updateWidget: props.updateWidget,
updateWidgetProperty: this.props.updateWidgetProperty, updateWidgetProperty: props.updateWidgetProperty,
}} }}
> >
<EditorWrapper> <EditorWrapper>
<CanvasContainer> <CanvasContainer>
{this.props.dsl && ( {props.dsl && (
<Canvas <Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />
dsl={this.props.dsl}
showPropertyPane={this.props.showPropertyPane}
/>
)} )}
</CanvasContainer> </CanvasContainer>
<PropertyPane /> <PropertyPane />
</EditorWrapper> </EditorWrapper>
</WidgetFunctionsContext.Provider> </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(
yield all(
api.jsonPathKeys.map((jsonPath: string) => { api.jsonPathKeys.map((jsonPath: string) => {
return call(evaluateJSONPathSaga, jsonPath); 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";