Merge branch 'fix/api-pane-p0' into 'release'
Fix various api pane bugs See merge request theappsmith/internal-tools-client!136
This commit is contained in:
commit
5886bccbdc
|
|
@ -2,7 +2,7 @@ import { ReduxActionTypes } from "../constants/ReduxActionConstants";
|
|||
import { RestAction } from "../api/ActionAPI";
|
||||
import { ActionPayload } from "../constants/ActionConstants";
|
||||
|
||||
export const createActionRequest = (payload: RestAction) => {
|
||||
export const createActionRequest = (payload: Partial<RestAction>) => {
|
||||
return {
|
||||
type: ReduxActionTypes.CREATE_ACTION_INIT,
|
||||
payload,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../constants/ReduxActionConstants";
|
||||
import { NamePathBindingMap } from "../constants/BindingsConstants";
|
||||
|
||||
export const createUpdateBindingsMap = (): ReduxActionWithoutPayload => ({
|
||||
export const initBindingMapListener = (): ReduxActionWithoutPayload => ({
|
||||
type: ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_INIT,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
REQUEST_HEADERS,
|
||||
AUTH_CREDENTIALS,
|
||||
} from "../constants/ApiConstants";
|
||||
import { ActionApiResponse, ActionResponse } from "./ActionAPI";
|
||||
import { ActionApiResponse } from "./ActionAPI";
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
|
|
|
|||
|
|
@ -1,24 +1,40 @@
|
|||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
||||
import { FormGroup, IconName, InputGroup, Intent } from "@blueprintjs/core";
|
||||
import { ComponentProps } from "./BaseComponent";
|
||||
|
||||
const InputStyles = css`
|
||||
padding: ${props => `${props.theme.spaces[3]}px ${props.theme.spaces[1]}px`};
|
||||
const TextInput = styled(InputGroup)`
|
||||
flex: 1;
|
||||
border: 1px solid ${props => props.theme.colors.inputInactiveBorders};
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
background-color: ${props => props.theme.colors.textOnDarkBG};
|
||||
&:focus {
|
||||
border-color: ${props => props.theme.colors.secondary};
|
||||
input {
|
||||
border: 1px solid ${props => props.theme.colors.inputInactiveBorders};
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
height: 38px;
|
||||
background-color: ${props => props.theme.colors.textOnDarkBG};
|
||||
outline: 0;
|
||||
&:focus {
|
||||
border-color: ${props => props.theme.colors.secondary};
|
||||
background-color: ${props => props.theme.colors.textOnDarkBG};
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
.bp3-icon {
|
||||
border-radius: 4px 0 0 4px;
|
||||
margin: 0;
|
||||
height: 38px;
|
||||
width: 30px;
|
||||
background-color: ${props => props.theme.colors.menuButtonBGInactive};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
path {
|
||||
fill: ${props => props.theme.colors.textDefault};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Input = styled.input`
|
||||
${InputStyles}
|
||||
`;
|
||||
|
||||
const InputContainer = styled.div`
|
||||
|
|
@ -27,32 +43,26 @@ const InputContainer = styled.div`
|
|||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Error = styled.span`
|
||||
const ErrorText = styled.span`
|
||||
height: 10px;
|
||||
padding: 3px;
|
||||
font-size: 10px;
|
||||
color: ${props => props.theme.colors.error};
|
||||
fontsize: ${props => props.theme.fontSizes[1]};
|
||||
`;
|
||||
|
||||
const TextArea = styled.textarea`
|
||||
${InputStyles}
|
||||
height: 100px;
|
||||
`;
|
||||
|
||||
export interface TextInputProps {
|
||||
placeholderMessage?: string;
|
||||
multiline?: boolean;
|
||||
input?: WrappedFieldInputProps;
|
||||
input?: Partial<WrappedFieldInputProps>;
|
||||
meta?: WrappedFieldMetaProps;
|
||||
icon?: IconName;
|
||||
}
|
||||
|
||||
export const BaseTextInput = (props: TextInputProps) => {
|
||||
const { placeholderMessage, multiline, input, meta } = props;
|
||||
if (multiline) {
|
||||
return <TextArea placeholder={placeholderMessage} {...input} />;
|
||||
}
|
||||
const { placeholderMessage, input, meta, icon } = props;
|
||||
return (
|
||||
<InputContainer>
|
||||
<Input placeholder={placeholderMessage} {...input} />
|
||||
{meta && meta.touched && meta.error && <Error>{meta.error}</Error>}
|
||||
<TextInput {...input} placeholder={placeholderMessage} leftIcon={icon} />
|
||||
<ErrorText>{meta && meta.touched && meta.error}</ErrorText>
|
||||
</InputContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,16 +3,23 @@ import { AnchorButton, IButtonProps, MaybeElement } from "@blueprintjs/core";
|
|||
import styled, { css } from "styled-components";
|
||||
import { TextComponentProps } from "./TextComponent";
|
||||
import { ButtonStyle } from "../../../widgets/ButtonWidget";
|
||||
import { Theme } from "../../../constants/DefaultTheme";
|
||||
|
||||
const getButtonColorStyles = (props: { theme: Theme } & ButtonStyleProps) => {
|
||||
if (props.filled) return props.theme.colors.textOnDarkBG;
|
||||
if (props.styleName) {
|
||||
if (props.styleName === "secondary") {
|
||||
return props.theme.colors.OXFORD_BLUE;
|
||||
}
|
||||
return props.theme.colors[props.styleName];
|
||||
}
|
||||
};
|
||||
|
||||
const ButtonColorStyles = css<ButtonStyleProps>`
|
||||
color: ${props => {
|
||||
if (props.filled) return props.theme.colors.textOnDarkBG;
|
||||
if (props.styleName) {
|
||||
if (props.styleName === "secondary")
|
||||
return props.theme.colors.OXFORD_BLUE;
|
||||
return props.theme.colors[props.styleName];
|
||||
}
|
||||
}};
|
||||
color: ${getButtonColorStyles};
|
||||
svg {
|
||||
fill: ${getButtonColorStyles};
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonWrapper = styled(AnchorButton)<ButtonStyleProps>`
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ const ApiResponseView = (props: Props) => {
|
|||
|
||||
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
||||
responses: state.entities.apiData,
|
||||
isRunning: state.entities.actions.isRunning,
|
||||
isRunning: state.ui.apiPane.isRunning,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withRouter(ApiResponseView));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { DatasourceDataState } from "../../../reducers/entityReducers/datasource
|
|||
import _ from "lodash";
|
||||
import { createDatasource } from "../../../actions/datasourcesActions";
|
||||
import { REST_PLUGIN_ID } from "../../../constants/ApiEditorConstants";
|
||||
import { required } from "../../../utils/validation/common";
|
||||
|
||||
interface ReduxStateProps {
|
||||
datasources: DatasourceDataState;
|
||||
|
|
@ -41,7 +40,6 @@ const DatasourcesField = (
|
|||
onCreateOption={props.createDatasource}
|
||||
format={(value: string) => _.find(options, { value })}
|
||||
parse={(option: { value: string }) => (option ? option.value : null)}
|
||||
validate={required}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { RefObject } from "react";
|
|||
|
||||
export const ReduxActionTypes: { [key: string]: string } = {
|
||||
INIT_EDITOR: "INIT_EDITOR",
|
||||
INIT_SUCCESS: "INIT_SUCCESS",
|
||||
REPORT_ERROR: "REPORT_ERROR",
|
||||
FLUSH_ERRORS: "FLUSH_ERRORS",
|
||||
UPDATE_CANVAS: "UPDATE_CANVAS",
|
||||
|
|
@ -95,8 +96,10 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
|
|||
PROPERTY_PANE_ERROR: "PROPERTY_PANE_ERROR",
|
||||
FETCH_ACTIONS_ERROR: "FETCH_ACTIONS_ERROR",
|
||||
UPDATE_WIDGET_PROPERTY_ERROR: "UPDATE_WIDGET_PROPERTY_ERROR",
|
||||
CREATE_ACTION_ERROR: "CREATE_ACTION_ERROR",
|
||||
UPDATE_ACTION_ERROR: "UPDATE_ACTION_ERROR",
|
||||
DELETE_ACTION_ERROR: "DELETE_ACTION_ERROR",
|
||||
EXECUTE_ACTION_ERROR: "EXECUTE_ACTION_ERROR",
|
||||
FETCH_DATASOURCES_ERROR: "FETCH_DATASOURCES_ERROR",
|
||||
CREATE_DATASOURCE_ERROR: "CREATE_DATASOURCE_ERROR",
|
||||
FETCH_PUBLISHED_PAGE_ERROR: "FETCH_PUBLISHED_PAGE_ERROR",
|
||||
|
|
|
|||
|
|
@ -40,23 +40,16 @@ const Form = styled.form`
|
|||
}
|
||||
`;
|
||||
|
||||
const MainConfiguration = styled.div`
|
||||
padding-top: 10px;
|
||||
`;
|
||||
|
||||
const SecondaryWrapper = styled.div`
|
||||
display: flex;
|
||||
height: 100%;
|
||||
border-top: 1px solid #d0d7dd;
|
||||
`;
|
||||
|
||||
const ForwardSlash = styled.div`
|
||||
&& {
|
||||
margin: 0 10px;
|
||||
height: 27px;
|
||||
width: 1px;
|
||||
background-color: #d0d7dd;
|
||||
transform: rotate(27deg);
|
||||
align-self: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const RequestParamsWrapper = styled.div`
|
||||
flex: 5;
|
||||
border-right: 1px solid #d0d7dd;
|
||||
|
|
@ -103,48 +96,50 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
|||
} = props;
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormRow>
|
||||
<TextField
|
||||
name="name"
|
||||
placeholderMessage="API Name"
|
||||
validate={required}
|
||||
/>
|
||||
<ActionButtons>
|
||||
<ActionButton
|
||||
text="Delete"
|
||||
styleName="error"
|
||||
onClick={onDeleteClick}
|
||||
loading={isDeleting}
|
||||
<MainConfiguration>
|
||||
<FormRow>
|
||||
<TextField
|
||||
name="name"
|
||||
placeholderMessage="API Name *"
|
||||
validate={required}
|
||||
/>
|
||||
<ActionButton
|
||||
text="Run"
|
||||
styleName="secondary"
|
||||
onClick={onRunClick}
|
||||
loading={isRunning}
|
||||
<ActionButtons>
|
||||
<ActionButton
|
||||
text="Delete"
|
||||
styleName="error"
|
||||
onClick={onDeleteClick}
|
||||
loading={isDeleting}
|
||||
/>
|
||||
<ActionButton
|
||||
text="Run"
|
||||
styleName="secondary"
|
||||
onClick={onRunClick}
|
||||
loading={isRunning}
|
||||
/>
|
||||
<ActionButton
|
||||
text="Save"
|
||||
styleName="primary"
|
||||
filled
|
||||
onClick={onSaveClick}
|
||||
loading={isSaving}
|
||||
/>
|
||||
</ActionButtons>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<DropdownField
|
||||
placeholder="Method"
|
||||
name="actionConfiguration.httpMethod"
|
||||
options={HTTP_METHOD_OPTIONS}
|
||||
/>
|
||||
<ActionButton
|
||||
text="Save"
|
||||
styleName="primary"
|
||||
filled
|
||||
onClick={onSaveClick}
|
||||
loading={isSaving}
|
||||
<DatasourcesField name="datasource.id" />
|
||||
<TextField
|
||||
placeholderMessage="API Path"
|
||||
name="actionConfiguration.path"
|
||||
validate={[apiPathValidation]}
|
||||
icon="slash"
|
||||
/>
|
||||
</ActionButtons>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<DropdownField
|
||||
placeholder="Method"
|
||||
name="actionConfiguration.httpMethod"
|
||||
options={HTTP_METHOD_OPTIONS}
|
||||
/>
|
||||
<DatasourcesField name="datasource.id" />
|
||||
<ForwardSlash />
|
||||
<TextField
|
||||
placeholderMessage="API Path"
|
||||
name="actionConfiguration.path"
|
||||
validate={[required, apiPathValidation]}
|
||||
/>
|
||||
</FormRow>
|
||||
</FormRow>
|
||||
</MainConfiguration>
|
||||
<SecondaryWrapper>
|
||||
<RequestParamsWrapper>
|
||||
<KeyValueFieldArray
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ import { AppState } from "../../reducers";
|
|||
import { RouteComponentProps } from "react-router";
|
||||
import { API_EDITOR_URL } from "../../constants/routes";
|
||||
import { API_EDITOR_FORM_NAME } from "../../constants/forms";
|
||||
import { FORM_INITIAL_VALUES } from "../../constants/ApiEditorConstants";
|
||||
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
|
||||
import { ApiPaneReduxState } from "../../reducers/uiReducers/apiPaneReducer";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface ReduxStateProps {
|
||||
actions: ActionDataState;
|
||||
apiPane: ApiPaneReduxState;
|
||||
formData: any;
|
||||
}
|
||||
interface ReduxActionProps {
|
||||
|
|
@ -36,6 +38,13 @@ type Props = ReduxActionProps &
|
|||
ReduxStateProps &
|
||||
RouteComponentProps<{ id: string }>;
|
||||
|
||||
const EmptyStateContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
class ApiEditor extends React.Component<Props> {
|
||||
componentDidMount(): void {
|
||||
const currentId = this.props.match.params.id;
|
||||
|
|
@ -52,9 +61,6 @@ class ApiEditor extends React.Component<Props> {
|
|||
|
||||
componentDidUpdate(prevProps: Readonly<Props>): void {
|
||||
const currentId = this.props.match.params.id;
|
||||
if (!currentId && prevProps.match.params.id) {
|
||||
this.props.initialize(API_EDITOR_FORM_NAME, FORM_INITIAL_VALUES);
|
||||
}
|
||||
if (currentId && currentId !== prevProps.match.params.id) {
|
||||
const data = this.props.actions.data.filter(
|
||||
action => action.id === currentId,
|
||||
|
|
@ -90,24 +96,36 @@ class ApiEditor extends React.Component<Props> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
actions: { isSaving, isRunning, isDeleting },
|
||||
apiPane: { isSaving, isRunning, isDeleting },
|
||||
match: {
|
||||
params: { id },
|
||||
},
|
||||
} = this.props;
|
||||
return (
|
||||
<ApiEditorForm
|
||||
isSaving={isSaving}
|
||||
isRunning={isRunning}
|
||||
isDeleting={isDeleting}
|
||||
onSubmit={this.handleSubmit}
|
||||
onSaveClick={this.handleSaveClick}
|
||||
onDeleteClick={this.handleDeleteClick}
|
||||
onRunClick={this.handleRunClick}
|
||||
/>
|
||||
<React.Fragment>
|
||||
{id ? (
|
||||
<ApiEditorForm
|
||||
isSaving={isSaving}
|
||||
isRunning={isRunning}
|
||||
isDeleting={isDeleting}
|
||||
onSubmit={this.handleSubmit}
|
||||
onSaveClick={this.handleSaveClick}
|
||||
onDeleteClick={this.handleDeleteClick}
|
||||
onRunClick={this.handleRunClick}
|
||||
/>
|
||||
) : (
|
||||
<EmptyStateContainer>
|
||||
{"Create an api select from the list"}
|
||||
</EmptyStateContainer>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
||||
actions: state.entities.actions,
|
||||
apiPane: state.ui.apiPane,
|
||||
formData: getFormValues(API_EDITOR_FORM_NAME)(state),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes";
|
|||
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
|
||||
import { FormIcons } from "../../icons/FormIcons";
|
||||
import { Spinner } from "@blueprintjs/core";
|
||||
import { ApiPaneReduxState } from "../../reducers/uiReducers/apiPaneReducer";
|
||||
import { BaseTextInput } from "../../components/designSystems/appsmith/TextInputComponent";
|
||||
import { TICK } from "@blueprintjs/icons/lib/esm/generated/iconNames";
|
||||
import { createActionRequest } from "../../actions/actionActions";
|
||||
|
||||
const ApiSidebarWrapper = styled.div`
|
||||
height: 100%;
|
||||
|
|
@ -81,24 +85,81 @@ const CreateNewButton = styled(BaseButton)`
|
|||
}
|
||||
`;
|
||||
|
||||
const CreateApiWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 4fr 1fr;
|
||||
grid-gap: 5px;
|
||||
height: 40px;
|
||||
`;
|
||||
|
||||
interface ReduxStateProps {
|
||||
actions: ActionDataState;
|
||||
apiPane: ApiPaneReduxState;
|
||||
}
|
||||
|
||||
type Props = ReduxStateProps & RouteComponentProps<{ id: string }>;
|
||||
interface ReduxDispatchProps {
|
||||
createAction: (name: string) => void;
|
||||
}
|
||||
|
||||
type Props = ReduxStateProps &
|
||||
ReduxDispatchProps &
|
||||
RouteComponentProps<{ id: string }>;
|
||||
type State = {
|
||||
isCreating: boolean;
|
||||
name: string;
|
||||
};
|
||||
|
||||
class ApiSidebar extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isCreating: false,
|
||||
name: "",
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>): void {
|
||||
if (!prevProps.match.params.id && this.props.match.params.id) {
|
||||
this.setState({
|
||||
isCreating: false,
|
||||
name: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ApiSidebar extends React.Component<Props> {
|
||||
handleCreateNew = () => {
|
||||
const { history } = this.props;
|
||||
history.push(API_EDITOR_URL);
|
||||
this.setState({
|
||||
isCreating: true,
|
||||
name: "",
|
||||
});
|
||||
};
|
||||
|
||||
saveAction = () => {
|
||||
if (this.state.name) {
|
||||
this.props.createAction(this.state.name);
|
||||
} else {
|
||||
this.setState({
|
||||
isCreating: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleNameChange = (e: React.ChangeEvent<{ value: string }>) => {
|
||||
const value = e.target.value;
|
||||
this.setState({
|
||||
name: value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { actions, history, match } = this.props;
|
||||
const { actions, apiPane, history, match } = this.props;
|
||||
const { isCreating } = this.state;
|
||||
const activeActionId = match.params.id;
|
||||
return (
|
||||
<ApiSidebarWrapper>
|
||||
{actions.isFetching && <Spinner size={30} />}
|
||||
{apiPane.isFetching && <Spinner size={30} />}
|
||||
<ApiItemsWrapper>
|
||||
{actions.data.map(action => (
|
||||
<ApiItem
|
||||
|
|
@ -112,18 +173,30 @@ class ApiSidebar extends React.Component<Props> {
|
|||
<ActionName>{action.name}</ActionName>
|
||||
</ApiItem>
|
||||
))}
|
||||
{!activeActionId && !actions.isFetching && (
|
||||
<ApiItem isSelected>
|
||||
<HTTPMethod method="" />
|
||||
<ActionName>New Api</ActionName>
|
||||
</ApiItem>
|
||||
)}
|
||||
</ApiItemsWrapper>
|
||||
<CreateNewButton
|
||||
text="Create new API"
|
||||
icon={FormIcons.ADD_NEW_ICON()}
|
||||
onClick={this.handleCreateNew}
|
||||
/>
|
||||
{isCreating ? (
|
||||
<CreateApiWrapper>
|
||||
<BaseTextInput
|
||||
input={{
|
||||
value: this.state.name,
|
||||
onChange: this.handleNameChange,
|
||||
}}
|
||||
/>
|
||||
<BaseButton
|
||||
icon={TICK}
|
||||
styleName="primary"
|
||||
text=""
|
||||
onClick={this.saveAction}
|
||||
filled
|
||||
/>
|
||||
</CreateApiWrapper>
|
||||
) : (
|
||||
<CreateNewButton
|
||||
text="Create new API"
|
||||
icon={FormIcons.ADD_NEW_ICON()}
|
||||
onClick={this.handleCreateNew}
|
||||
/>
|
||||
)}
|
||||
</ApiSidebarWrapper>
|
||||
);
|
||||
}
|
||||
|
|
@ -131,6 +204,19 @@ class ApiSidebar extends React.Component<Props> {
|
|||
|
||||
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
||||
actions: state.entities.actions,
|
||||
apiPane: state.ui.apiPane,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ApiSidebar);
|
||||
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
|
||||
createAction: (name: string) =>
|
||||
dispatch(
|
||||
createActionRequest({
|
||||
name,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ApiSidebar);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ const PageSelector = styled(DropdownComponent)`
|
|||
flex: 2;
|
||||
`;
|
||||
|
||||
const NotificationText = styled.div`
|
||||
const LoadingContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
margin: 0 10px;
|
||||
`;
|
||||
|
||||
const PreviewPublishSection = styled.div`
|
||||
|
|
@ -37,7 +38,7 @@ const StretchedBreadCrumb = styled(Breadcrumbs)`
|
|||
`;
|
||||
|
||||
type EditorHeaderProps = {
|
||||
notificationText?: string;
|
||||
isSaving?: boolean;
|
||||
pageName: string;
|
||||
onPublish: React.FormEventHandler;
|
||||
onCreatePage: (name: string) => void;
|
||||
|
|
@ -83,9 +84,9 @@ export const EditorHeader = (props: EditorHeaderProps) => {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
<NotificationText>
|
||||
<span>{props.notificationText}</span>
|
||||
</NotificationText>
|
||||
<LoadingContainer>
|
||||
{props.isSaving ? "Saving..." : "All changed Saved"}
|
||||
</LoadingContainer>
|
||||
<PreviewPublishSection>
|
||||
<BaseButton
|
||||
onClick={props.onPublish}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class Editor extends Component<EditorProps> {
|
|||
return (
|
||||
<div>
|
||||
<EditorHeader
|
||||
notificationText={this.props.isSaving ? "Saving page..." : undefined}
|
||||
isSaving={this.props.isSaving}
|
||||
pageName={this.props.currentPageName}
|
||||
onPublish={this.handlePublish}
|
||||
onCreatePage={this.handleCreatePage}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,13 @@ import { RestAction } from "../../api/ActionAPI";
|
|||
|
||||
const initialState: ActionDataState = {
|
||||
data: [],
|
||||
isFetching: false,
|
||||
isRunning: false,
|
||||
isSaving: false,
|
||||
isDeleting: false,
|
||||
};
|
||||
|
||||
export interface ActionDataState {
|
||||
data: RestAction[];
|
||||
isFetching: boolean;
|
||||
isRunning: boolean;
|
||||
isSaving: boolean;
|
||||
isDeleting: boolean;
|
||||
}
|
||||
|
||||
const actionsReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_ACTIONS_INIT]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isFetching: true,
|
||||
}),
|
||||
[ReduxActionTypes.FETCH_ACTIONS_SUCCESS]: (
|
||||
state: ActionDataState,
|
||||
action: ReduxAction<RestAction[]>,
|
||||
|
|
@ -38,11 +26,6 @@ const actionsReducer = createReducer(initialState, {
|
|||
[ReduxActionErrorTypes.FETCH_ACTIONS_ERROR]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
data: [],
|
||||
isFetching: false,
|
||||
}),
|
||||
[ReduxActionTypes.CREATE_ACTION_INIT]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isSaving: true,
|
||||
}),
|
||||
[ReduxActionTypes.CREATE_ACTION_SUCCESS]: (
|
||||
state: ActionDataState,
|
||||
|
|
@ -50,11 +33,6 @@ const actionsReducer = createReducer(initialState, {
|
|||
) => ({
|
||||
...state,
|
||||
data: state.data.concat([action.payload]),
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionTypes.UPDATE_ACTION_INIT]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isSaving: true,
|
||||
}),
|
||||
[ReduxActionTypes.UPDATE_ACTION_SUCCESS]: (
|
||||
state: ActionDataState,
|
||||
|
|
@ -65,19 +43,6 @@ const actionsReducer = createReducer(initialState, {
|
|||
if (d.id === action.payload.data.id) return action.payload.data;
|
||||
return d;
|
||||
}),
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionTypes.EXECUTE_ACTION]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isRunning: true,
|
||||
}),
|
||||
[ReduxActionTypes.EXECUTE_ACTION_SUCCESS]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isRunning: false,
|
||||
}),
|
||||
[ReduxActionTypes.DELETE_ACTION_INIT]: (state: ActionDataState) => ({
|
||||
...state,
|
||||
isDeleting: true,
|
||||
}),
|
||||
[ReduxActionTypes.DELETE_ACTION_SUCCESS]: (
|
||||
state: ActionDataState,
|
||||
|
|
@ -85,7 +50,6 @@ const actionsReducer = createReducer(initialState, {
|
|||
) => ({
|
||||
...state,
|
||||
data: state.data.filter(d => d.id !== action.payload.id),
|
||||
isDeleting: false,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { DatasourceDataState } from "./entityReducers/datasourceReducer";
|
|||
import { AppViewReduxState } from "./uiReducers/appViewReducer";
|
||||
import { ApplicationsReduxState } from "./uiReducers/applicationsReducer";
|
||||
import { BindingsDataState } from "./entityReducers/bindingsReducer";
|
||||
import { ApiPaneReduxState } from "./uiReducers/apiPaneReducer";
|
||||
|
||||
const appReducer = combineReducers({
|
||||
entities: entityReducer,
|
||||
|
|
@ -33,6 +34,7 @@ export interface AppState {
|
|||
errors: ErrorReduxState;
|
||||
appView: AppViewReduxState;
|
||||
applications: ApplicationsReduxState;
|
||||
apiPane: ApiPaneReduxState;
|
||||
};
|
||||
entities: {
|
||||
canvasWidgets: CanvasWidgetsReduxState;
|
||||
|
|
|
|||
84
app/client/src/reducers/uiReducers/apiPaneReducer.ts
Normal file
84
app/client/src/reducers/uiReducers/apiPaneReducer.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { createReducer } from "../../utils/AppsmithUtils";
|
||||
import {
|
||||
ReduxActionTypes,
|
||||
ReduxActionErrorTypes,
|
||||
} from "../../constants/ReduxActionConstants";
|
||||
|
||||
const initialState: ApiPaneReduxState = {
|
||||
isFetching: false,
|
||||
isRunning: false,
|
||||
isSaving: false,
|
||||
isDeleting: false,
|
||||
};
|
||||
|
||||
export interface ApiPaneReduxState {
|
||||
isFetching: boolean;
|
||||
isRunning: boolean;
|
||||
isSaving: boolean;
|
||||
isDeleting: boolean;
|
||||
}
|
||||
|
||||
const apiPaneReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_ACTIONS_INIT]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isFetching: true,
|
||||
}),
|
||||
[ReduxActionTypes.FETCH_ACTIONS_SUCCESS]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isFetching: false,
|
||||
}),
|
||||
[ReduxActionErrorTypes.FETCH_ACTIONS_ERROR]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isFetching: false,
|
||||
}),
|
||||
[ReduxActionTypes.CREATE_ACTION_INIT]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: true,
|
||||
}),
|
||||
[ReduxActionTypes.CREATE_ACTION_SUCCESS]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionErrorTypes.CREATE_ACTION_ERROR]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionTypes.EXECUTE_ACTION]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isRunning: true,
|
||||
}),
|
||||
[ReduxActionTypes.EXECUTE_ACTION_SUCCESS]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isRunning: false,
|
||||
}),
|
||||
[ReduxActionErrorTypes.EXECUTE_ACTION_ERROR]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isRunning: false,
|
||||
}),
|
||||
[ReduxActionTypes.UPDATE_ACTION_INIT]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: true,
|
||||
}),
|
||||
[ReduxActionTypes.UPDATE_ACTION_SUCCESS]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionErrorTypes.UPDATE_ACTION_ERROR]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isSaving: false,
|
||||
}),
|
||||
[ReduxActionTypes.DELETE_ACTION_INIT]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isDeleting: true,
|
||||
}),
|
||||
[ReduxActionTypes.DELETE_ACTION_SUCCESS]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isDeleting: false,
|
||||
}),
|
||||
[ReduxActionErrorTypes.DELETE_ACTION_ERROR]: (state: ApiPaneReduxState) => ({
|
||||
...state,
|
||||
isDeleting: false,
|
||||
}),
|
||||
});
|
||||
|
||||
export default apiPaneReducer;
|
||||
|
|
@ -5,6 +5,7 @@ import propertyPaneReducer from "./propertyPaneReducer";
|
|||
import appViewReducer from "./appViewReducer";
|
||||
import applicationsReducer from "./applicationsReducer";
|
||||
import { widgetSidebarReducer } from "./widgetSidebarReducer";
|
||||
import apiPaneReducer from "./apiPaneReducer";
|
||||
|
||||
const uiReducer = combineReducers({
|
||||
widgetSidebar: widgetSidebarReducer,
|
||||
|
|
@ -13,5 +14,6 @@ const uiReducer = combineReducers({
|
|||
propertyPane: propertyPaneReducer,
|
||||
appView: appViewReducer,
|
||||
applications: applicationsReducer,
|
||||
apiPane: apiPaneReducer,
|
||||
});
|
||||
export default uiReducer;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import {
|
|||
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../constants/routes";
|
||||
import { getDynamicBoundValue } from "../utils/DynamicBindingUtils";
|
||||
import history from "../utils/history";
|
||||
import { createUpdateBindingsMap } from "../actions/bindingActions";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
|
||||
const getDataTree = (state: AppState): DataTree => {
|
||||
return state.entities;
|
||||
|
|
@ -69,56 +69,70 @@ export function* evaluateJSONPathSaga(path: string): any {
|
|||
}
|
||||
|
||||
export function* executeAPIQueryActionSaga(apiAction: ActionPayload) {
|
||||
const api: PageAction = yield select(getAction, apiAction.actionId);
|
||||
|
||||
const executeActionRequest: ExecuteActionRequest = {
|
||||
action: {
|
||||
id: apiAction.actionId,
|
||||
},
|
||||
};
|
||||
if (!_.isNil(api.jsonPathKeys)) {
|
||||
const values: any = _.flatten(
|
||||
yield all(
|
||||
api.jsonPathKeys.map((jsonPath: string) => {
|
||||
return call(evaluateJSONPathSaga, jsonPath);
|
||||
}),
|
||||
),
|
||||
);
|
||||
const dynamicBindings: Record<string, string> = {};
|
||||
api.jsonPathKeys.forEach((key, i) => {
|
||||
dynamicBindings[key] = values[i];
|
||||
});
|
||||
executeActionRequest.params = mapToPropList(dynamicBindings);
|
||||
}
|
||||
const response: ActionApiResponse = yield ActionAPI.executeAction(
|
||||
executeActionRequest,
|
||||
);
|
||||
let payload = createActionResponse(response);
|
||||
if (response.responseMeta && response.responseMeta.error) {
|
||||
payload = createActionErrorResponse(response);
|
||||
if (apiAction.onError) {
|
||||
try {
|
||||
const api: PageAction = yield select(getAction, apiAction.actionId);
|
||||
if (!api) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION,
|
||||
payload: apiAction.onError,
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_ERROR,
|
||||
payload: "No action selected",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const executeActionRequest: ExecuteActionRequest = {
|
||||
action: {
|
||||
id: apiAction.actionId,
|
||||
},
|
||||
};
|
||||
if (!_.isNil(api.jsonPathKeys)) {
|
||||
const values: any = _.flatten(
|
||||
yield all(
|
||||
api.jsonPathKeys.map((jsonPath: string) => {
|
||||
return call(evaluateJSONPathSaga, jsonPath);
|
||||
}),
|
||||
),
|
||||
);
|
||||
const dynamicBindings: Record<string, string> = {};
|
||||
api.jsonPathKeys.forEach((key, i) => {
|
||||
dynamicBindings[key] = values[i];
|
||||
});
|
||||
executeActionRequest.params = mapToPropList(dynamicBindings);
|
||||
}
|
||||
const response: ActionApiResponse = yield ActionAPI.executeAction(
|
||||
executeActionRequest,
|
||||
);
|
||||
let payload = createActionResponse(response);
|
||||
if (response.responseMeta && response.responseMeta.error) {
|
||||
payload = createActionErrorResponse(response);
|
||||
if (apiAction.onError) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION,
|
||||
payload: apiAction.onError,
|
||||
});
|
||||
}
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_ERROR,
|
||||
payload: { [apiAction.actionId]: payload },
|
||||
});
|
||||
} else {
|
||||
if (apiAction.onSuccess) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION,
|
||||
payload: apiAction.onSuccess,
|
||||
});
|
||||
}
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_SUCCESS,
|
||||
payload: { [apiAction.actionId]: payload },
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_ERROR,
|
||||
payload: { [apiAction.actionId]: payload },
|
||||
});
|
||||
} else {
|
||||
if (apiAction.onSuccess) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION,
|
||||
payload: apiAction.onSuccess,
|
||||
});
|
||||
}
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_SUCCESS,
|
||||
payload: { [apiAction.actionId]: payload },
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// TODO(satbir): Refact this to not make this recursive.
|
||||
|
|
@ -131,7 +145,10 @@ export function* executeActionSaga(actionPayloads: ActionPayload[]): any {
|
|||
case "QUERY":
|
||||
return call(executeAPIQueryActionSaga, actionPayload);
|
||||
default:
|
||||
return undefined;
|
||||
return put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_ERROR,
|
||||
payload: "No action type defined",
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
@ -140,6 +157,11 @@ export function* executeActionSaga(actionPayloads: ActionPayload[]): any {
|
|||
export function* executeReduxActionSaga(action: ReduxAction<ActionPayload[]>) {
|
||||
if (!_.isNil(action.payload)) {
|
||||
yield call(executeActionSaga, action.payload);
|
||||
} else {
|
||||
yield put({
|
||||
type: ReduxActionTypes.EXECUTE_ACTION_ERROR,
|
||||
payload: "No action payload",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,33 +186,43 @@ function* dryRunActionSaga(action: ReduxAction<RestAction>) {
|
|||
}
|
||||
|
||||
export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
|
||||
const response: ActionCreateUpdateResponse = yield ActionAPI.createAPI(
|
||||
actionPayload.payload,
|
||||
);
|
||||
if (response.responseMeta.success) {
|
||||
AppToaster.show({
|
||||
message: `${actionPayload.payload.name} Action created`,
|
||||
intent: Intent.SUCCESS,
|
||||
try {
|
||||
const response: ActionCreateUpdateResponse = yield ActionAPI.createAPI(
|
||||
actionPayload.payload,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
AppToaster.show({
|
||||
message: `${actionPayload.payload.name} Action created`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
yield put(createActionSuccess(response.data));
|
||||
history.push(API_EDITOR_ID_URL(response.data.id));
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.CREATE_ACTION_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
yield put(createActionSuccess(response.data));
|
||||
yield put(createUpdateBindingsMap());
|
||||
history.push(API_EDITOR_ID_URL(response.data.id));
|
||||
}
|
||||
}
|
||||
|
||||
export function* fetchActionsSaga() {
|
||||
const response: GenericApiResponse<
|
||||
RestAction[]
|
||||
> = yield ActionAPI.fetchActions();
|
||||
if (response.responseMeta.success) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
RestAction[]
|
||||
> = yield ActionAPI.fetchActions();
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
|
||||
payload: response.responseMeta.status,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -198,41 +230,45 @@ export function* fetchActionsSaga() {
|
|||
export function* updateActionSaga(
|
||||
actionPayload: ReduxAction<{ data: RestAction }>,
|
||||
) {
|
||||
const response: GenericApiResponse<RestAction> = yield ActionAPI.updateAPI(
|
||||
actionPayload.payload.data,
|
||||
);
|
||||
if (response.responseMeta.success) {
|
||||
AppToaster.show({
|
||||
message: `${actionPayload.payload.data.name} Action updated`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
yield put(updateActionSuccess({ data: response.data }));
|
||||
yield put(createUpdateBindingsMap());
|
||||
} else {
|
||||
AppToaster.show({
|
||||
message: "Error occurred when updating action",
|
||||
intent: Intent.DANGER,
|
||||
try {
|
||||
const response: GenericApiResponse<RestAction> = yield ActionAPI.updateAPI(
|
||||
actionPayload.payload.data,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
AppToaster.show({
|
||||
message: `${actionPayload.payload.data.name} Action updated`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
yield put(updateActionSuccess({ data: response.data }));
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.UPDATE_ACTION_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) {
|
||||
const id = actionPayload.payload.id;
|
||||
const response: GenericApiResponse<RestAction> = yield ActionAPI.deleteAction(
|
||||
id,
|
||||
);
|
||||
if (response.responseMeta.success) {
|
||||
AppToaster.show({
|
||||
message: `${response.data.name} Action deleted`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
yield put(deleteActionSuccess({ id }));
|
||||
yield put(createUpdateBindingsMap());
|
||||
history.push(API_EDITOR_URL);
|
||||
} else {
|
||||
AppToaster.show({
|
||||
message: "Error occurred when deleting action",
|
||||
intent: Intent.DANGER,
|
||||
try {
|
||||
const id = actionPayload.payload.id;
|
||||
const response: GenericApiResponse<
|
||||
RestAction
|
||||
> = yield ActionAPI.deleteAction(id);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
AppToaster.show({
|
||||
message: `${response.data.name} Action deleted`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
yield put(deleteActionSuccess({ id }));
|
||||
history.push(API_EDITOR_URL);
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.DELETE_ACTION_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { all, select, takeLatest, put } from "redux-saga/effects";
|
||||
import { all, select, takeLatest, put, call, take } from "redux-saga/effects";
|
||||
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
|
||||
import { AppState } from "../reducers";
|
||||
import { bindingsMapSuccess } from "../actions/bindingActions";
|
||||
|
|
@ -16,11 +16,26 @@ function* createUpdateBindingsMapData() {
|
|||
yield put(bindingsMapSuccess(map));
|
||||
}
|
||||
|
||||
// The listener will keep track of any action
|
||||
// that requires an update of the action and
|
||||
// then call the update function again
|
||||
function* initListener() {
|
||||
while (true) {
|
||||
// list all actions types here
|
||||
yield take([
|
||||
ReduxActionTypes.INIT_SUCCESS,
|
||||
ReduxActionTypes.CREATE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.UPDATE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.DELETE_ACTION_SUCCESS,
|
||||
ReduxActionTypes.UPDATE_CANVAS,
|
||||
ReduxActionTypes.SAVE_PAGE_INIT,
|
||||
]);
|
||||
yield call(createUpdateBindingsMapData);
|
||||
}
|
||||
}
|
||||
|
||||
export default function* watchBindingsSagas() {
|
||||
yield all([
|
||||
takeLatest(
|
||||
ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_INIT,
|
||||
createUpdateBindingsMapData,
|
||||
),
|
||||
takeLatest(ReduxActionTypes.CREATE_UPDATE_BINDINGS_MAP_INIT, initListener),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,24 @@ import DatasourcesApi, {
|
|||
Datasource,
|
||||
} from "../api/DatasourcesApi";
|
||||
import { API_EDITOR_FORM_NAME } from "../constants/forms";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
|
||||
function* fetchDatasourcesSaga() {
|
||||
const response: GenericApiResponse<
|
||||
Datasource[]
|
||||
> = yield DatasourcesApi.fetchDatasources();
|
||||
if (response.responseMeta.success) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
Datasource[]
|
||||
> = yield DatasourcesApi.fetchDatasources();
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR,
|
||||
payload: response.responseMeta.status,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -32,19 +36,23 @@ function* fetchDatasourcesSaga() {
|
|||
function* createDatasourceSaga(
|
||||
actionPayload: ReduxAction<CreateDatasourceConfig>,
|
||||
) {
|
||||
const response: GenericApiResponse<
|
||||
Datasource
|
||||
> = yield DatasourcesApi.createDatasource(actionPayload.payload);
|
||||
if (response.responseMeta.success) {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
Datasource
|
||||
> = yield DatasourcesApi.createDatasource(actionPayload.payload);
|
||||
if (response.responseMeta.success) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
yield put(
|
||||
change(API_EDITOR_FORM_NAME, "datasource.id", response.data.id),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
yield put(change(API_EDITOR_FORM_NAME, "datasource.id", response.data.id));
|
||||
} else {
|
||||
yield put({
|
||||
type: ReduxActionTypes.CREATE_DATASOURCES_ERROR,
|
||||
payload: response.responseMeta.error,
|
||||
type: ReduxActionTypes.CREATE_DATASOURCE_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@ import { fetchEditorConfigs } from "../actions/configsActions";
|
|||
import { fetchPage, fetchPageList } from "../actions/pageActions";
|
||||
import { fetchActions } from "../actions/actionActions";
|
||||
import { fetchDatasources } from "../actions/datasourcesActions";
|
||||
import { createUpdateBindingsMap } from "../actions/bindingActions";
|
||||
import { initBindingMapListener } from "../actions/bindingActions";
|
||||
|
||||
function* initializeEditorSaga() {
|
||||
// Step 1: Start getting all the data needed by the app
|
||||
const propertyPaneConfigsId = yield select(getPropertyPaneConfigsId);
|
||||
const currentPageId = yield select(getCurrentPageId);
|
||||
yield all([
|
||||
put(initBindingMapListener()),
|
||||
put(fetchPageList()),
|
||||
put(fetchEditorConfigs({ propertyPaneConfigsId })),
|
||||
put(fetchPage(currentPageId)),
|
||||
|
|
@ -31,13 +32,16 @@ function* initializeEditorSaga() {
|
|||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
||||
]);
|
||||
// Step 3: Create the bindings map;
|
||||
yield put(createUpdateBindingsMap());
|
||||
// Step 3: Create the success;
|
||||
yield put({
|
||||
type: ReduxActionTypes.INIT_SUCCESS,
|
||||
});
|
||||
}
|
||||
|
||||
export function* initializeAppViewerSaga(
|
||||
action: ReduxAction<{ pageId: string }>,
|
||||
) {
|
||||
yield put(initBindingMapListener());
|
||||
yield all([put(fetchPageList()), put(fetchActions())]);
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
|
|
@ -48,7 +52,9 @@ export function* initializeAppViewerSaga(
|
|||
payload: action.payload,
|
||||
});
|
||||
yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS);
|
||||
yield put(createUpdateBindingsMap());
|
||||
yield put({
|
||||
type: ReduxActionTypes.INIT_SUCCESS,
|
||||
});
|
||||
}
|
||||
|
||||
export default function* watchInitSagas() {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ import { getPageLayoutId } from "./selectors";
|
|||
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
|
||||
import { getEditorConfigs, getWidgets } from "./selectors";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
import { createUpdateBindingsMap } from "../actions/bindingActions";
|
||||
import { UpdateWidgetPropertyPayload } from "../actions/controlActions";
|
||||
import { RenderModes } from "../constants/WidgetConstants";
|
||||
|
||||
export function* fetchPageListSaga() {
|
||||
try {
|
||||
|
|
@ -105,7 +102,6 @@ export function* fetchPageSaga(
|
|||
if (isValidResponse) {
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(fetchPageResponse);
|
||||
yield put(updateCanvas(canvasWidgetsPayload));
|
||||
yield put(createUpdateBindingsMap());
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
@ -143,7 +139,6 @@ export function* fetchPublishedPageSaga(
|
|||
});
|
||||
const canvasWidgetsPayload = getAppViewWidgetsPayload(response);
|
||||
yield put(updateCanvas(canvasWidgetsPayload));
|
||||
yield put(createUpdateBindingsMap());
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
@ -192,20 +187,15 @@ function getLayoutSavePayload(
|
|||
};
|
||||
}
|
||||
|
||||
export function* saveLayoutSaga(
|
||||
updateLayoutAction: ReduxAction<{
|
||||
widgets: { [widgetId: string]: FlattenedWidgetProps };
|
||||
}>,
|
||||
) {
|
||||
export function* saveLayoutSaga() {
|
||||
try {
|
||||
const { widgets } = updateLayoutAction.payload;
|
||||
const widgets = yield select(getWidgets);
|
||||
const editorConfigs = yield select(getEditorConfigs) as any;
|
||||
|
||||
yield put({
|
||||
type: ReduxActionTypes.SAVE_PAGE_INIT,
|
||||
payload: getLayoutSavePayload(widgets, editorConfigs),
|
||||
});
|
||||
put(createUpdateBindingsMap());
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.SAVE_PAGE_ERROR,
|
||||
|
|
@ -216,38 +206,6 @@ export function* saveLayoutSaga(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(abhinav): This has redundant code. The only thing different here is the lack of state update.
|
||||
// For now, this is fire and forget.
|
||||
export function* asyncSaveLayout(
|
||||
actionPayload: ReduxAction<UpdateWidgetPropertyPayload>,
|
||||
) {
|
||||
if (actionPayload.payload.renderMode === RenderModes.PAGE) return;
|
||||
try {
|
||||
const widgets = yield select(getWidgets);
|
||||
const editorConfigs = yield select(getEditorConfigs) as any;
|
||||
|
||||
const request: SavePageRequest = getLayoutSavePayload(
|
||||
widgets,
|
||||
editorConfigs,
|
||||
);
|
||||
|
||||
const savePageResponse: SavePageResponse = yield call(
|
||||
PageApi.savePage,
|
||||
request,
|
||||
);
|
||||
if (!validateResponse(savePageResponse)) {
|
||||
throw Error("Error when saving layout");
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.UPDATE_WIDGET_PROPERTY_ERROR,
|
||||
payload: {
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function* createPageSaga(
|
||||
createPageAction: ReduxAction<CreatePageRequest>,
|
||||
) {
|
||||
|
|
@ -286,13 +244,8 @@ export default function* pageSagas() {
|
|||
),
|
||||
takeLatest(ReduxActionTypes.SAVE_PAGE_INIT, savePageSaga),
|
||||
takeEvery(ReduxActionTypes.UPDATE_LAYOUT, saveLayoutSaga),
|
||||
// No need to save layout everytime a property is updated.
|
||||
// We save the latest request to update layout.
|
||||
takeLatest(ReduxActionTypes.UPDATE_WIDGET_PROPERTY, asyncSaveLayout),
|
||||
takeLatest(
|
||||
ReduxActionTypes.UPDATE_WIDGET_DYNAMIC_PROPERTY,
|
||||
asyncSaveLayout,
|
||||
),
|
||||
takeLatest(ReduxActionTypes.UPDATE_WIDGET_PROPERTY, saveLayoutSaga),
|
||||
takeLatest(ReduxActionTypes.UPDATE_WIDGET_DYNAMIC_PROPERTY, saveLayoutSaga),
|
||||
takeLatest(ReduxActionTypes.CREATE_PAGE_INIT, createPageSaga),
|
||||
takeLatest(ReduxActionTypes.FETCH_PAGE_LIST_INIT, fetchPageListSaga),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects";
|
|||
import { getNextWidgetName } from "../utils/AppsmithUtils";
|
||||
import { UpdateWidgetPropertyPayload } from "../actions/controlActions";
|
||||
import { DATA_BIND_REGEX } from "../constants/BindingsConstants";
|
||||
import { createUpdateBindingsMap } from "../actions/bindingActions";
|
||||
|
||||
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
|
||||
try {
|
||||
|
|
@ -57,8 +56,6 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
|
|||
type: ReduxActionTypes.UPDATE_LAYOUT,
|
||||
payload: { widgets },
|
||||
});
|
||||
// TODO might be a potential performance choke point.
|
||||
yield put(createUpdateBindingsMap());
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
|
||||
|
|
@ -84,7 +81,6 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
|
|||
type: ReduxActionTypes.UPDATE_LAYOUT,
|
||||
payload: { widgets },
|
||||
});
|
||||
yield put(createUpdateBindingsMap());
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { API_PATH_START_WITH_SLASH_ERROR } from "../../constants/validations";
|
||||
|
||||
export const apiPathValidation = (value: string) => {
|
||||
if (value.startsWith("/")) return API_PATH_START_WITH_SLASH_ERROR;
|
||||
if (value && value.startsWith("/")) return API_PATH_START_WITH_SLASH_ERROR;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ abstract class BaseWidget<
|
|||
*/
|
||||
executeAction(actionPayloads?: ActionPayload[]): void {
|
||||
const { executeAction } = this.context;
|
||||
executeAction && executeAction(actionPayloads);
|
||||
executeAction && !_.isNil(actionPayloads) && executeAction(actionPayloads);
|
||||
}
|
||||
|
||||
updateWidgetProperty(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user