+
+ )}
+ {errorComponent}
+
{!isNil(currentFormConfig) && !isNil(datasource) ? (
{
componentDidUpdate(prevProps: Props) {
if (prevProps.datasourceId !== this.props.datasourceId) {
diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
index 20590217d7..1e22f05f43 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx
@@ -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;
}
`;
diff --git a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx
index e3748635dd..2f84b1096f 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/JSONtoForm.tsx
@@ -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 (
+
+
+
+ );
+};
+
+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 (
-
+
- {content}
-
+
+ {formContent}
+
+
);
};
@@ -214,6 +237,8 @@ export class JSONtoForm<
{this.renderEachConfig(section)}
diff --git a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx
index af6b46c69d..3384368cb6 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx
@@ -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 (
);
}
diff --git a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx
index a180ddd1a8..81f63fe245 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx
@@ -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;
-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) => {
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 (
- <>
+
{/* this is true during import flow */}
- {!this.props.hiddenHeader && }
-
+ {!hiddenHeader && }
+
-
- >
+
+
);
};
@@ -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 (
-
- {!hiddenHeader && (
- {
- 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}
- />
- )}
- this.save()}
- size="medium"
- tag="button"
- text="Save"
- variant={Variant.success}
- />
-
- );
- };
-
renderEditor = () => {
const {
datasource,
@@ -506,6 +411,46 @@ class DatasourceRestAPIEditor extends React.Component<
messages.map((msg, i) => (
))}
+ {this.renderGeneralSettings()}
+ {this.renderAuthFields()}
+ {this.renderOauth2AdvancedSettings()}
+ {this.renderSelfSignedCertificateFields()}
+ {formData.authType &&
+ formData.authType === AuthType.OAuth2 &&
+ _.get(authentication, "grantType") ===
+ GrantType.AuthorizationCode && (
+
+
+ this.save(
+ redirectAuthorizationCode(
+ pageId,
+ datasourceId,
+ PluginType.API,
+ ),
+ )
+ }
+ tag="button"
+ text={
+ isAuthorized ? "Save and Re-Authorize" : "Save and Authorize"
+ }
+ variant={Variant.success}
+ />
+
+ )}
+ >
+ );
+ };
+
+ renderGeneralSettings = () => {
+ const { formData } = this.props;
+
+ return (
+
{this.renderInputTextControlViaFormControl(
"url",
@@ -598,39 +543,7 @@ class DatasourceRestAPIEditor extends React.Component<
"",
)}
- {this.renderAuthFields()}
-
- {this.renderOauth2AdvancedSettings()}
-
- {this.renderSelfSignedCertificateFields()}
- {formData.authType &&
- formData.authType === AuthType.OAuth2 &&
- _.get(authentication, "grantType") ===
- GrantType.AuthorizationCode && (
-
-
- this.save(
- redirectAuthorizationCode(
- pageId,
- datasourceId,
- PluginType.API,
- ),
- )
- }
- tag="button"
- text={
- isAuthorized ? "Save and Re-Authorize" : "Save and Authorize"
- }
- variant={Variant.success}
- />
-
- )}
- >
+
);
};
@@ -933,7 +846,7 @@ class DatasourceRestAPIEditor extends React.Component<
_.get(connection, "ssl.authType") === SSLType.SELF_SIGNED_CERTIFICATE;
return (
- <>
+
{isAuthenticationTypeOAuth2 && isGrantTypeAuthorizationCode && (
)}
- >
+
);
};
diff --git a/app/client/src/pages/Editor/IntegrationEditor/DatasourceCard.tsx b/app/client/src/pages/Editor/IntegrationEditor/DatasourceCard.tsx
index 3929f0ea15..349cfede21 100644
--- a/app/client/src/pages/Editor/IntegrationEditor/DatasourceCard.tsx
+++ b/app/client/src/pages/Editor/IntegrationEditor/DatasourceCard.tsx
@@ -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) {
- {(!datasource.isConfigured || supportTemplateGeneration) && (
-
- )}
+ {(!datasource.isConfigured || supportTemplateGeneration) &&
+ isDatasourceAuthorizedForQueryCreation(datasource, plugin) && (
+
+ )}
{datasource.isConfigured && (
diff --git a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx
index 4024fea459..6c2d619b0e 100644
--- a/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx
+++ b/app/client/src/pages/Editor/SaaSEditor/DatasourceForm.tsx
@@ -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;
-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 {
renderDataSourceConfigForm = (sections: any) => {
const {
+ canCreateDatasourceActions,
canManageDatasource,
datasource,
datasourceButtonConfiguration,
@@ -251,12 +257,23 @@ class DatasourceSaaSEditor extends JSONtoForm {
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 {
{viewMode && (
- {
- this.props.setDatasourceViewMode(false);
- this.props.history.replace(
- saasEditorDatasourceIdURL({
- pageId: pageId || "",
- pluginPackageName,
- datasourceId,
- params: {
- viewMode: false,
- },
- }),
- );
- }}
- text="EDIT"
- />
+
+ {
+ this.props.setDatasourceViewMode(false);
+ this.props.history.replace(
+ saasEditorDatasourceIdURL({
+ pageId: pageId || "",
+ pluginPackageName,
+ datasourceId,
+ params: {
+ viewMode: false,
+ },
+ }),
+ );
+ }}
+ text="EDIT"
+ />
+ {isGoogleSheetPlugin && (
+
+ )}
+
)}
)}
{(!viewMode || datasourceId === TEMP_DATASOURCE_ID) && (
<>
+ {datasource && isGoogleSheetPlugin && !isPluginAuthorized ? (
+
+ ) : null}
{!_.isNil(sections)
? _.map(sections, this.renderMainSection)
: null}
{""}
>
)}
- {viewMode && }
+ {viewMode && (
+
+ ) : null
+ }
+ showDatasourceSavedText={!isGoogleSheetPlugin}
+ />
+ )}
{/* Render datasource form call-to-actions */}
{datasource && (
{
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,
};
};
diff --git a/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx b/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx
index 53fcf78f36..45cd2caa3c 100644
--- a/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx
+++ b/app/client/src/pages/Editor/gitSync/ReconnectDatasourceModal.tsx
@@ -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;
diff --git a/app/client/src/pages/common/datasourceAuth/AuthMessage.tsx b/app/client/src/pages/common/datasourceAuth/AuthMessage.tsx
new file mode 100644
index 0000000000..1a407f95b9
--- /dev/null
+++ b/app/client/src/pages/common/datasourceAuth/AuthMessage.tsx
@@ -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> = {};
+
+ if (actionType === "authorize") {
+ extraInfo.actionLabel = "Authorize Datasource";
+ extraInfo.onClick = handleOauthAuthorization;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/app/client/src/pages/common/datasourceAuth/index.tsx b/app/client/src/pages/common/datasourceAuth/index.tsx
index a60d733643..b073a875d4 100644
--- a/app/client/src/pages/common/datasourceAuth/index.tsx
+++ b/app/client/src/pages/common/datasourceAuth/index.tsx
@@ -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 && (
- Datasource not authorized
- )}
+ {authType === AuthType.OAUTH2 &&
+ !isAuthorized &&
+ shouldDisplayAuthMessage && (
+ Datasource not authorized
+ )}
{shouldRender && (
{datasourceButtonConfiguration?.map((btnConfig) =>
@@ -349,7 +363,6 @@ function DatasourceAuth({
)}
)}
- {""}
>
);
}
diff --git a/app/client/src/utils/editorContextUtils.ts b/app/client/src/utils/editorContextUtils.ts
index 153c9c7cff..4506c5c57f 100644
--- a/app/client/src/utils/editorContextUtils.ts
+++ b/app/client/src/utils/editorContextUtils.ts
@@ -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;
+}