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:
Ayangade Adeoluwa 2023-04-27 14:52:41 +01:00 committed by GitHub
parent 82d931e173
commit f87ba6a671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 115 additions and 56 deletions

View File

@ -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";

View File

@ -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;

View File

@ -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;

View File

@ -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>
</>
);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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 && (

View File

@ -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,
});

View File

@ -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;

View File

@ -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",