fix: google sheet ui issues, removed redundant code (#18785)
Co-authored-by: “sneha122” <“sneha@appsmith.com”>
This commit is contained in:
parent
d452b2452c
commit
4a381607eb
|
|
@ -1,5 +1,6 @@
|
|||
const homePage = require("../../../locators/HomePage");
|
||||
const reconnectDatasourceModal = require("../../../locators/ReconnectLocators");
|
||||
const datasource = require("../../../locators/DatasourcesEditor.json");
|
||||
|
||||
describe("Reconnect Datasource Modal validation while importing application", function() {
|
||||
let workspaceId;
|
||||
|
|
@ -39,12 +40,14 @@ describe("Reconnect Datasource Modal validation while importing application", fu
|
|||
cy.get(".t--ds-list").contains("PostgreSQL");
|
||||
// check the postgres form config with default value
|
||||
cy.get("[data-cy='section-Connection']").should("be.visible");
|
||||
cy.get("[data-cy='section-Authentication']").should("be.visible");
|
||||
cy.get("[data-cy='section-SSL (optional)']").should("be.visible");
|
||||
cy.get(datasource.authenticationSettingsSection).should(
|
||||
"be.visible",
|
||||
);
|
||||
cy.get(datasource.sslSettingsSection).should("be.visible");
|
||||
cy.get(
|
||||
"[data-cy='datasourceConfiguration.connection.mode']",
|
||||
).should("contain", "Read / Write");
|
||||
cy.get("[data-cy='section-SSL (optional)']").click({ force: true });
|
||||
cy.get(datasource.sslSettingsSection).click({ force: true });
|
||||
// should expand ssl pan
|
||||
cy.get(
|
||||
"[data-cy='datasourceConfiguration.connection.ssl.authType']",
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
"MongoDB": ".t--plugin-name:contains('MongoDB')",
|
||||
"RESTAPI": ".t--plugin-name:contains('REST API')",
|
||||
"PostgreSQL": ".t--plugin-name:contains('PostgreSQL')",
|
||||
"SMTP":".t--plugin-name:contains('SMTP')",
|
||||
"SMTP": ".t--plugin-name:contains('SMTP')",
|
||||
"MySQL": ".t--plugin-name:contains('MySQL')",
|
||||
"GoogleSheets": ".t--plugin-name:contains('Google Sheets')",
|
||||
"sectionAuthentication": "[data-cy=section-Authentication]",
|
||||
"sectionAuthentication": "[data-cy=section-Authentication] .t--collapse-section-container",
|
||||
"PostgresEntity": ".t--entity-name:contains(PostgreSQL)",
|
||||
"MySQLEntity": ".t--entity-name:contains(Mysql)",
|
||||
"createQuery": ".t--create-query",
|
||||
|
|
@ -24,15 +24,15 @@
|
|||
"datasourceCardMenu": ".t--datasource-menu-option",
|
||||
"datasourceCardGeneratePageBtn": ".t--generate-template",
|
||||
"datasourceMenuOptionEdit": "t--datasource-option-edit",
|
||||
"datasourceMenuOptionDelete":"t--datasource-option-delete",
|
||||
"datasourceMenuOptionDelete": "t--datasource-option-delete",
|
||||
"editDatasource": ".t--edit-datasource",
|
||||
"datasourceTitle": ".t--edit-datasource-name .bp3-editable-text-content",
|
||||
"datasourceTitleLocator": ".t--edit-datasource-name",
|
||||
"defaultDatabaseName": "input[name='datasourceConfiguration.connection.defaultDatabaseName']",
|
||||
"datasourceConfigurationProperty":"input[name='datasourceConfiguration.properties[0]']",
|
||||
"googleSheets":".t--plugin-name:contains('Google Sheets')",
|
||||
"datasourceConfigurationProperty": "input[name='datasourceConfiguration.properties[0]']",
|
||||
"googleSheets": ".t--plugin-name:contains('Google Sheets')",
|
||||
"selConnectionType": "[data-cy='datasourceConfiguration.connection.type']",
|
||||
"scope":"[data-cy='authentication.scopeString']",
|
||||
"scope": "[data-cy='authentication.scopeString']",
|
||||
"Mysql": ".t--plugin-name:contains('Mysql')",
|
||||
"ElasticSearch": ".t--plugin-name:contains('Elasticsearch')",
|
||||
"DynamoDB": ".t--plugin-name:contains('DynamoDB')",
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
"projectID": "[data-cy='datasourceConfiguration.authentication.username'] input",
|
||||
"serviceAccCredential": "[data-cy='datasourceConfiguration.authentication.password'] input",
|
||||
"grantType": "[data-cy='authentication.grantType']",
|
||||
"authorizationURL":"[data-cy='authentication.authorizationUrl'] input",
|
||||
"authorizationURL": "[data-cy='authentication.authorizationUrl'] input",
|
||||
"authorizationCode": ".t--dropdown-option:contains('Authorization Code')",
|
||||
"clientCredentials": ".t--dropdown-option:contains('Client Credentials')",
|
||||
"clientAuthentication": "[data-cy='authentication.isAuthorizationHeader']",
|
||||
|
|
@ -64,20 +64,22 @@
|
|||
"basic": "//div[contains(@class,'option') and text()='Basic']",
|
||||
"basicUsername": "input[name='authentication.username']",
|
||||
"basicPassword": "input[name='authentication.password']",
|
||||
"mockUserDatabase":"div[id='mock-database'] span:contains('Users')",
|
||||
"mockUserDatasources":".t--datasource-name:contains('Users')",
|
||||
"mockUserDatabase": "div[id='mock-database'] span:contains('Users')",
|
||||
"mockUserDatasources": ".t--datasource-name:contains('Users')",
|
||||
"mongoUriDropdown": "//p[text()='Use Mongo Connection String URI']/following-sibling::div",
|
||||
"mongoUriYes": "//div[text()='Yes']",
|
||||
"mongoUriInput":"//p[text()='Connection String URI']/following-sibling::div//input",
|
||||
"advancedSettings": "[data-cy='section-Advanced Settings']",
|
||||
"mongoUriInput": "//p[text()='Connection String URI']/following-sibling::div//input",
|
||||
"advancedSettings": "[data-cy='section-Advanced Settings'] .t--collapse-section-container",
|
||||
"useSelfSignedCert": ".t--connection\\.ssl\\.authType",
|
||||
"useCertInAuth": "[data-cy='authentication.useSelfSignedCert'] input",
|
||||
"certificateDetails": "[data-cy='section-Certificate Details']",
|
||||
"certificateDetails": "[data-cy='section-Certificate Details'] .t--collapse-section-container",
|
||||
"saveBtn": ".t--save-datasource",
|
||||
"gSheetsOperationDropdown": "[data-cy='actionConfiguration.formData.command.data']",
|
||||
"gSheetsEntityDropdown": "[data-cy='actionConfiguration.formData.entityType.data']",
|
||||
"gSheetsInsertOneOption": ".t--dropdown-option:contains('Insert One')",
|
||||
"gSheetsSheetRowsOption": ".t--dropdown-option:contains('Sheet Row(s)')",
|
||||
"gSheetsCodeMirrorPlaceholder": ".CodeMirror-placeholder"
|
||||
|
||||
}
|
||||
"gSheetsCodeMirrorPlaceholder": ".CodeMirror-placeholder",
|
||||
"connectionSettingsSection": "[data-cy='section-Connection'] .t--collapse-section-container",
|
||||
"authenticationSettingsSection": "[data-cy='section-Authentication'] .t--collapse-section-container",
|
||||
"sslSettingsSection": "[data-cy='section-SSL (optional)'] .t--collapse-section-container"
|
||||
}
|
||||
|
|
@ -24,7 +24,8 @@ export class DataSources {
|
|||
"input[name='datasourceConfiguration.authentication.databaseName']";
|
||||
private _username =
|
||||
"input[name='datasourceConfiguration.authentication.username']";
|
||||
private _sectionAuthentication = "[data-cy=section-Authentication]";
|
||||
private _sectionAuthentication =
|
||||
"[data-cy=section-Authentication] .t--collapse-section-container";
|
||||
private _password =
|
||||
"input[name = 'datasourceConfiguration.authentication.password']";
|
||||
private _testDs = ".t--test-datasource";
|
||||
|
|
|
|||
|
|
@ -315,6 +315,9 @@ export const OAUTH_AUTHORIZATION_FAILED =
|
|||
export const OAUTH_AUTHORIZATION_APPSMITH_ERROR = "Something went wrong.";
|
||||
export const OAUTH_APPSMITH_TOKEN_NOT_FOUND = "Appsmith token not found";
|
||||
|
||||
export const GSHEET_AUTHORIZATION_ERROR =
|
||||
"Data source is not authorized, please authorize to continue.";
|
||||
|
||||
export const LOCAL_STORAGE_QUOTA_EXCEEDED_MESSAGE = () =>
|
||||
"Error saving a key in localStorage. You have exceeded the allowed storage size limit";
|
||||
export const LOCAL_STORAGE_NO_SPACE_LEFT_ON_DEVICE_MESSAGE = () =>
|
||||
|
|
@ -1400,6 +1403,11 @@ export const PAGE_SETTINGS_SET_AS_HOMEPAGE_TOOLTIP_NON_HOME_PAGE = () =>
|
|||
export const PAGE_SETTINGS_ACTION_NAME_CONFLICT_ERROR = (name: string) =>
|
||||
`${name} is already being used.`;
|
||||
|
||||
export const NEW_QUERY_BUTTON_TEXT = () => "New Query";
|
||||
export const NEW_API_BUTTON_TEXT = () => "New API";
|
||||
export const GENERATE_NEW_PAGE_BUTTON_TEXT = () => "GENERATE NEW PAGE";
|
||||
export const RECONNECT_BUTTON_TEXT = () => "RECONNECT";
|
||||
|
||||
// Alert options and labels for showMessage types
|
||||
export const ALERT_STYLE_OPTIONS = [
|
||||
{ label: "Info", value: "'info'", id: "info" },
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const IconContainer = styled.div`
|
|||
//width: 100%;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding-left: 16px;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from "react";
|
|||
import { Collapse, Icon } from "@blueprintjs/core";
|
||||
import styled from "styled-components";
|
||||
import { Icon as AdsIcon, IconName, IconSize } from "design-system";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import { getDatasourceCollapsibleState } from "selectors/ui";
|
||||
|
|
@ -31,8 +32,8 @@ const SectionContainer = styled.div`
|
|||
`;
|
||||
|
||||
const TopBorder = styled.div`
|
||||
height: 2px;
|
||||
background-color: #d0d7dd;
|
||||
height: 1px;
|
||||
background-color: ${Colors.ALTO};
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
`;
|
||||
|
|
@ -46,12 +47,21 @@ interface ComponentProps {
|
|||
name: IconName;
|
||||
color?: string;
|
||||
};
|
||||
showTopBorder?: boolean;
|
||||
showSection?: boolean;
|
||||
}
|
||||
|
||||
type Props = ComponentProps;
|
||||
|
||||
function Collapsible(props: Props) {
|
||||
const { children, defaultIsOpen, headerIcon, title } = props;
|
||||
const {
|
||||
children,
|
||||
defaultIsOpen,
|
||||
headerIcon,
|
||||
showSection = true,
|
||||
showTopBorder = true,
|
||||
title,
|
||||
} = props;
|
||||
const dispatch = useDispatch();
|
||||
const isOpen = useSelector((state: AppState) =>
|
||||
getDatasourceCollapsibleState(state, title),
|
||||
|
|
@ -69,35 +79,35 @@ function Collapsible(props: Props) {
|
|||
}, [defaultIsOpen, isOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBorder className="t--collapse-top-border" />
|
||||
<SectionContainer
|
||||
className="t--collapse-section-container"
|
||||
data-cy={`section-${title}`}
|
||||
data-replay-id={`section-${title}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<SectionLabel>
|
||||
{title}
|
||||
{headerIcon && (
|
||||
<AdsIcon
|
||||
fillColor={headerIcon.color}
|
||||
name={headerIcon.name}
|
||||
size={IconSize.MEDIUM}
|
||||
/>
|
||||
)}
|
||||
</SectionLabel>
|
||||
<Icon
|
||||
icon={isOpen ? "chevron-up" : "chevron-down"}
|
||||
iconSize={16}
|
||||
style={{ color: "#2E3D49" }}
|
||||
/>
|
||||
</SectionContainer>
|
||||
<section data-cy={`section-${title}`} data-replay-id={`section-${title}`}>
|
||||
{showTopBorder && <TopBorder className="t--collapse-top-border" />}
|
||||
{showSection && (
|
||||
<SectionContainer
|
||||
className="t--collapse-section-container"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<SectionLabel>
|
||||
{title}
|
||||
{headerIcon && (
|
||||
<AdsIcon
|
||||
fillColor={headerIcon.color}
|
||||
name={headerIcon.name}
|
||||
size={IconSize.MEDIUM}
|
||||
/>
|
||||
)}
|
||||
</SectionLabel>
|
||||
<Icon
|
||||
icon={isOpen ? "chevron-up" : "chevron-down"}
|
||||
iconSize={16}
|
||||
style={{ color: "#2E3D49" }}
|
||||
/>
|
||||
</SectionContainer>
|
||||
)}
|
||||
|
||||
<Collapse isOpen={isOpen} keepChildrenMounted>
|
||||
{children}
|
||||
</Collapse>
|
||||
</>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,14 +31,18 @@ const Header = styled.div`
|
|||
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;
|
||||
`;
|
||||
|
||||
function Connected() {
|
||||
function Connected({
|
||||
errorComponent,
|
||||
showDatasourceSavedText = true,
|
||||
}: {
|
||||
errorComponent?: JSX.Element | null;
|
||||
showDatasourceSavedText?: boolean;
|
||||
}) {
|
||||
const params = useParams<{ datasourceId: string }>();
|
||||
|
||||
const datasource = useSelector((state: AppState) =>
|
||||
|
|
@ -67,25 +71,26 @@ function Connected() {
|
|||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Header>
|
||||
<ConnectedText>
|
||||
<HeaderIcons.SAVE_SUCCESS
|
||||
color={Colors.GREEN}
|
||||
height={30}
|
||||
width={30}
|
||||
{showDatasourceSavedText && (
|
||||
<Header>
|
||||
<ConnectedText>
|
||||
<HeaderIcons.SAVE_SUCCESS
|
||||
color={Colors.GREEN}
|
||||
height={30}
|
||||
width={30}
|
||||
/>
|
||||
<div style={{ marginLeft: "12px" }}>Datasource Saved</div>
|
||||
</ConnectedText>
|
||||
<NewActionButton
|
||||
datasource={datasource}
|
||||
disabled={!canCreateDatasourceActions}
|
||||
eventFrom="datasource-pane"
|
||||
plugin={plugin}
|
||||
/>
|
||||
|
||||
<div style={{ marginLeft: "12px" }}>Datasource Saved</div>
|
||||
</ConnectedText>
|
||||
|
||||
<NewActionButton
|
||||
datasource={datasource}
|
||||
disabled={!canCreateDatasourceActions}
|
||||
eventFrom="datasource-pane"
|
||||
plugin={plugin}
|
||||
/>
|
||||
</Header>
|
||||
<div style={{ marginTop: "30px" }}>
|
||||
</Header>
|
||||
)}
|
||||
{errorComponent}
|
||||
<div style={{ marginTop: showDatasourceSavedText ? "30px" : "" }}>
|
||||
{!isNil(currentFormConfig) && !isNil(datasource) ? (
|
||||
<RenderDatasourceInformation
|
||||
config={currentFormConfig[0]}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import _ from "lodash";
|
|||
import { DATASOURCE_DB_FORM } from "@appsmith/constants/forms";
|
||||
import { Icon } from "@blueprintjs/core";
|
||||
import FormTitle from "./FormTitle";
|
||||
import { Button, Callout, Category, Variant } from "design-system";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { Callout, Category, Variant } from "design-system";
|
||||
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
|
||||
import Connected from "./Connected";
|
||||
import { Datasource } from "entities/Datasource";
|
||||
|
|
@ -18,6 +17,7 @@ import { convertArrayToSentence } from "utils/helpers";
|
|||
import { PluginType } from "entities/Action";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import {
|
||||
EditDatasourceButton,
|
||||
FormTitleContainer,
|
||||
Header,
|
||||
JSONtoForm,
|
||||
|
|
@ -66,16 +66,6 @@ const CollapsibleWrapper = styled.div`
|
|||
width: max-content;
|
||||
`;
|
||||
|
||||
const EditDatasourceButton = styled(Button)`
|
||||
padding: 10px 20px;
|
||||
&&&& {
|
||||
height: 32px;
|
||||
max-width: 160px;
|
||||
border: 1px solid ${Colors.HIT_GRAY};
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
class DatasourceDBEditor extends JSONtoForm<Props> {
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (prevProps.datasourceId !== this.props.datasourceId) {
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ const Value = styled.div`
|
|||
|
||||
const ValueWrapper = styled.div`
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
&:not(:first-child) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FieldWrapper = styled.div`
|
||||
&:not(first-child) {
|
||||
&:not(:first-child) {
|
||||
margin-top: 9px;
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -7,18 +7,48 @@ import { ControlProps } from "components/formControls/BaseControl";
|
|||
import { Datasource } from "entities/Datasource";
|
||||
import { isHidden, isKVArray } from "components/formControls/utils";
|
||||
import log from "loglevel";
|
||||
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import CloseEditor from "components/editorComponents/CloseEditor";
|
||||
import { getType, Types } from "utils/TypeHelpers";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { Button } from "design-system";
|
||||
|
||||
export const LoadingContainer = styled(CenteredWrapper)`
|
||||
height: 50%;
|
||||
export const PluginImageWrapper = styled.div`
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: ${Colors.GREY_200};
|
||||
border-radius: 100%;
|
||||
img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export const PluginImage = styled.img`
|
||||
height: 40px;
|
||||
width: auto;
|
||||
export const PluginImage = (props: any) => {
|
||||
return (
|
||||
<PluginImageWrapper>
|
||||
<img {...props} />
|
||||
</PluginImageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const FormContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const FormContainerBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
export const FormTitleContainer = styled.div`
|
||||
|
|
@ -32,13 +62,13 @@ export const Header = styled.div`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid ${Colors.ALTO};
|
||||
padding-bottom: 24px;
|
||||
//margin-top: 16px;
|
||||
`;
|
||||
|
||||
export const SaveButtonContainer = styled.div`
|
||||
margin-top: 24px;
|
||||
export const ActionWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export const ActionButton = styled(Button)`
|
||||
|
|
@ -54,19 +84,13 @@ export const ActionButton = styled(Button)`
|
|||
}
|
||||
`;
|
||||
|
||||
const DBForm = styled.div`
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
margin-right: 0px;
|
||||
overflow: auto;
|
||||
.backBtn {
|
||||
padding-bottom: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.backBtnText {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
export const EditDatasourceButton = styled(Button)`
|
||||
padding: 10px 20px;
|
||||
&&&& {
|
||||
height: 36px;
|
||||
max-width: 160px;
|
||||
border: 1px solid ${Colors.HIT_GRAY};
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -196,15 +220,14 @@ export class JSONtoForm<
|
|||
return formData;
|
||||
};
|
||||
|
||||
renderForm = (content: any) => {
|
||||
renderForm = (formContent: any) => {
|
||||
return (
|
||||
<div
|
||||
className="t--json-to-form-wrapper"
|
||||
style={{ height: "100%", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<FormContainer className="t--json-to-form-wrapper">
|
||||
<CloseEditor />
|
||||
<DBForm>{content}</DBForm>
|
||||
</div>
|
||||
<FormContainerBody className="t--json-to-form-body">
|
||||
{formContent}
|
||||
</FormContainerBody>
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -214,6 +237,8 @@ export class JSONtoForm<
|
|||
<Collapsible
|
||||
defaultIsOpen={index === 0}
|
||||
key={section.sectionName}
|
||||
showSection={index !== 0}
|
||||
showTopBorder={index !== 0}
|
||||
title={section.sectionName}
|
||||
>
|
||||
{this.renderEachConfig(section)}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ import {
|
|||
Toaster,
|
||||
Variant,
|
||||
} from "design-system";
|
||||
import { ERROR_ADD_API_INVALID_URL } from "@appsmith/constants/messages";
|
||||
import {
|
||||
createMessage,
|
||||
ERROR_ADD_API_INVALID_URL,
|
||||
NEW_API_BUTTON_TEXT,
|
||||
NEW_QUERY_BUTTON_TEXT,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { createNewQueryAction } from "actions/apiPaneActions";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
|
|
@ -16,6 +21,7 @@ import { getCurrentPageId } from "selectors/editorSelectors";
|
|||
import { Datasource } from "entities/Datasource";
|
||||
import { Plugin } from "api/PluginApi";
|
||||
import { EventLocation } from "utils/AnalyticsUtil";
|
||||
import { noop } from "utils/AppsmithUtils";
|
||||
|
||||
const ActionButton = styled(Button)`
|
||||
padding: 10px 10px;
|
||||
|
|
@ -43,9 +49,10 @@ type NewActionButtonProps = {
|
|||
isLoading?: boolean;
|
||||
eventFrom?: string; // this is to track from where the new action is being generated
|
||||
plugin?: Plugin;
|
||||
style?: any;
|
||||
};
|
||||
function NewActionButton(props: NewActionButtonProps) {
|
||||
const { datasource, disabled, plugin } = props;
|
||||
const { datasource, disabled, plugin, style = {} } = props;
|
||||
const pluginType = plugin?.type;
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
|
||||
|
|
@ -88,13 +95,18 @@ function NewActionButton(props: NewActionButtonProps) {
|
|||
return (
|
||||
<ActionButton
|
||||
className="t--create-query"
|
||||
disabled={disabled}
|
||||
disabled={!!disabled}
|
||||
icon="plus"
|
||||
iconPosition={IconPositions.left}
|
||||
isLoading={isSelected || props.isLoading}
|
||||
onClick={createQueryAction}
|
||||
onClick={disabled ? noop : createQueryAction}
|
||||
style={style}
|
||||
tag="button"
|
||||
text={pluginType === PluginType.DB ? "New Query" : "New API"}
|
||||
text={
|
||||
pluginType === PluginType.DB || pluginType === PluginType.SAAS
|
||||
? createMessage(NEW_QUERY_BUTTON_TEXT)
|
||||
: createMessage(NEW_API_BUTTON_TEXT)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import FormControl from "pages/Editor/FormControl";
|
||||
import { StyledInfo } from "components/formControls/InputTextControl";
|
||||
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import { ApiActionConfig, PluginType } from "entities/Action";
|
||||
|
|
@ -40,12 +39,7 @@ import {
|
|||
GrantType,
|
||||
SSLType,
|
||||
} from "entities/Datasource/RestAPIForm";
|
||||
import {
|
||||
createMessage,
|
||||
CONTEXT_DELETE,
|
||||
CONFIRM_CONTEXT_DELETE,
|
||||
INVALID_URL,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { createMessage, INVALID_URL } from "@appsmith/constants/messages";
|
||||
import Collapsible from "./Collapsible";
|
||||
import _ from "lodash";
|
||||
import FormLabel from "components/editorComponents/FormLabel";
|
||||
|
|
@ -54,11 +48,18 @@ import { Callout } from "design-system";
|
|||
import CloseEditor from "components/editorComponents/CloseEditor";
|
||||
import { updateReplayEntity } from "actions/pageActions";
|
||||
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
||||
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
|
||||
import {
|
||||
hasDeleteDatasourcePermission,
|
||||
hasManageDatasourcePermission,
|
||||
} from "@appsmith/utils/permissionHelpers";
|
||||
FormContainer,
|
||||
FormContainerBody,
|
||||
FormTitleContainer,
|
||||
Header,
|
||||
PluginImage,
|
||||
} from "./JSONtoForm";
|
||||
import DatasourceAuth, {
|
||||
DatasourceButtonType,
|
||||
} from "pages/common/datasourceAuth";
|
||||
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
|
||||
import { hasManageDatasourcePermission } from "@appsmith/utils/permissionHelpers";
|
||||
|
||||
interface DatasourceRestApiEditorProps {
|
||||
initializeReplayEntity: (id: string, data: any) => void;
|
||||
|
|
@ -99,68 +100,10 @@ interface DatasourceRestApiEditorProps {
|
|||
type Props = DatasourceRestApiEditorProps &
|
||||
InjectedFormProps<ApiDatasourceForm, DatasourceRestApiEditorProps>;
|
||||
|
||||
const RestApiForm = styled.div`
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
margin-left: 10px;
|
||||
margin-right: 0px;
|
||||
overflow: auto;
|
||||
.backBtn {
|
||||
padding-bottom: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.backBtnText {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const FormInputContainer = styled.div`
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
export const LoadingContainer = styled(CenteredWrapper)`
|
||||
height: 50%;
|
||||
`;
|
||||
|
||||
const PluginImage = styled.img`
|
||||
height: 40px;
|
||||
width: auto;
|
||||
`;
|
||||
|
||||
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;
|
||||
`;
|
||||
|
||||
const SaveButtonContainer = styled.div`
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const ActionButton = styled(Button)`
|
||||
&&& {
|
||||
width: auto;
|
||||
min-width: 74px;
|
||||
margin-right: 9px;
|
||||
min-height: 32px;
|
||||
|
||||
& > span {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
&&&& {
|
||||
width: 87px;
|
||||
|
|
@ -289,26 +232,23 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
}
|
||||
};
|
||||
|
||||
disableSave = (): boolean => {
|
||||
validate = (): boolean => {
|
||||
const { datasource, datasourceId, formData } = this.props;
|
||||
const createMode = datasourceId === TEMP_DATASOURCE_ID;
|
||||
const canManageDatasource = hasManageDatasourcePermission(
|
||||
datasource?.userPermissions || [],
|
||||
);
|
||||
if (!formData) return true;
|
||||
return (
|
||||
!formData.url ||
|
||||
!this.props.isFormDirty ||
|
||||
(!createMode && !canManageDatasource)
|
||||
);
|
||||
return !formData.url || (!createMode && !canManageDatasource);
|
||||
};
|
||||
|
||||
getSanitizedFormData = () =>
|
||||
formValuesToDatasource(this.props.datasource, this.props.formData);
|
||||
|
||||
save = (onSuccess?: ReduxAction<unknown>) => {
|
||||
this.props.toggleSaveActionFlag(true);
|
||||
const normalizedValues = formValuesToDatasource(
|
||||
this.props.datasource,
|
||||
this.props.formData,
|
||||
);
|
||||
const normalizedValues = this.getSanitizedFormData();
|
||||
|
||||
AnalyticsUtil.logEvent("SAVE_DATA_SOURCE_CLICK", {
|
||||
pageId: this.props.pageId,
|
||||
appId: this.props.applicationId,
|
||||
|
|
@ -387,17 +327,14 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
return { isValid: true, message: "" };
|
||||
};
|
||||
|
||||
handleDeleteDatasource = (datasourceId: string) => {
|
||||
this.props.deleteDatasource(datasourceId);
|
||||
this.props.datasourceDeleteTrigger();
|
||||
};
|
||||
|
||||
render = () => {
|
||||
const { datasource, formData, hiddenHeader, pageId } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormContainer>
|
||||
{/* this is true during import flow */}
|
||||
{!this.props.hiddenHeader && <CloseEditor />}
|
||||
<RestApiForm>
|
||||
{!hiddenHeader && <CloseEditor />}
|
||||
<FormContainerBody>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -405,10 +342,23 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
>
|
||||
{this.renderHeader()}
|
||||
{this.renderEditor()}
|
||||
{this.renderSave()}
|
||||
<DatasourceAuth
|
||||
datasource={datasource}
|
||||
datasourceButtonConfiguration={[
|
||||
DatasourceButtonType.DELETE,
|
||||
DatasourceButtonType.SAVE,
|
||||
]}
|
||||
datasourceDeleteTrigger={this.props.datasourceDeleteTrigger}
|
||||
formData={formData}
|
||||
getSanitizedFormData={this.getSanitizedFormData}
|
||||
isFormDirty={this.props.isFormDirty}
|
||||
isInvalid={this.validate()}
|
||||
pageId={pageId}
|
||||
shouldRender
|
||||
/>
|
||||
</form>
|
||||
</RestApiForm>
|
||||
</>
|
||||
</FormContainerBody>
|
||||
</FormContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -437,51 +387,6 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
) : null;
|
||||
};
|
||||
|
||||
renderSave = () => {
|
||||
const { datasourceId, hiddenHeader, isDeleting, isSaving } = this.props;
|
||||
const createMode = datasourceId === TEMP_DATASOURCE_ID;
|
||||
const canDeleteDatasource = hasDeleteDatasourcePermission(
|
||||
this.props.datasource?.userPermissions || [],
|
||||
);
|
||||
|
||||
return (
|
||||
<SaveButtonContainer>
|
||||
{!hiddenHeader && (
|
||||
<ActionButton
|
||||
category={Category.primary}
|
||||
className="t--delete-datasource"
|
||||
disabled={createMode || !canDeleteDatasource}
|
||||
isLoading={isDeleting}
|
||||
onClick={() => {
|
||||
this.state.confirmDelete
|
||||
? this.handleDeleteDatasource(datasourceId)
|
||||
: this.setState({ confirmDelete: true });
|
||||
}}
|
||||
size="medium"
|
||||
tag="button"
|
||||
text={
|
||||
this.state.confirmDelete
|
||||
? createMessage(CONFIRM_CONTEXT_DELETE)
|
||||
: createMessage(CONTEXT_DELETE)
|
||||
}
|
||||
variant={Variant.danger}
|
||||
/>
|
||||
)}
|
||||
<StyledButton
|
||||
category={Category.primary}
|
||||
className="t--save-datasource"
|
||||
disabled={this.disableSave()}
|
||||
isLoading={isSaving}
|
||||
onClick={() => this.save()}
|
||||
size="medium"
|
||||
tag="button"
|
||||
text="Save"
|
||||
variant={Variant.success}
|
||||
/>
|
||||
</SaveButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
renderEditor = () => {
|
||||
const {
|
||||
datasource,
|
||||
|
|
@ -506,6 +411,46 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
messages.map((msg, i) => (
|
||||
<Callout fill key={i} text={msg} variant={Variant.warning} />
|
||||
))}
|
||||
{this.renderGeneralSettings()}
|
||||
{this.renderAuthFields()}
|
||||
{this.renderOauth2AdvancedSettings()}
|
||||
{this.renderSelfSignedCertificateFields()}
|
||||
{formData.authType &&
|
||||
formData.authType === AuthType.OAuth2 &&
|
||||
_.get(authentication, "grantType") ===
|
||||
GrantType.AuthorizationCode && (
|
||||
<FormInputContainer>
|
||||
<AuthorizeButton
|
||||
category={Category.primary}
|
||||
className="t--save-and-authorize-datasource"
|
||||
disabled={this.validate() || !this.props.isFormDirty}
|
||||
isLoading={isSaving}
|
||||
onClick={() =>
|
||||
this.save(
|
||||
redirectAuthorizationCode(
|
||||
pageId,
|
||||
datasourceId,
|
||||
PluginType.API,
|
||||
),
|
||||
)
|
||||
}
|
||||
tag="button"
|
||||
text={
|
||||
isAuthorized ? "Save and Re-Authorize" : "Save and Authorize"
|
||||
}
|
||||
variant={Variant.success}
|
||||
/>
|
||||
</FormInputContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderGeneralSettings = () => {
|
||||
const { formData } = this.props;
|
||||
|
||||
return (
|
||||
<section data-cy="section-General" data-replay-id="section-General">
|
||||
<FormInputContainer data-replay-id={btoa("url")}>
|
||||
{this.renderInputTextControlViaFormControl(
|
||||
"url",
|
||||
|
|
@ -598,39 +543,7 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
"",
|
||||
)}
|
||||
</FormInputContainer>
|
||||
{this.renderAuthFields()}
|
||||
<Collapsible title="Advanced Settings">
|
||||
{this.renderOauth2AdvancedSettings()}
|
||||
</Collapsible>
|
||||
{this.renderSelfSignedCertificateFields()}
|
||||
{formData.authType &&
|
||||
formData.authType === AuthType.OAuth2 &&
|
||||
_.get(authentication, "grantType") ===
|
||||
GrantType.AuthorizationCode && (
|
||||
<FormInputContainer>
|
||||
<AuthorizeButton
|
||||
category={Category.primary}
|
||||
className="t--save-and-authorize-datasource"
|
||||
disabled={this.disableSave()}
|
||||
isLoading={isSaving}
|
||||
onClick={() =>
|
||||
this.save(
|
||||
redirectAuthorizationCode(
|
||||
pageId,
|
||||
datasourceId,
|
||||
PluginType.API,
|
||||
),
|
||||
)
|
||||
}
|
||||
tag="button"
|
||||
text={
|
||||
isAuthorized ? "Save and Re-Authorize" : "Save and Authorize"
|
||||
}
|
||||
variant={Variant.success}
|
||||
/>
|
||||
</FormInputContainer>
|
||||
)}
|
||||
</>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -933,7 +846,7 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
_.get(connection, "ssl.authType") === SSLType.SELF_SIGNED_CERTIFICATE;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Collapsible title="Advanced Settings">
|
||||
{isAuthenticationTypeOAuth2 && isGrantTypeAuthorizationCode && (
|
||||
<FormInputContainer
|
||||
data-replay-id={btoa("authentication.sendScopeWithRefreshToken")}
|
||||
|
|
@ -1012,7 +925,7 @@ class DatasourceRestAPIEditor extends React.Component<
|
|||
)}
|
||||
</FormInputContainer>
|
||||
)}
|
||||
</>
|
||||
</Collapsible>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ import {
|
|||
CONFIRM_CONTEXT_DELETE,
|
||||
createMessage,
|
||||
CONFIRM_CONTEXT_DELETING,
|
||||
GENERATE_NEW_PAGE_BUTTON_TEXT,
|
||||
RECONNECT_BUTTON_TEXT,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { isDatasourceAuthorizedForQueryCreation } from "utils/editorContextUtils";
|
||||
import {
|
||||
getCurrentPageId,
|
||||
getPagePermissions,
|
||||
|
|
@ -319,26 +322,34 @@ function DatasourceCard(props: DatasourceCardProps) {
|
|||
</Queries>
|
||||
</div>
|
||||
<ButtonsWrapper className="action-wrapper">
|
||||
{(!datasource.isConfigured || supportTemplateGeneration) && (
|
||||
<GenerateTemplateOrReconnect
|
||||
category={Category.secondary}
|
||||
className={
|
||||
datasource.isConfigured
|
||||
? "t--generate-template"
|
||||
: "t--reconnect-btn"
|
||||
}
|
||||
onClick={
|
||||
datasource.isConfigured ? routeToGeneratePage : editDatasource
|
||||
}
|
||||
text={
|
||||
datasource.isConfigured ? "GENERATE NEW PAGE" : "RECONNECT"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(!datasource.isConfigured || supportTemplateGeneration) &&
|
||||
isDatasourceAuthorizedForQueryCreation(datasource, plugin) && (
|
||||
<GenerateTemplateOrReconnect
|
||||
category={Category.secondary}
|
||||
className={
|
||||
datasource.isConfigured
|
||||
? "t--generate-template"
|
||||
: "t--reconnect-btn"
|
||||
}
|
||||
onClick={
|
||||
datasource.isConfigured
|
||||
? routeToGeneratePage
|
||||
: editDatasource
|
||||
}
|
||||
text={
|
||||
datasource.isConfigured
|
||||
? createMessage(GENERATE_NEW_PAGE_BUTTON_TEXT)
|
||||
: createMessage(RECONNECT_BUTTON_TEXT)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{datasource.isConfigured && (
|
||||
<NewActionButton
|
||||
datasource={datasource}
|
||||
disabled={!canCreateDatasourceActions}
|
||||
disabled={
|
||||
!canCreateDatasourceActions ||
|
||||
!isDatasourceAuthorizedForQueryCreation(datasource, plugin)
|
||||
}
|
||||
eventFrom="active-datasources"
|
||||
plugin={plugin}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import _, { merge } from "lodash";
|
||||
import { DATASOURCE_SAAS_FORM } from "@appsmith/constants/forms";
|
||||
import FormTitle from "pages/Editor/DataSourceEditor/FormTitle";
|
||||
import { Button as AdsButton, Category } from "design-system";
|
||||
import { Category } from "design-system";
|
||||
import { Datasource } from "entities/Datasource";
|
||||
import {
|
||||
getFormValues,
|
||||
|
|
@ -18,9 +17,12 @@ import {
|
|||
getDatasource,
|
||||
getPluginImages,
|
||||
getDatasourceFormButtonConfig,
|
||||
getPlugin,
|
||||
} from "selectors/entitiesSelector";
|
||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||
import {
|
||||
ActionWrapper,
|
||||
EditDatasourceButton,
|
||||
FormTitleContainer,
|
||||
Header,
|
||||
JSONtoForm,
|
||||
|
|
@ -29,14 +31,24 @@ import {
|
|||
} from "../DataSourceEditor/JSONtoForm";
|
||||
import { getConfigInitialValues } from "components/formControls/utils";
|
||||
import Connected from "../DataSourceEditor/Connected";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
import { getCurrentApplicationId } from "selectors/editorSelectors";
|
||||
import DatasourceAuth from "../../common/datasourceAuth";
|
||||
import {
|
||||
getCurrentApplicationId,
|
||||
getPagePermissions,
|
||||
} from "selectors/editorSelectors";
|
||||
import DatasourceAuth from "pages/common/datasourceAuth";
|
||||
import EntityNotFoundPane from "../EntityNotFoundPane";
|
||||
import { saasEditorDatasourceIdURL } from "RouteBuilder";
|
||||
import NewActionButton from "../DataSourceEditor/NewActionButton";
|
||||
import { Plugin } from "api/PluginApi";
|
||||
import { isDatasourceAuthorizedForQueryCreation } from "utils/editorContextUtils";
|
||||
import { PluginPackageName } from "entities/Action";
|
||||
import AuthMessage from "pages/common/datasourceAuth/AuthMessage";
|
||||
import { isDatasourceInViewMode } from "selectors/ui";
|
||||
import { hasManageDatasourcePermission } from "@appsmith/utils/permissionHelpers";
|
||||
import {
|
||||
hasCreateDatasourceActionPermission,
|
||||
hasManageDatasourcePermission,
|
||||
} from "@appsmith/utils/permissionHelpers";
|
||||
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
|
||||
import {
|
||||
createTempDatasourceFromForm,
|
||||
|
|
@ -47,15 +59,18 @@ import {
|
|||
toggleSaveActionFromPopupFlag,
|
||||
} from "actions/datasourceActions";
|
||||
import SaveOrDiscardDatasourceModal from "../DataSourceEditor/SaveOrDiscardDatasourceModal";
|
||||
import { GSHEET_AUTHORIZATION_ERROR } from "ce/constants/messages";
|
||||
|
||||
interface StateProps extends JSONtoFormProps {
|
||||
applicationId: string;
|
||||
canManageDatasource?: boolean;
|
||||
canCreateDatasourceActions?: boolean;
|
||||
isSaving: boolean;
|
||||
isDeleting: boolean;
|
||||
loadingFormConfigs: boolean;
|
||||
isNewDatasource: boolean;
|
||||
pluginImage: string;
|
||||
plugin?: Plugin;
|
||||
pluginId: string;
|
||||
actions: ActionDataState;
|
||||
datasource?: Datasource;
|
||||
|
|
@ -89,16 +104,6 @@ type DatasourceSaaSEditorProps = StateProps &
|
|||
type Props = DatasourceSaaSEditorProps &
|
||||
InjectedFormProps<Datasource, DatasourceSaaSEditorProps>;
|
||||
|
||||
const EditDatasourceButton = styled(AdsButton)`
|
||||
padding: 10px 20px;
|
||||
&&&& {
|
||||
height: 32px;
|
||||
max-width: 160px;
|
||||
border: 1px solid ${Colors.HIT_GRAY};
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
/*
|
||||
**** State Variables Description ****
|
||||
showDialog: flag used to show/hide the datasource discard popup
|
||||
|
|
@ -244,6 +249,7 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
|
||||
renderDataSourceConfigForm = (sections: any) => {
|
||||
const {
|
||||
canCreateDatasourceActions,
|
||||
canManageDatasource,
|
||||
datasource,
|
||||
datasourceButtonConfiguration,
|
||||
|
|
@ -251,12 +257,23 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
formData,
|
||||
hiddenHeader,
|
||||
pageId,
|
||||
plugin,
|
||||
pluginPackageName,
|
||||
} = this.props;
|
||||
const params: string = location.search;
|
||||
const viewMode =
|
||||
!hiddenHeader && new URLSearchParams(params).get("viewMode");
|
||||
|
||||
/*
|
||||
TODO: This flag will be removed once the multiple environment is merged to avoid design inconsistency between different datasources.
|
||||
Search for: GoogleSheetPluginFlag to check for all the google sheet conditional logic throughout the code.
|
||||
*/
|
||||
const isGoogleSheetPlugin =
|
||||
plugin?.packageName === PluginPackageName.GOOGLE_SHEETS;
|
||||
|
||||
const isPluginAuthorized =
|
||||
plugin && isDatasourceAuthorizedForQueryCreation(formData, plugin);
|
||||
|
||||
const createFlow = datasourceId === TEMP_DATASOURCE_ID;
|
||||
|
||||
return (
|
||||
|
|
@ -277,36 +294,75 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
</FormTitleContainer>
|
||||
|
||||
{viewMode && (
|
||||
<EditDatasourceButton
|
||||
category={Category.secondary}
|
||||
className="t--edit-datasource"
|
||||
onClick={() => {
|
||||
this.props.setDatasourceViewMode(false);
|
||||
this.props.history.replace(
|
||||
saasEditorDatasourceIdURL({
|
||||
pageId: pageId || "",
|
||||
pluginPackageName,
|
||||
datasourceId,
|
||||
params: {
|
||||
viewMode: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}}
|
||||
text="EDIT"
|
||||
/>
|
||||
<ActionWrapper>
|
||||
<EditDatasourceButton
|
||||
category={Category.secondary}
|
||||
className="t--edit-datasource"
|
||||
onClick={() => {
|
||||
this.props.setDatasourceViewMode(false);
|
||||
this.props.history.replace(
|
||||
saasEditorDatasourceIdURL({
|
||||
pageId: pageId || "",
|
||||
pluginPackageName,
|
||||
datasourceId,
|
||||
params: {
|
||||
viewMode: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}}
|
||||
text="EDIT"
|
||||
/>
|
||||
{isGoogleSheetPlugin && (
|
||||
<NewActionButton
|
||||
datasource={datasource}
|
||||
disabled={
|
||||
!canCreateDatasourceActions || !isPluginAuthorized
|
||||
}
|
||||
eventFrom="datasource-pane"
|
||||
plugin={plugin}
|
||||
style={{
|
||||
marginLeft: "16px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ActionWrapper>
|
||||
)}
|
||||
</Header>
|
||||
)}
|
||||
{(!viewMode || datasourceId === TEMP_DATASOURCE_ID) && (
|
||||
<>
|
||||
{datasource && isGoogleSheetPlugin && !isPluginAuthorized ? (
|
||||
<AuthMessage
|
||||
datasource={datasource}
|
||||
description={GSHEET_AUTHORIZATION_ERROR}
|
||||
pageId={pageId}
|
||||
style={{
|
||||
paddingTop: "24px",
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{!_.isNil(sections)
|
||||
? _.map(sections, this.renderMainSection)
|
||||
: null}
|
||||
{""}
|
||||
</>
|
||||
)}
|
||||
{viewMode && <Connected />}
|
||||
{viewMode && (
|
||||
<Connected
|
||||
errorComponent={
|
||||
datasource && isGoogleSheetPlugin && !isPluginAuthorized ? (
|
||||
<AuthMessage
|
||||
actionType="authorize"
|
||||
datasource={datasource}
|
||||
description={GSHEET_AUTHORIZATION_ERROR}
|
||||
pageId={pageId}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
showDatasourceSavedText={!isGoogleSheetPlugin}
|
||||
/>
|
||||
)}
|
||||
{/* Render datasource form call-to-actions */}
|
||||
{datasource && (
|
||||
<DatasourceAuth
|
||||
|
|
@ -317,6 +373,7 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
getSanitizedFormData={_.memoize(this.getSanitizedData)}
|
||||
isInvalid={this.validate()}
|
||||
pageId={pageId}
|
||||
shouldDisplayAuthMessage={!isGoogleSheetPlugin}
|
||||
shouldRender={!viewMode}
|
||||
triggerSave={this.props.isDatasourceBeingSavedFromPopup}
|
||||
/>
|
||||
|
|
@ -344,6 +401,7 @@ const mapStateToProps = (state: AppState, props: any) => {
|
|||
const { formConfigs } = plugins;
|
||||
const formData = getFormValues(DATASOURCE_SAAS_FORM)(state) as Datasource;
|
||||
const pluginId = _.get(datasource, "pluginId", "");
|
||||
const plugin = getPlugin(state, pluginId);
|
||||
const formConfig = formConfigs[pluginId];
|
||||
const initialValues = {};
|
||||
if (formConfig) {
|
||||
|
|
@ -360,12 +418,18 @@ const mapStateToProps = (state: AppState, props: any) => {
|
|||
? true
|
||||
: isDirty(DATASOURCE_SAAS_FORM)(state);
|
||||
|
||||
const datsourcePermissions = datasource?.userPermissions || [];
|
||||
const datasourcePermissions = datasource?.userPermissions || [];
|
||||
|
||||
const canManageDatasource = hasManageDatasourcePermission(
|
||||
datsourcePermissions,
|
||||
datasourcePermissions,
|
||||
);
|
||||
|
||||
const pagePermissions = getPagePermissions(state);
|
||||
const canCreateDatasourceActions = hasCreateDatasourceActionPermission([
|
||||
...datasourcePermissions,
|
||||
...pagePermissions,
|
||||
]);
|
||||
|
||||
return {
|
||||
datasource,
|
||||
datasourceButtonConfiguration,
|
||||
|
|
@ -377,6 +441,7 @@ const mapStateToProps = (state: AppState, props: any) => {
|
|||
viewMode: viewMode ?? !props.fromImporting,
|
||||
isNewDatasource: datasourcePane.newDatasource === TEMP_DATASOURCE_ID,
|
||||
pageId: props.pageId || props.match?.params?.pageId,
|
||||
plugin: plugin,
|
||||
pluginImage: getPluginImages(state)[pluginId],
|
||||
pluginPackageName:
|
||||
props.pluginPackageName || props.match?.params?.pluginPackageName,
|
||||
|
|
@ -391,6 +456,7 @@ const mapStateToProps = (state: AppState, props: any) => {
|
|||
isDatasourceBeingSavedFromPopup:
|
||||
state.entities.datasources.isDatasourceBeingSavedFromPopup,
|
||||
isFormDirty,
|
||||
canCreateDatasourceActions,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,13 @@ const ContentWrapper = styled.div`
|
|||
.t--json-to-form-wrapper {
|
||||
width: 100%;
|
||||
|
||||
.t--json-to-form-body {
|
||||
padding: 0 20px;
|
||||
.t--collapse-section-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.t--close-editor {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -226,7 +233,6 @@ const TooltipWrapper = styled.div`
|
|||
`;
|
||||
|
||||
const DBFormWrapper = styled.div`
|
||||
padding: 10px;
|
||||
width: calc(100% - 206px);
|
||||
overflow: auto;
|
||||
|
||||
|
|
|
|||
50
app/client/src/pages/common/datasourceAuth/AuthMessage.tsx
Normal file
50
app/client/src/pages/common/datasourceAuth/AuthMessage.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { AppState } from "@appsmith/reducers";
|
||||
import { redirectAuthorizationCode } from "actions/datasourceActions";
|
||||
import { CalloutV2 } from "design-system";
|
||||
import { Datasource } from "entities/Datasource";
|
||||
import React from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getPluginTypeFromDatasourceId } from "selectors/entitiesSelector";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledAuthMessage = styled.div`
|
||||
max-width: 560px;
|
||||
margin-bottom: 16px;
|
||||
& > div {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
type AuthMessageProps = {
|
||||
// We can handle for other action types as well eg. save, delete etc.
|
||||
actionType?: "authorize";
|
||||
datasource: Datasource;
|
||||
description: string;
|
||||
pageId?: string;
|
||||
style?: any;
|
||||
};
|
||||
|
||||
export default function AuthMessage(props: AuthMessageProps) {
|
||||
const { actionType, datasource, description, pageId, style = {} } = props;
|
||||
const dispatch = useDispatch();
|
||||
const pluginType = useSelector((state: AppState) =>
|
||||
getPluginTypeFromDatasourceId(state, datasource.id),
|
||||
);
|
||||
const handleOauthAuthorization: any = () => {
|
||||
if (!pluginType || !pageId) return;
|
||||
dispatch(redirectAuthorizationCode(pageId, datasource.id, pluginType));
|
||||
};
|
||||
|
||||
const extraInfo: Partial<React.ComponentProps<typeof CalloutV2>> = {};
|
||||
|
||||
if (actionType === "authorize") {
|
||||
extraInfo.actionLabel = "Authorize Datasource";
|
||||
extraInfo.onClick = handleOauthAuthorization;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledAuthMessage style={style}>
|
||||
<CalloutV2 desc={description} type="Warning" {...extraInfo} />
|
||||
</StyledAuthMessage>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import {
|
||||
ActionButton,
|
||||
SaveButtonContainer,
|
||||
} from "pages/Editor/DataSourceEditor/JSONtoForm";
|
||||
import { ActionButton } from "pages/Editor/DataSourceEditor/JSONtoForm";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
getEntities,
|
||||
|
|
@ -30,6 +27,7 @@ import {
|
|||
AuthenticationStatus,
|
||||
} from "entities/Datasource";
|
||||
import {
|
||||
CONFIRM_CONTEXT_DELETING,
|
||||
OAUTH_AUTHORIZATION_APPSMITH_ERROR,
|
||||
OAUTH_AUTHORIZATION_FAILED,
|
||||
} from "@appsmith/constants/messages";
|
||||
|
|
@ -40,6 +38,7 @@ import {
|
|||
createMessage,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { debounce } from "lodash";
|
||||
import { ApiDatasourceForm } from "entities/Datasource/RestAPIForm";
|
||||
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
|
||||
|
||||
import {
|
||||
|
|
@ -49,12 +48,13 @@ import {
|
|||
|
||||
interface Props {
|
||||
datasource: Datasource;
|
||||
formData: Datasource;
|
||||
formData: Datasource | ApiDatasourceForm;
|
||||
getSanitizedFormData: () => Datasource;
|
||||
isInvalid: boolean;
|
||||
pageId?: string;
|
||||
shouldRender: boolean;
|
||||
shouldRender?: boolean;
|
||||
datasourceButtonConfiguration: string[] | undefined;
|
||||
shouldDisplayAuthMessage?: boolean;
|
||||
triggerSave?: boolean;
|
||||
isFormDirty?: boolean;
|
||||
datasourceDeleteTrigger: () => void;
|
||||
|
|
@ -91,6 +91,12 @@ const StyledButton = styled(ActionButton)<{ fluidWidth?: boolean }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const SaveButtonContainer = styled.div`
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const StyledAuthMessage = styled.div`
|
||||
color: ${(props) => props.theme.colors.error};
|
||||
margin-top: 15px;
|
||||
|
|
@ -109,12 +115,14 @@ function DatasourceAuth({
|
|||
isInvalid,
|
||||
pageId: pageIdProp,
|
||||
shouldRender,
|
||||
shouldDisplayAuthMessage = true,
|
||||
triggerSave,
|
||||
isFormDirty,
|
||||
}: Props) {
|
||||
const authType =
|
||||
formData &&
|
||||
formData?.datasourceConfiguration?.authentication?.authenticationType;
|
||||
formData && "authType" in formData
|
||||
? formData?.authType
|
||||
: formData?.datasourceConfiguration?.authentication?.authenticationType;
|
||||
|
||||
const { id: datasourceId, isDeleting } = datasource;
|
||||
const applicationId = useSelector(getCurrentApplicationId);
|
||||
|
|
@ -276,12 +284,16 @@ function DatasourceAuth({
|
|||
isLoading={isDeleting}
|
||||
key={buttonType}
|
||||
onClick={() => {
|
||||
confirmDelete ? handleDatasourceDelete() : setConfirmDelete(true);
|
||||
if (!isDeleting) {
|
||||
confirmDelete ? handleDatasourceDelete() : setConfirmDelete(true);
|
||||
}
|
||||
}}
|
||||
size="medium"
|
||||
tag="button"
|
||||
text={
|
||||
confirmDelete && !isDeleting
|
||||
isDeleting
|
||||
? createMessage(CONFIRM_CONTEXT_DELETING)
|
||||
: confirmDelete
|
||||
? createMessage(CONFIRM_CONTEXT_DELETE)
|
||||
: createMessage(CONTEXT_DELETE)
|
||||
}
|
||||
|
|
@ -339,9 +351,11 @@ function DatasourceAuth({
|
|||
|
||||
return (
|
||||
<>
|
||||
{authType === AuthType.OAUTH2 && !isAuthorized && (
|
||||
<StyledAuthMessage>Datasource not authorized</StyledAuthMessage>
|
||||
)}
|
||||
{authType === AuthType.OAUTH2 &&
|
||||
!isAuthorized &&
|
||||
shouldDisplayAuthMessage && (
|
||||
<StyledAuthMessage>Datasource not authorized</StyledAuthMessage>
|
||||
)}
|
||||
{shouldRender && (
|
||||
<SaveButtonContainer>
|
||||
{datasourceButtonConfiguration?.map((btnConfig) =>
|
||||
|
|
@ -349,7 +363,6 @@ function DatasourceAuth({
|
|||
)}
|
||||
</SaveButtonContainer>
|
||||
)}
|
||||
{""}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
import { Plugin } from "api/PluginApi";
|
||||
import { PluginPackageName } from "entities/Action";
|
||||
import {
|
||||
AuthenticationStatus,
|
||||
AuthType,
|
||||
Datasource,
|
||||
} from "entities/Datasource";
|
||||
export function isCurrentFocusOnInput() {
|
||||
return (
|
||||
["input", "textarea"].indexOf(
|
||||
|
|
@ -57,3 +64,36 @@ export function getPropertyControlFocusElement(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if :
|
||||
* - authentication type is not oauth2 or is not a Google Sheet Plugin
|
||||
* - authentication type is oauth2 and authorized status success and is a Google Sheet Plugin
|
||||
* @param datasource Datasource
|
||||
* @param plugin Plugin
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isDatasourceAuthorizedForQueryCreation(
|
||||
datasource: Datasource,
|
||||
plugin: Plugin,
|
||||
): boolean {
|
||||
if (!datasource) return false;
|
||||
const authType =
|
||||
datasource &&
|
||||
datasource?.datasourceConfiguration?.authentication?.authenticationType;
|
||||
|
||||
/*
|
||||
TODO: This flag will be removed once the multiple environment is merged to avoid design inconsistency between different datasources.
|
||||
Search for: GoogleSheetPluginFlag to check for all the google sheet conditional logic throughout the code.
|
||||
*/
|
||||
const isGoogleSheetPlugin =
|
||||
plugin.packageName === PluginPackageName.GOOGLE_SHEETS;
|
||||
if (isGoogleSheetPlugin && authType === AuthType.OAUTH2) {
|
||||
const isAuthorized =
|
||||
datasource?.datasourceConfiguration?.authentication
|
||||
?.authenticationStatus === AuthenticationStatus.SUCCESS;
|
||||
return isAuthorized;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user