Merge branch 'master' into release

This commit is contained in:
Nikhil Nandagopal 2020-10-12 19:08:23 +05:30
commit c550e9202f
23 changed files with 462 additions and 61 deletions

View File

@ -58,7 +58,6 @@ describe("Binding the multiple widgets and validating default data", function()
);
});
it("Input widget test with default value update with query data", function() {
cy.SearchEntityandOpen("Input1");
cy.get(widgetsPage.defaultInput).type(testdata.defaultInputQuery);

View File

@ -1,5 +1,4 @@
const datasource = require("../../../locators/DatasourcesEditor.json");
let pageid;
describe("Create, test, save then delete a mongo datasource", function() {
it("Create, test, save then delete a mongo datasource", function() {

View File

@ -1,11 +1,38 @@
const datasource = require("../../../locators/DatasourcesEditor.json");
const queryEditor = require("../../../locators/QueryEditor.json");
describe("Create, test, save then delete a postgres datasource", function() {
let datasourceName;
describe("Postgres datasource test cases", function() {
it("Create, test, save then delete a postgres datasource", function() {
cy.NavigateToDatasourceEditor();
cy.get(datasource.PostgreSQL).click();
cy.getPluginFormsAndCreateDatasource();
cy.fillPostgresDatasourceForm();
cy.testSaveDeleteDatasource();
cy.get("@createDatasource").then(httpResponse => {
datasourceName = httpResponse.response.body.data.name;
});
cy.testSaveDatasource();
});
it("Create a new query from the datasource editor", function() {
cy.saveDatasource();
cy.get(datasource.createQuerty).click();
cy.wait("@createNewApi").should(
"have.nested.property",
"response.body.responseMeta.status",
201,
);
cy.get(queryEditor.deleteQuery).click();
cy.wait("@deleteAction").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
cy.GlobalSearchEntity(`${datasourceName}`);
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
cy.deleteDataSource();
});
});

View File

@ -224,7 +224,6 @@ describe("Table Widget Functionality", function() {
cy.get(publish.canvas)
.first()
.click();
});
});

View File

@ -86,6 +86,7 @@ describe("Entity explorer tests related to copy query", function() {
cy.log("sliced id :" + updatedName);
cy.EditEntityNameByDoubleClick(datasourceName, updatedName);
cy.SearchEntityandOpen(updatedName);
cy.get(datasource.editDatasource).click();
cy.testSaveDatasource();
cy.hoverAndClick();
cy.get(apiwidget.delete).click({ force: true });

View File

@ -50,6 +50,7 @@ describe("Entity explorer datasource structure", function() {
"response.body.responseMeta.status",
200,
);
cy.deletePostgresDatasource(datasourceName);
});

View File

@ -44,6 +44,8 @@ describe("Create a query with a mongo datasource, run, save and then delete the
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
});
cy.get(datasource.editDatasource).click();
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",

View File

@ -50,6 +50,7 @@ describe("Create a query with a postgres datasource, run, save and then delete t
it("Deletes a datasource", () => {
cy.NavigateToDatasourceEditor();
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
cy.get(datasource.editDatasource).click();
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(

View File

@ -14,5 +14,7 @@
"sectionAuthentication": "[data-cy=section-Authentication]",
"sectionSSL": "[data-cy=section-SSL\\ \\(optional\\)]",
"addDatasourceEntity": ".plugins .t--entity-add-btn",
"PostgresEntity": ".t--entity-name:contains(PostgreSQL)"
"PostgresEntity": ".t--entity-name:contains(PostgreSQL)",
"createQuerty": ".t--create-query",
"editDatasource": ".t--edit-datasource"
}

View File

@ -1315,6 +1315,8 @@ Cypress.Commands.add("testSaveDeleteDatasource", () => {
200,
);
cy.get(datasourceEditor.editDatasource).click();
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
@ -1360,6 +1362,7 @@ Cypress.Commands.add("saveDatasource", () => {
Cypress.Commands.add("testSaveDatasource", () => {
cy.saveDatasource();
cy.get(datasourceEditor.editDatasource).click();
cy.testDatasource();
});
@ -1420,7 +1423,7 @@ Cypress.Commands.add("createPostgresDatasource", () => {
Cypress.Commands.add("deletePostgresDatasource", datasourceName => {
cy.NavigateToDatasourceEditor();
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
cy.get(datasourceEditor.editDatasource).click();
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",

View File

@ -34,6 +34,7 @@ import { setThemeMode } from "actions/themeActions";
import { connect } from "react-redux";
import * as Sentry from "@sentry/react";
import AnalyticsUtil from "utils/AnalyticsUtil";
const SentryRoute = Sentry.withSentryRouting(Route);
const loadingIndicator = <PageLoadingBar />;
@ -51,15 +52,25 @@ function changeAppBackground(currentTheme: any) {
}
class AppRouter extends React.Component<any, any> {
unlisten: any;
componentDidMount() {
// This is needed for the route switch.
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: window.location.pathname });
this.unlisten = history.listen((location: any) => {
AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: location.pathname });
changeAppBackground(this.props.currentTheme);
});
}
componentWillUnmount() {
this.unlisten();
}
render() {
const { currentTheme } = this.props;
// This is needed for the theme switch.
changeAppBackground(currentTheme);
// This is needed for the route switch.
history.listen(() => {
changeAppBackground(currentTheme);
});
return (
<Router history={history}>
<Suspense fallback={loadingIndicator}>

View File

@ -80,6 +80,16 @@ export const deleteDatasource = (payload: Partial<Datasource>) => {
};
};
export const setDatsourceEditorMode = (payload: {
id: string;
viewMode: boolean;
}) => {
return {
type: ReduxActionTypes.SET_DATASOURCE_EDITOR_MODE,
payload,
};
};
export const fetchDatasources = () => {
return {
type: ReduxActionTypes.FETCH_DATASOURCES_INIT,

View File

@ -2,6 +2,7 @@ import API from "./Api";
import { GenericApiResponse } from "./ApiResponses";
import { AxiosPromise } from "axios";
import { DEFAULT_TEST_DATA_SOURCE_TIMEOUT_MS } from "constants/ApiConstants";
import { Property } from "entities/Action";
interface DatasourceAuthentication {
authType?: string;
@ -45,7 +46,7 @@ export interface Datasource {
url: string;
authentication?: DatasourceAuthentication;
properties?: Record<string, string>;
headers?: Record<string, string>;
headers?: Property[];
databaseName?: string;
};
invalids?: string[];

View File

@ -63,6 +63,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
UPDATE_ACTION_INIT: "UPDATE_ACTION_INIT",
UPDATE_ACTION_SUCCESS: "UPDATE_ACTION_SUCCESS",
DELETE_ACTION_INIT: "DELETE_ACTION_INIT",
SET_DATASOURCE_EDITOR_MODE: "SET_DATASOURCE_EDITOR_MODE",
DELETE_ACTION_SUCCESS: "DELETE_ACTION_SUCCESS",
SHOW_RUN_ACTION_CONFIRM_MODAL: "SHOW_RUN_ACTION_CONFIRM_MODAL",
CANCEL_RUN_ACTION_CONFIRM_MODAL: "CANCEL_RUN_ACTION_CONFIRM_MODAL",

View File

@ -0,0 +1,254 @@
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router";
import { AppState } from "reducers";
import { isNil, map, get } from "lodash";
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
import { getDatasource, getPlugin } from "selectors/entitiesSelector";
import { Colors } from "constants/Colors";
import { HeaderIcons } from "icons/HeaderIcons";
import history from "utils/history";
import styled from "styled-components";
import { createActionRequest } from "actions/actionActions";
import {
API_EDITOR_URL_WITH_SELECTED_PAGE_ID,
QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID,
} from "constants/routes";
import { createNewApiName, createNewQueryName } from "utils/AppsmithUtils";
import { getCurrentPageId } from "selectors/editorSelectors";
import { Datasource } from "api/DatasourcesApi";
import { DEFAULT_API_ACTION } from "constants/ApiEditorConstants";
import { ApiActionConfig, PluginType } from "entities/Action";
import { AppToaster } from "components/editorComponents/ToastComponent";
const ConnectedText = styled.div`
color: ${Colors.GREEN};
font-size: 17px;
font-weight: bold;
display: flex;
align-items: center;
`;
const Header = styled.div`
flex-direction: row;
display: flex;
align-items: center;
justify-content: space-between;
`;
const Wrapper = styled.div`
display: flex;
flex-direction: column;
border-top: 1px solid #d0d7dd;
border-bottom: 1px solid #d0d7dd;
padding-top: 24px;
padding-bottom: 24px;
margin-top: 18px;
`;
const ActionButton = styled(BaseButton)`
&&& {
max-width: 140px;
align-self: center;
}
`;
const Key = styled.div`
color: #6d6d6d;
font-size: 14px;
font-weight: 500;
display: inline-block;
`;
const Value = styled.div`
font-size: 14px;
font-weight: 400;
display: inline-block;
margin-left: 5px;
`;
const ValueWrapper = styled.div`
display: inline-block;
margin-left: 10px;
`;
const Connected = () => {
const params = useParams<{ datasourceId: string; applicationId: string }>();
const datasource = useSelector((state: AppState) =>
getDatasource(state, params.datasourceId),
);
const dispatch = useDispatch();
const actions = useSelector((state: AppState) => state.entities.actions);
const currentPageId = useSelector(getCurrentPageId);
const datasourceFormConfigs = useSelector(
(state: AppState) => state.entities.plugins.formConfigs,
);
const plugin = useSelector((state: AppState) =>
getPlugin(state, datasource?.pluginId ?? ""),
);
const isDBDatasource = plugin?.type === PluginType.DB;
const createQueryAction = useCallback(() => {
const newQueryName = createNewQueryName(actions, currentPageId || "");
dispatch(
createActionRequest({
name: newQueryName,
pageId: currentPageId,
pluginId: datasource?.pluginId,
datasource: {
id: datasource?.id,
},
actionConfiguration: {},
eventData: {
actionType: "Query",
from: "datasource-pane",
},
}),
);
history.push(
QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID(
params.applicationId,
currentPageId,
currentPageId,
),
);
}, [dispatch, actions, currentPageId, params.applicationId, datasource]);
const createApiAction = useCallback(() => {
const newApiName = createNewApiName(actions, currentPageId || "");
const headers = datasource?.datasourceConfiguration?.headers ?? [];
const defaultAction: Partial<ApiActionConfig> | undefined = {
...DEFAULT_API_ACTION.actionConfiguration,
headers: headers.length
? headers
: DEFAULT_API_ACTION.actionConfiguration?.headers,
};
if (!datasource?.datasourceConfiguration?.url) {
AppToaster.show({
message: "Unable to create API. Try adding a url to the datasource",
type: "error",
});
return;
}
dispatch(
createActionRequest({
name: newApiName,
pageId: currentPageId,
pluginId: datasource?.pluginId,
datasource: {
id: datasource?.id,
},
eventData: {
actionType: "API",
from: "datasource-pane",
},
actionConfiguration: {
...defaultAction,
},
}),
);
history.push(
API_EDITOR_URL_WITH_SELECTED_PAGE_ID(
params.applicationId,
currentPageId,
currentPageId,
),
);
}, [dispatch, actions, currentPageId, params.applicationId, datasource]);
const currentFormConfig: Array<any> =
datasourceFormConfigs[datasource?.pluginId ?? ""];
return (
<Wrapper>
<Header>
<ConnectedText>
<HeaderIcons.SAVE_SUCCESS
color={Colors.GREEN}
height={30}
width={30}
/>
<div style={{ marginLeft: "12px" }}>Datasource Saved</div>
</ConnectedText>
<ActionButton
className="t--create-query"
icon={"plus"}
text={isDBDatasource ? "New Query" : "New API"}
filled
accent="primary"
onClick={isDBDatasource ? createQueryAction : createApiAction}
/>
</Header>
<div style={{ marginTop: "30px" }}>
{!isNil(currentFormConfig)
? renderSection(currentFormConfig[0], datasource)
: undefined}
</div>
</Wrapper>
);
};
const renderSection = (
section: any,
datasource: Datasource | undefined,
): any => {
return (
<>
{map(section.children, subSection => {
if ("children" in subSection) {
return renderSection(subSection, datasource);
} else {
try {
const { label, configProperty, controlType } = subSection;
let value = get(datasource, configProperty);
if (controlType === "KEYVALUE_ARRAY") {
const configPropertyInfo = configProperty.split("[*].");
const values = get(datasource, configPropertyInfo[0], null);
if (values) {
const keyValuePair = values[0];
value = keyValuePair[configPropertyInfo[1]];
} else {
value = "";
}
}
if (controlType === "KEY_VAL_INPUT") {
return (
<div style={{ marginTop: 9 }}>
<Key>{label}</Key>
{value.map((val: { key: string; value: string }) => {
return (
<div key={val.key}>
<div style={{ display: "inline-block" }}>
<Key>Key: </Key>
<Value>{val.key}</Value>
</div>
<ValueWrapper>
<Key>Value: </Key>
<Value>{val.value}</Value>
</ValueWrapper>
</div>
);
})}
</div>
);
}
return (
<div style={{ marginTop: 9 }}>
<Key>{label}: </Key> <Value>{value}</Value>
</div>
);
} catch (e) {}
}
})}
</>
);
};
export default Connected;

View File

@ -11,6 +11,7 @@ import FormTitle from "./FormTitle";
import { ControlProps } from "components/formControls/BaseControl";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
import Connected from "./Connected";
import FormControlFactory from "utils/FormControlFactory";
import { HelpBaseURL, HelpMap } from "constants/HelpConstants";
@ -27,6 +28,7 @@ interface DatasourceDBEditorProps {
onSave: (formValues: Datasource) => void;
onTest: (formValus: Datasource) => void;
handleDelete: (id: string) => void;
setDatasourceEditorMode: (id: string, viewMode: boolean) => void;
selectedPluginPackage: string;
isSaving: boolean;
isDeleting: boolean;
@ -39,10 +41,11 @@ interface DatasourceDBEditorProps {
formConfig: any[];
isNewDatasource: boolean;
pluginImage: string;
viewMode: boolean;
}
interface DatasourceDBEditorState {
isNameEditable: boolean;
viewMode: boolean;
}
type Props = DatasourceDBEditorProps &
@ -74,6 +77,13 @@ export const FormTitleContainer = styled.div`
flex-direction: row;
display: flex;
align-items: center;
`;
export const Header = styled.div`
flex-direction: row;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16px;
`;
@ -123,16 +133,32 @@ class DatasourceDBEditor extends React.Component<
super(props);
this.state = {
isNameEditable: true,
viewMode: true,
};
this.requiredFields = {};
this.configDetails = {};
}
componentDidMount() {
if (this.props.isNewDatasource) {
this.setState({
viewMode: false,
});
}
}
componentDidUpdate(prevProps: Props) {
if (prevProps.datasourceId !== this.props.datasourceId) {
this.requiredFields = {};
this.configDetails = {};
this.setState({
viewMode: true,
});
}
if (!this.state.viewMode && this.props.viewMode) {
this.setState({
viewMode: true,
});
}
}
@ -238,7 +264,7 @@ class DatasourceDBEditor extends React.Component<
if (newValues.length) {
formData = _.set(formData, properties[0], newValues);
} else {
_.unset(formData, properties[0]);
formData = _.set(formData, properties[0], []);
}
} else if (controlType === "KEY_VAL_INPUT") {
if (checked[configProperty]) continue;
@ -259,7 +285,7 @@ class DatasourceDBEditor extends React.Component<
if (newValues.length) {
formData = _.set(formData, configProperty, newValues);
} else {
_.unset(formData, configProperty);
formData = _.set(formData, configProperty, []);
}
}
}
@ -295,6 +321,7 @@ class DatasourceDBEditor extends React.Component<
datasourceId,
handleDelete,
} = this.props;
const { viewMode } = this.state;
return (
<form
@ -319,10 +346,28 @@ class DatasourceDBEditor extends React.Component<
{" Back"}
</span>
<br />
<FormTitleContainer>
<PluginImage src={this.props.pluginImage} alt="Datasource" />
<FormTitle focusOnMount={this.props.isNewDatasource} />
</FormTitleContainer>
<Header>
<FormTitleContainer>
<PluginImage src={this.props.pluginImage} alt="Datasource" />
<FormTitle focusOnMount={this.props.isNewDatasource} />
</FormTitleContainer>
{viewMode && (
<ActionButton
className="t--edit-datasource"
text="EDIT"
accent="secondary"
onClick={() => {
this.setState({
viewMode: false,
});
this.props.setDatasourceEditorMode(
this.props.datasourceId,
false,
);
}}
/>
)}
</Header>
{cloudHosting && (
<CollapsibleWrapper>
<CollapsibleHelp>
@ -338,36 +383,42 @@ class DatasourceDBEditor extends React.Component<
</CollapsibleHelp>
</CollapsibleWrapper>
)}
{!_.isNil(sections)
? _.map(sections, this.renderMainSection)
: undefined}
<SaveButtonContainer>
<ActionButton
className="t--delete-datasource"
text="Delete"
accent="error"
loading={isDeleting}
onClick={() => handleDelete(datasourceId)}
/>
{!viewMode ? (
<>
{!_.isNil(sections)
? _.map(sections, this.renderMainSection)
: undefined}
<SaveButtonContainer>
<ActionButton
className="t--delete-datasource"
text="Delete"
accent="error"
loading={isDeleting}
onClick={() => handleDelete(datasourceId)}
/>
<ActionButton
className="t--test-datasource"
text="Test"
loading={isTesting}
accent="secondary"
onClick={this.test}
/>
<StyledButton
className="t--save-datasource"
onClick={this.save}
text="Save"
disabled={this.validate()}
loading={isSaving}
intent="primary"
filled
size="small"
/>
</SaveButtonContainer>
<ActionButton
className="t--test-datasource"
text="Test"
loading={isTesting}
accent="secondary"
onClick={this.test}
/>
<StyledButton
className="t--save-datasource"
onClick={this.save}
text="Save"
disabled={this.validate()}
loading={isSaving}
intent="primary"
filled
size="small"
/>
</SaveButtonContainer>
</>
) : (
<Connected />
)}
</form>
);
};

View File

@ -13,6 +13,7 @@ import {
testDatasource,
deleteDatasource,
switchDatasource,
setDatsourceEditorMode,
} from "actions/datasourceActions";
import { DATASOURCE_DB_FORM } from "constants/forms";
import DatasourceHome from "./DatasourceHome";
@ -31,6 +32,7 @@ interface ReduxStateProps {
newDatasource: string;
pluginImages: Record<string, string>;
pluginId: string;
viewMode: boolean;
}
type Props = ReduxStateProps &
@ -80,6 +82,8 @@ class DataSourceEditor extends React.Component<Props> {
newDatasource,
pluginImages,
pluginId,
viewMode,
setDatasourceEditorMode,
} = this.props;
return (
@ -102,6 +106,8 @@ class DataSourceEditor extends React.Component<Props> {
loadingFormConfigs={loadingFormConfigs}
formConfig={formConfig}
handleDelete={deleteDatasource}
viewMode={viewMode}
setDatasourceEditorMode={setDatasourceEditorMode}
/>
) : (
<DatasourceHome
@ -138,6 +144,7 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
formConfig: formConfigs[datasourcePane.selectedPlugin] || [],
loadingFormConfigs,
newDatasource: datasourcePane.newDatasource,
viewMode: datasourcePane.viewMode[datasource?.id ?? ""] ?? true,
};
};
@ -149,6 +156,8 @@ const mapDispatchToProps = (dispatch: any): DatasourcePaneFunctions => ({
testDatasource: (data: Datasource) => dispatch(testDatasource(data)),
deleteDatasource: (id: string) => dispatch(deleteDatasource({ id })),
switchDatasource: (id: string) => dispatch(switchDatasource(id)),
setDatasourceEditorMode: (id: string, viewMode: boolean) =>
dispatch(setDatsourceEditorMode({ id, viewMode })),
});
export interface DatasourcePaneFunctions {
@ -157,6 +166,7 @@ export interface DatasourcePaneFunctions {
testDatasource: (data: Datasource) => void;
deleteDatasource: (id: string) => void;
switchDatasource: (id: string) => void;
setDatasourceEditorMode: (id: string, viewMode: boolean) => void;
}
export default connect(mapStateToProps, mapDispatchToProps)(DataSourceEditor);

View File

@ -13,7 +13,6 @@ import {
QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID,
DATA_SOURCES_EDITOR_URL,
} from "constants/routes";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { QueryAction } from "entities/Action";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";

View File

@ -10,6 +10,7 @@ const initialState: DatasourcePaneReduxState = {
actionRouteInfo: {},
expandDatasourceId: "",
newDatasource: "",
viewMode: {},
};
export interface DatasourcePaneReduxState {
@ -23,6 +24,7 @@ export interface DatasourcePaneReduxState {
applicationId: string;
}>;
newDatasource: string;
viewMode: Record<string, boolean>;
}
const datasourcePaneReducer = createReducer(initialState, {
@ -92,6 +94,18 @@ const datasourcePaneReducer = createReducer(initialState, {
expandDatasourceId: action.payload.id,
};
},
[ReduxActionTypes.SET_DATASOURCE_EDITOR_MODE]: (
state: DatasourcePaneReduxState,
action: ReduxAction<{ id: string; viewMode: boolean }>,
) => {
return {
...state,
viewMode: {
...state.viewMode,
[action.payload.id]: action.payload.viewMode,
},
};
},
[ReduxActionTypes.EXPAND_DATASOURCE_ENTITY]: (
state: DatasourcePaneReduxState,
action: ReduxAction<string>,

View File

@ -298,11 +298,12 @@ export function* executeActionSaga(
);
try {
const api: RestAction = yield select(getAction, actionId);
const currentAppId = yield select(getCurrentApplicationId);
AnalyticsUtil.logEvent("EXECUTE_ACTION", {
type: api.pluginType,
name: api.name,
pageId: api.pageId,
appId: currentAppId,
});
if (api.confirmBeforeExecute) {
const confirmed = yield call(confirmRunActionSaga);
@ -616,6 +617,7 @@ function* executePageLoadAction(pageAction: PageAction) {
PerformanceTransactionName.EXECUTE_PAGE_LOAD_ACTIONS,
);
const pageId = yield select(getCurrentPageId);
const appId = yield select(getCurrentApplicationId);
yield put(executeApiActionRequest({ id: pageAction.id }));
const params: Property[] = yield call(
getActionParams,
@ -629,6 +631,7 @@ function* executePageLoadAction(pageAction: PageAction) {
type: pageAction.pluginType,
name: pageAction.name,
pageId: pageId,
appId: appId,
onPageLoad: true,
});
const response: ActionApiResponse = yield ActionAPI.executeAction(

View File

@ -29,6 +29,8 @@ import {
selectPlugin,
createDatasource,
changeDatasource,
testDatasource,
setDatsourceEditorMode,
expandDatasourceEntity,
fetchDatasourceStructure,
} from "actions/datasourceActions";
@ -149,13 +151,9 @@ export function* deleteDatasourceSaga(
});
}
} catch (error) {
AppToaster.show({
message: error.message,
type: ToastType.ERROR,
});
yield put({
type: ReduxActionErrorTypes.DELETE_DATASOURCE_ERROR,
payload: { error, id: actionPayload.payload.id },
payload: { error, id: actionPayload.payload.id, show: false },
});
}
}
@ -193,6 +191,9 @@ function* updateDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
id: response.data.id,
},
});
yield put(
setDatsourceEditorMode({ id: datasourcePayload.id, viewMode: true }),
);
if (expandDatasourceId === response.data.id && !datasourceStruture) {
yield put(fetchDatasourceStructure(response.data.id));
@ -270,6 +271,10 @@ function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
message: responseData.invalids[0],
type: ToastType.ERROR,
});
yield put({
type: ReduxActionErrorTypes.TEST_DATASOURCE_ERROR,
payload: { show: false },
});
} else {
AnalyticsUtil.logEvent("TEST_DATA_SOURCE_SUCCESS", {
datasource: payload.name,
@ -278,11 +283,11 @@ function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
message: `${payload.name} is valid`,
type: ToastType.SUCCESS,
});
yield put({
type: ReduxActionTypes.TEST_DATASOURCE_SUCCESS,
payload: datasource,
});
}
yield put({
type: ReduxActionTypes.TEST_DATASOURCE_SUCCESS,
payload: datasource,
});
}
} catch (error) {
yield put({
@ -370,6 +375,9 @@ function* createDatasourceFromFormSaga(
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
payload: response.data,
});
yield put(
setDatsourceEditorMode({ id: response.data.id, viewMode: false }),
);
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);

View File

@ -203,6 +203,10 @@ export const getActionsForCurrentPage = createSelector(
},
);
export const getPlugin = (state: AppState, pluginId: string) => {
return state.entities.plugins.list.find(plugin => plugin.id === pluginId);
};
export const getActionResponses = createSelector(getActions, actions => {
const responses: Record<string, ActionResponse | undefined> = {};

View File

@ -82,6 +82,7 @@ export type EventName =
| "WIDGET_DELETE_VIA_SHORTCUT"
| "OPEN_HELP"
| "INVITE_USER"
| "ROUTE_CHANGE"
| "PROPERTY_PANE_CLOSE_CLICK"
| "APPLICATIONS_PAGE_LOAD"
| "EXECUTE_ACTION";