feat: Feat/add google sheets metdata (#22577)
This PR does three things: 1. Cleans up the Datasource Form page and makes the code more readable and discoverable 2. Adds the Account information for google sheets 3. Adds a way to render form configs specifically in view or edit modes.
This commit is contained in:
parent
82d931e173
commit
f87ba6a671
|
|
@ -1571,8 +1571,7 @@ export const RECONNECT_BUTTON_TEXT = () => "RECONNECT";
|
|||
export const SAVE_BUTTON_TEXT = () => "SAVE";
|
||||
export const SAVE_AND_AUTHORIZE_BUTTON_TEXT = () => "SAVE AND AUTHORIZE";
|
||||
export const DISCARD_POPUP_DONT_SAVE_BUTTON_TEXT = () => "DON'T SAVE";
|
||||
export const GSHEET_AUTHORISED_FILE_IDS_KEY = () =>
|
||||
"Google sheets authorised file ids key";
|
||||
export const GSHEET_AUTHORISED_FILE_IDS_KEY = () => "userAuthorizedSheetIds";
|
||||
export const GOOGLE_SHEETS_INFO_BANNER_MESSAGE = () =>
|
||||
"Appsmith will require access to your google drive to access google sheets.";
|
||||
export const GOOGLE_SHEETS_AUTHORIZE_DATASOURCE = () => "Authorize Datasource";
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ export type ComparisonOperations =
|
|||
| "GREATER"
|
||||
| "IN"
|
||||
| "NOT_IN"
|
||||
| "FEATURE_FLAG";
|
||||
| "FEATURE_FLAG"
|
||||
| "VIEW_MODE";
|
||||
|
||||
export enum ComparisonOperationsEnum {
|
||||
VIEW_MODE = "VIEW_MODE",
|
||||
}
|
||||
|
||||
export type HiddenType = boolean | Condition | ConditionObject;
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export const caculateIsHidden = (
|
|||
values: any,
|
||||
hiddenConfig?: HiddenType,
|
||||
featureFlags?: FeatureFlags,
|
||||
viewMode?: boolean,
|
||||
) => {
|
||||
if (!!hiddenConfig && !isBoolean(hiddenConfig)) {
|
||||
let valueAtPath;
|
||||
|
|
@ -102,6 +103,9 @@ export const caculateIsHidden = (
|
|||
// and show new configs if feature flag is enabled, if disabled/ not present,
|
||||
// previous config would be shown as is
|
||||
return !!featureFlags && featureFlags[flagValue] === value;
|
||||
case "VIEW_MODE":
|
||||
// This can be used to decide which form controls to show in view mode or edit mode depending on the value.
|
||||
return viewMode === value;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
|
@ -112,13 +116,14 @@ export const isHidden = (
|
|||
values: any,
|
||||
hiddenConfig?: HiddenType,
|
||||
featureFlags?: FeatureFlags,
|
||||
viewMode?: boolean,
|
||||
) => {
|
||||
if (!!hiddenConfig && !isBoolean(hiddenConfig)) {
|
||||
if ("conditionType" in hiddenConfig) {
|
||||
//check if nested conditions exist
|
||||
return isHiddenConditionsEvaluation(values, hiddenConfig);
|
||||
} else {
|
||||
return caculateIsHidden(values, hiddenConfig, featureFlags);
|
||||
return caculateIsHidden(values, hiddenConfig, featureFlags, viewMode);
|
||||
}
|
||||
}
|
||||
return !!hiddenConfig;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@ import React from "react";
|
|||
import { useSelector } from "react-redux";
|
||||
import { useParams } from "react-router";
|
||||
import type { AppState } from "@appsmith/reducers";
|
||||
import { isNil } from "lodash";
|
||||
import { getDatasource, getPlugin } from "selectors/entitiesSelector";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { HeaderIcons } from "icons/HeaderIcons";
|
||||
import styled from "styled-components";
|
||||
import RenderDatasourceInformation from "./DatasourceSection";
|
||||
import NewActionButton from "./NewActionButton";
|
||||
|
||||
import { hasCreateDatasourceActionPermission } from "@appsmith/utils/permissionHelpers";
|
||||
|
|
@ -28,16 +26,8 @@ const Header = styled.div`
|
|||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid #d0d7dd;
|
||||
padding: 24px 20px;
|
||||
`;
|
||||
|
||||
function Connected({
|
||||
errorComponent,
|
||||
hideDatasourceRenderSection = false,
|
||||
showDatasourceSavedText = true,
|
||||
}: {
|
||||
errorComponent?: JSX.Element | null;
|
||||
|
|
@ -50,10 +40,6 @@ function Connected({
|
|||
getDatasource(state, params.datasourceId),
|
||||
);
|
||||
|
||||
const datasourceFormConfigs = useSelector(
|
||||
(state: AppState) => state.entities.plugins.formConfigs,
|
||||
);
|
||||
|
||||
const plugin = useSelector((state: AppState) =>
|
||||
getPlugin(state, datasource?.pluginId ?? ""),
|
||||
);
|
||||
|
|
@ -67,11 +53,8 @@ function Connected({
|
|||
...pagePermissions,
|
||||
]);
|
||||
|
||||
const currentFormConfig: Array<any> =
|
||||
datasourceFormConfigs[datasource?.pluginId ?? ""];
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<>
|
||||
{showDatasourceSavedText && (
|
||||
<Header>
|
||||
<ConnectedText>
|
||||
|
|
@ -91,17 +74,7 @@ function Connected({
|
|||
</Header>
|
||||
)}
|
||||
{errorComponent}
|
||||
<div style={{ marginTop: showDatasourceSavedText ? "30px" : "" }}>
|
||||
{!isNil(currentFormConfig) &&
|
||||
!isNil(datasource) &&
|
||||
!hideDatasourceRenderSection ? (
|
||||
<RenderDatasourceInformation
|
||||
config={currentFormConfig[0]}
|
||||
datasource={datasource}
|
||||
/>
|
||||
) : undefined}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import Debugger, {
|
|||
} from "./Debugger";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import { showDebuggerFlag } from "selectors/debuggerSelectors";
|
||||
import DatasourceInformation from "./DatasourceSection";
|
||||
import { DocsLink, openDoc } from "../../../constants/DocumentationLinks";
|
||||
|
||||
const { cloudHosting } = getAppsmithConfigs();
|
||||
|
|
@ -86,6 +87,13 @@ export const Form = styled.form`
|
|||
flex: 1;
|
||||
`;
|
||||
|
||||
const ViewModeWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid #d0d7dd;
|
||||
padding: 24px 20px;
|
||||
`;
|
||||
|
||||
class DatasourceDBEditor extends JSONtoForm<Props> {
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (prevProps.datasourceId !== this.props.datasourceId) {
|
||||
|
|
@ -125,6 +133,7 @@ class DatasourceDBEditor extends JSONtoForm<Props> {
|
|||
datasourceButtonConfiguration,
|
||||
datasourceDeleteTrigger,
|
||||
datasourceId,
|
||||
formConfig,
|
||||
formData,
|
||||
messages,
|
||||
pluginType,
|
||||
|
|
@ -200,7 +209,20 @@ class DatasourceDBEditor extends JSONtoForm<Props> {
|
|||
{""}
|
||||
</>
|
||||
)}
|
||||
{viewMode && <Connected />}
|
||||
{viewMode && (
|
||||
<ViewModeWrapper>
|
||||
<Connected />
|
||||
<div style={{ marginTop: "30px" }}>
|
||||
{!_.isNil(formConfig) && !_.isNil(datasource) ? (
|
||||
<DatasourceInformation
|
||||
config={formConfig[0]}
|
||||
datasource={datasource}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
) : undefined}
|
||||
</div>
|
||||
</ViewModeWrapper>
|
||||
)}
|
||||
{/* Render datasource form call-to-actions */}
|
||||
{datasource && (
|
||||
<DatasourceAuth
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Colors } from "constants/Colors";
|
|||
import styled from "styled-components";
|
||||
import { isHidden, isKVArray } from "components/formControls/utils";
|
||||
import log from "loglevel";
|
||||
import { ComparisonOperationsEnum } from "components/formControls/BaseControl";
|
||||
|
||||
const Key = styled.div`
|
||||
color: ${Colors.DOVE_GRAY};
|
||||
|
|
@ -35,6 +36,7 @@ const FieldWrapper = styled.div`
|
|||
export default class RenderDatasourceInformation extends React.Component<{
|
||||
config: any;
|
||||
datasource: Datasource;
|
||||
viewMode?: boolean;
|
||||
}> {
|
||||
renderKVArray = (children: Array<any>) => {
|
||||
try {
|
||||
|
|
@ -85,11 +87,12 @@ export default class RenderDatasourceInformation extends React.Component<{
|
|||
};
|
||||
|
||||
renderDatasourceSection(section: any) {
|
||||
const { datasource } = this.props;
|
||||
const { datasource, viewMode } = this.props;
|
||||
return (
|
||||
<React.Fragment key={datasource.id}>
|
||||
{map(section.children, (section) => {
|
||||
if (isHidden(datasource, section.hidden)) return null;
|
||||
if (isHidden(datasource, section.hidden, undefined, viewMode))
|
||||
return null;
|
||||
if ("children" in section) {
|
||||
if (isKVArray(section.children)) {
|
||||
return this.renderKVArray(section.children);
|
||||
|
|
@ -123,6 +126,15 @@ export default class RenderDatasourceInformation extends React.Component<{
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!value &&
|
||||
!!viewMode &&
|
||||
"comparison" in section.hidden &&
|
||||
section.hidden.comparison === ComparisonOperationsEnum.VIEW_MODE
|
||||
) {
|
||||
value = section.initialValue;
|
||||
}
|
||||
|
||||
if (!value || (isArray(value) && value.length < 1)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,14 @@ export class JSONtoForm<
|
|||
// hides features/configs that are hidden behind feature flag
|
||||
// TODO: remove hidden config property as well as this param,
|
||||
// when feature flag is removed
|
||||
if (isHidden(this.props.formData, section.hidden, this.props?.featureFlags))
|
||||
if (
|
||||
isHidden(
|
||||
this.props.formData,
|
||||
section.hidden,
|
||||
this.props?.featureFlags,
|
||||
false, // viewMode is false here.
|
||||
)
|
||||
)
|
||||
return null;
|
||||
return (
|
||||
<Collapsible
|
||||
|
|
@ -314,8 +321,9 @@ export class JSONtoForm<
|
|||
if (
|
||||
isHidden(
|
||||
this.props.formData,
|
||||
section.hidden,
|
||||
propertyControlOrSection.hidden,
|
||||
this.props?.featureFlags,
|
||||
false,
|
||||
)
|
||||
)
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -738,7 +738,8 @@ export function EditorJSONtoForm(props: Props) {
|
|||
(section: any): any => {
|
||||
return section.children.map(
|
||||
(formControlOrSection: ControlProps, idx: number) => {
|
||||
if (isHidden(props.formData, section.hidden)) return null;
|
||||
if (isHidden(props.formData, section.hidden, undefined, false))
|
||||
return null;
|
||||
if (formControlOrSection.hasOwnProperty("children")) {
|
||||
return renderEachConfig(formName)(formControlOrSection);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import {
|
|||
} from "../DataSourceEditor/JSONtoForm";
|
||||
import { getConfigInitialValues } from "components/formControls/utils";
|
||||
import Connected from "../DataSourceEditor/Connected";
|
||||
|
||||
import {
|
||||
getCurrentApplicationId,
|
||||
getGsheetProjectID,
|
||||
|
|
@ -71,6 +70,8 @@ import { getDatasourceErrorMessage } from "./errorUtils";
|
|||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import { DocumentationLink } from "../QueryEditor/EditorJSONtoForm";
|
||||
import GoogleSheetFilePicker from "./GoogleSheetFilePicker";
|
||||
import DatasourceInformation from "./../DataSourceEditor/DatasourceSection";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface StateProps extends JSONtoFormProps {
|
||||
applicationId: string;
|
||||
|
|
@ -133,6 +134,13 @@ type State = {
|
|||
navigation(): void;
|
||||
};
|
||||
|
||||
const ViewModeWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid #d0d7dd;
|
||||
padding: 24px 20px;
|
||||
`;
|
||||
|
||||
class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
|
@ -272,6 +280,7 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
datasourceId,
|
||||
documentationLink,
|
||||
featureFlags,
|
||||
formConfig,
|
||||
formData,
|
||||
gsheetProjectID,
|
||||
gsheetToken,
|
||||
|
|
@ -410,20 +419,32 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
</>
|
||||
)}
|
||||
{viewMode && (
|
||||
<Connected
|
||||
errorComponent={
|
||||
datasource && isGoogleSheetPlugin && !isPluginAuthorized ? (
|
||||
<AuthMessage
|
||||
actionType={ActionType.AUTHORIZE}
|
||||
<ViewModeWrapper>
|
||||
<Connected
|
||||
errorComponent={
|
||||
datasource && isGoogleSheetPlugin && !isPluginAuthorized ? (
|
||||
<AuthMessage
|
||||
actionType="authorize"
|
||||
datasource={datasource}
|
||||
description={authErrorMessage}
|
||||
pageId={pageId}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
showDatasourceSavedText={!isGoogleSheetPlugin}
|
||||
/>
|
||||
<div style={{ marginTop: "30px" }}>
|
||||
{!_.isNil(formConfig) &&
|
||||
!_.isNil(datasource) &&
|
||||
!hideDatasourceSection ? (
|
||||
<DatasourceInformation
|
||||
config={formConfig[0]}
|
||||
datasource={datasource}
|
||||
description={authErrorMessage}
|
||||
pageId={pageId}
|
||||
viewMode={!!viewMode}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
hideDatasourceRenderSection={hideDatasourceSection}
|
||||
showDatasourceSavedText={!isGoogleSheetPlugin}
|
||||
/>
|
||||
) : undefined}
|
||||
</div>
|
||||
</ViewModeWrapper>
|
||||
)}
|
||||
{/* Render datasource form call-to-actions */}
|
||||
{datasource && (
|
||||
|
|
|
|||
|
|
@ -1213,7 +1213,8 @@ function* filePickerActionCallbackSaga(
|
|||
// Once users selects/cancels the file selection,
|
||||
// Sending sheet ids selected as part of datasource
|
||||
// config properties in order to save it in database
|
||||
set(datasource, "datasourceConfiguration.properties[0]", {
|
||||
// using the second index specifically for file ids.
|
||||
set(datasource, "datasourceConfiguration.properties[1]", {
|
||||
key: createMessage(GSHEET_AUTHORISED_FILE_IDS_KEY),
|
||||
value: fileIds,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.appsmith.external.models.DatasourceConfiguration;
|
|||
public class SheetsUtil {
|
||||
|
||||
private static final String FILE_SPECIFIC_DRIVE_SCOPE = "https://www.googleapis.com/auth/drive.file";
|
||||
private static final int USER_AUTHORIZED_SHEET_IDS_INDEX = 1;
|
||||
static Pattern COLUMN_NAME_PATTERN = Pattern.compile("[a-zA-Z]+");
|
||||
|
||||
public static int getColumnNumber(String columnName) {
|
||||
|
|
@ -33,11 +34,11 @@ public class SheetsUtil {
|
|||
public static Set<String> getUserAuthorizedSheetIds(DatasourceConfiguration datasourceConfiguration) {
|
||||
OAuth2 oAuth2 = (OAuth2) datasourceConfiguration.getAuthentication();
|
||||
if (!isEmpty(datasourceConfiguration.getProperties())
|
||||
&& datasourceConfiguration.getProperties().get(0) != null
|
||||
&& datasourceConfiguration.getProperties().get(0).getValue() != null
|
||||
&& datasourceConfiguration.getProperties().get(USER_AUTHORIZED_SHEET_IDS_INDEX) != null
|
||||
&& datasourceConfiguration.getProperties().get(USER_AUTHORIZED_SHEET_IDS_INDEX).getValue() != null
|
||||
&& oAuth2.getScope() != null
|
||||
&& oAuth2.getScope().contains(FILE_SPECIFIC_DRIVE_SCOPE)) {
|
||||
ArrayList<String> temp = (ArrayList) datasourceConfiguration.getProperties().get(0).getValue();
|
||||
ArrayList<String> temp = (ArrayList) datasourceConfiguration.getProperties().get(USER_AUTHORIZED_SHEET_IDS_INDEX).getValue();
|
||||
return new HashSet<String>(temp);
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,17 @@
|
|||
"hidden": true,
|
||||
"initialValue": "authorization_code"
|
||||
},
|
||||
{
|
||||
"label": "Account",
|
||||
"configProperty": "datasourceConfiguration.properties[0].value",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"isRequired": false,
|
||||
"hidden": {
|
||||
"comparison": "VIEW_MODE",
|
||||
"value": false
|
||||
},
|
||||
"initialValue": "Authorize datasource to fetch account name"
|
||||
},
|
||||
{
|
||||
"label": "Permissions | Scope",
|
||||
"configProperty": "datasourceConfiguration.authentication.scopeString",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user