Merge branch 'master' into release
This commit is contained in:
commit
c550e9202f
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -224,7 +224,6 @@ describe("Table Widget Functionality", function() {
|
|||
cy.get(publish.canvas)
|
||||
.first()
|
||||
.click();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ describe("Entity explorer datasource structure", function() {
|
|||
"response.body.responseMeta.status",
|
||||
200,
|
||||
);
|
||||
|
||||
cy.deletePostgresDatasource(datasourceName);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
254
app/client/src/pages/Editor/DataSourceEditor/Connected.tsx
Normal file
254
app/client/src/pages/Editor/DataSourceEditor/Connected.tsx
Normal 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;
|
||||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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> = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user