From 2820627f2ddae4d67c2f5ce79531f805fce45ed1 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 22 Mar 2022 12:57:01 -0500 Subject: [PATCH 01/35] rebase --- .../CRUD/data/database/DatabaseList.test.jsx | 45 -------- .../views/CRUD/data/database/DatabaseList.tsx | 57 ---------- .../database/DatabaseModal/ModalHeader.tsx | 37 ++++-- .../data/database/DatabaseModal/index.tsx | 105 +++++++++++++++++- 4 files changed, 129 insertions(+), 115 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx index 12580d8ee73fa..47f4907f48527 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx @@ -23,10 +23,6 @@ import configureStore from 'redux-mock-store'; import fetchMock from 'fetch-mock'; import { Provider } from 'react-redux'; import { styledMount as mount } from 'spec/helpers/theming'; -import { render, screen, cleanup } from 'spec/helpers/testing-library'; -import userEvent from '@testing-library/user-event'; -import { QueryParamProvider } from 'use-query-params'; -import * as featureFlags from 'src/featureFlags'; import DatabaseList from 'src/views/CRUD/data/database/DatabaseList'; import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal'; @@ -208,44 +204,3 @@ describe('DatabaseList', () => { ); }); }); - -describe('RTL', () => { - async function renderAndWait() { - const mounted = act(async () => { - render( - - - , - { useRedux: true }, - mockAppState, - ); - }); - - return mounted; - } - - let isFeatureEnabledMock; - beforeEach(async () => { - isFeatureEnabledMock = jest - .spyOn(featureFlags, 'isFeatureEnabled') - .mockImplementation(() => true); - await renderAndWait(); - }); - - afterEach(() => { - cleanup(); - isFeatureEnabledMock.mockRestore(); - }); - - it('renders an "Import Database" tooltip under import button', async () => { - const importButton = await screen.findByTestId('import-button'); - userEvent.hover(importButton); - - await screen.findByRole('tooltip'); - const importTooltip = screen.getByRole('tooltip', { - name: 'Import databases', - }); - - expect(importTooltip).toBeInTheDocument(); - }); -}); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index 10149bc9e8a16..f980295cc2035 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -30,7 +30,6 @@ import { Tooltip } from 'src/components/Tooltip'; import Icons from 'src/components/Icons'; import ListView, { FilterOperator, Filters } from 'src/components/ListView'; import { commonMenuData } from 'src/views/CRUD/data/common'; -import ImportModelsModal from 'src/components/ImportModal/index'; import handleResourceExport from 'src/utils/export'; import { ExtentionConfigs } from 'src/views/components/types'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; @@ -39,17 +38,6 @@ import DatabaseModal from './DatabaseModal'; import { DatabaseObject } from './types'; const PAGE_SIZE = 25; -const PASSWORDS_NEEDED_MESSAGE = t( - 'The passwords for the databases below are needed in order to ' + - 'import them. Please note that the "Secure Extra" and "Certificate" ' + - 'sections of the database configuration are not present in export ' + - 'files, and should be added manually after the import if they are needed.', -); -const CONFIRM_OVERWRITE_MESSAGE = t( - 'You are importing one or more databases that already exist. ' + - 'Overwriting might cause you to lose some of your work. Are you ' + - 'sure you want to overwrite?', -); interface DatabaseDeleteObject extends DatabaseObject { chart_count: number; @@ -103,8 +91,6 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { const [currentDatabase, setCurrentDatabase] = useState( null, ); - const [importingDatabase, showImportModal] = useState(false); - const [passwordFields, setPasswordFields] = useState([]); const [preparingExport, setPreparingExport] = useState(false); const { roles } = useSelector( state => state.user, @@ -116,20 +102,6 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { ALLOWED_EXTENSIONS, } = useSelector(state => state.common.conf); - const openDatabaseImportModal = () => { - showImportModal(true); - }; - - const closeDatabaseImportModal = () => { - showImportModal(false); - }; - - const handleDatabaseImport = () => { - showImportModal(false); - refreshData(); - addSuccessToast(t('Database imported')); - }; - const openDatabaseDeleteModal = (database: DatabaseObject) => SupersetClient.get({ endpoint: `/api/v1/database/${database.id}/related_objects/`, @@ -245,22 +217,6 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { }, }, ]; - - if (isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT)) { - menuData.buttons.push({ - name: ( - - - - ), - buttonStyle: 'link', - onClick: openDatabaseImportModal, - }); - } } function handleDatabaseExport(database: DatabaseObject) { @@ -526,19 +482,6 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { pageSize={PAGE_SIZE} /> - {preparingExport && } ); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx index 992aa76e36060..c923f425aa9e3 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx @@ -19,6 +19,7 @@ import React from 'react'; import { getDatabaseDocumentationLinks } from 'src/views/CRUD/hooks'; +import { UploadFile } from 'antd/lib/upload/interface'; import { EditHeaderTitle, EditHeaderSubtitle, @@ -61,6 +62,7 @@ const ModalHeader = ({ dbName, dbModel, editNewDb, + fileName, }: { isLoading: boolean; isEditMode: boolean; @@ -70,6 +72,9 @@ const ModalHeader = ({ dbName: string; dbModel: DatabaseForm; editNewDb?: boolean; + fileName?: UploadFile[]; + passwordFields?: string[]; + needsOverwriteConfirm?: boolean; }) => { const isEditHeader = ( @@ -115,6 +120,10 @@ const ModalHeader = ({ ); + console.log( + 'findme modalHeader - fileName', + fileName ? fileName[0].name : '', + ); const hasDbHeader = ( @@ -142,19 +151,23 @@ const ModalHeader = ({ ); + const newHeader = ( + + +

STEP 2 OF 3

+

Enter the required {dbModel.name} credentials

+

{fileName ? fileName[0].name : ''}

+
+
+ ); + + if (fileName) return newHeader; if (isLoading) return <>; - if (isEditMode) { - return isEditHeader; - } - if (useSqlAlchemyForm) { - return useSqlAlchemyFormHeader; - } - if (hasConnectedDb && !editNewDb) { - return hasConnectedDbHeader; - } - if (db || editNewDb) { - return hasDbHeader; - } + if (isEditMode) return isEditHeader; + if (useSqlAlchemyForm) return useSqlAlchemyFormHeader; + if (hasConnectedDb && !editNewDb) return hasConnectedDbHeader; + if (db || editNewDb) return hasDbHeader; + return noDbHeader; }; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 783516231b629..ad337a97e6c83 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -29,8 +29,9 @@ import React, { useReducer, Reducer, } from 'react'; +import { UploadChangeParam, UploadFile } from 'antd/lib/upload/interface'; import Tabs from 'src/components/Tabs'; -import { AntdSelect } from 'src/components'; +import { AntdSelect, Upload } from 'src/components'; import Alert from 'src/components/Alert'; import Modal from 'src/components/Modal'; import Button from 'src/components/Button'; @@ -44,6 +45,7 @@ import { useDatabaseValidation, getDatabaseImages, getConnectionAlert, + useImportResource, } from 'src/views/CRUD/hooks'; import { useCommonConf } from 'src/views/CRUD/data/database/state'; import { @@ -520,6 +522,7 @@ const DatabaseModal: FunctionComponent = ({ setEditNewDb(false); onHide(); }; + const onSave = async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id, ...update } = db || {}; @@ -861,6 +864,49 @@ const DatabaseModal: FunctionComponent = ({ } }, [availableDbs]); + // ------------------------------ FINDME START 🚧 + + const [passwordFields, setPasswordFields] = useState([]); + const [needsOverwriteConfirm, setNeedsOverwriteConfirm] = + useState(false); + const [passwords, setPasswords] = useState>({}); + const [confirmedOverwrite, setConfirmedOverwrite] = useState(false); + + // isImporting ? render modal + + const [fileName, setFileName] = useState([]); + + console.log('findme databasemodal - fileName', fileName); + + const { + state: { alreadyExists, passwordsNeeded }, + importResource, + } = useImportResource('database', t('database'), msg => addDangerToast(msg)); + + useEffect(() => { + setPasswordFields(passwordsNeeded); + }, [passwordsNeeded, setPasswordFields]); + + useEffect(() => { + setNeedsOverwriteConfirm(alreadyExists.length > 0); + }, [alreadyExists, setNeedsOverwriteConfirm]); + + const onDbImport = (info: UploadChangeParam) => { + setFileName([ + { + ...info.file, + status: 'done', + }, + ]); + if (!(fileName[0]?.originFileObj instanceof File)) { + return; + } + + importResource(fileName[0].originFileObj, passwords, confirmedOverwrite); + }; + + // ------------------------------ FINDME END 🚧 + const tabChange = (key: string) => { setTabKey(key); }; @@ -1013,6 +1059,41 @@ const DatabaseModal: FunctionComponent = ({ ); }; + if (fileName.length > 0) { + console.log('findme made it'); + return ( + [ + antDModalNoPaddingStyles, + antDModalStyles(theme), + formHelperStyles(theme), + formStyles(theme), + ]} + name="database" + onHandledPrimaryAction={onSave} + onHide={onClose} + primaryButtonName={t('Connect')} + width="500px" + centered + show={show} + title={

{t('Connect a database')}

} + footer={renderModalFooter()} + > + + Test +
+ ); + } + return useTabLayout ? ( [ @@ -1252,8 +1333,30 @@ const DatabaseModal: FunctionComponent = ({ /> {renderPreferredSelector()} {renderAvailableSelector()} + {/* // ------------------------------ FINDME START 🚧 */} + {}} + onChange={info => { + onDbImport(info); + }} + > + + ) : ( + // ------------------------------ FINDME END 🚧 <> Date: Tue, 22 Mar 2022 12:46:20 -0500 Subject: [PATCH 02/35] more progress --- .../data/database/DatabaseModal/index.tsx | 100 ++++++++++++++++-- .../data/database/DatabaseModal/styles.ts | 23 ++++ 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index ad337a97e6c83..47dc06c2e377a 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -38,6 +38,7 @@ import Button from 'src/components/Button'; import IconButton from 'src/components/IconButton'; import InfoTooltip from 'src/components/InfoTooltip'; import withToasts from 'src/components/MessageToasts/withToasts'; +import ValidatedInput from 'src/components/Form/LabeledErrorBoundInput'; import { testDatabaseConnection, useSingleViewResource, @@ -61,6 +62,7 @@ import DatabaseConnectionForm from './DatabaseConnectionForm'; import { antDErrorAlertStyles, antDAlertStyles, + antdWarningAlertStyles, StyledAlertMargin, antDModalNoPaddingStyles, antDModalStyles, @@ -75,6 +77,7 @@ import { infoTooltip, StyledFooterButton, StyledStickyHeader, + formScrollableStyles, } from './styles'; import ModalHeader, { DOCUMENTATION_LINK } from './ModalHeader'; @@ -437,6 +440,7 @@ const DatabaseModal: FunctionComponent = ({ const [db, setDB] = useReducer< Reducer | null, DBReducerActionType> >(dbReducer, null); + console.log('findme db root', db); const [tabKey, setTabKey] = useState(DEFAULT_TAB_KEY); const [availableDbs, getAvailableDbs] = useAvailableDatabases(); const [validationErrors, getValidation, setValidationErrors] = @@ -884,27 +888,105 @@ const DatabaseModal: FunctionComponent = ({ } = useImportResource('database', t('database'), msg => addDangerToast(msg)); useEffect(() => { + // console.log('findme UE - pw', passwordsNeeded, 'pwfields', passwordFields); setPasswordFields(passwordsNeeded); }, [passwordsNeeded, setPasswordFields]); useEffect(() => { + // console.log( + // 'findme UE - ow', + // alreadyExists, + // 'confirm', + // needsOverwriteConfirm, + // ); setNeedsOverwriteConfirm(alreadyExists.length > 0); }, [alreadyExists, setNeedsOverwriteConfirm]); const onDbImport = (info: UploadChangeParam) => { + console.log('findme odi entrance'); setFileName([ { ...info.file, status: 'done', }, ]); - if (!(fileName[0]?.originFileObj instanceof File)) { - return; - } + if (!(fileName[0]?.originFileObj instanceof File)) return; + console.log( + 'findme odi filecheck', + fileName[0].originFileObj, + passwords, + confirmedOverwrite, + ); + + console.log( + 'findme onDbImport', + fileName[0].originFileObj, + passwords, + confirmedOverwrite, + ); importResource(fileName[0].originFileObj, passwords, confirmedOverwrite); }; + const passwordNeededField = () => ( + // passwordFields.map(password => ( + // passwordsNeeded.length > 0 && ( + <> + + antDAlertStyles(theme)} + type="info" + showIcon + message="Database passwords" + description={t( + `The passwords for the databases below are needed in order to import them. Please not that the "Secure Extra" and "Certificate" sections of the database configuration are not present in explore files and should be added manually after the import if they are needed.`, + )} + /> + + {} }} + errorMessage={validationErrors?.password_needed} + label={t(`${db?.database_name} PASSWORD`)} + css={formScrollableStyles} + /> + + // )); + ); + + const confirmOverwriteField = () => ( + // alreadyExists.length > 0 && ( + <> + + antdWarningAlertStyles(theme)} + type="warning" + showIcon + message="" + description={t( + 'You are importing one or more databases that already exist. Overwriting might cause you to lose some of your work. Are you sure you want to overwrite?', + )} + /> + + {} }} + errorMessage={validationErrors?.confirm_overwrite} + label={t(`TYPE "OVERWRITE" TO CONFIRM`)} + css={formScrollableStyles} + /> + + ); + // ------------------------------ FINDME END 🚧 const tabChange = (key: string) => { @@ -1059,8 +1141,10 @@ const DatabaseModal: FunctionComponent = ({ ); }; + // console.log('findme pwneed exists', passwordsNeeded, alreadyExists); + if (fileName.length > 0) { - console.log('findme made it'); + console.log('findme made it', db, db?.database_name); return ( [ @@ -1089,7 +1173,8 @@ const DatabaseModal: FunctionComponent = ({ dbModel={dbModel} fileName={fileName} /> - Test + {passwordNeededField()} + {confirmOverwriteField()} ); } @@ -1341,15 +1426,12 @@ const DatabaseModal: FunctionComponent = ({ accept=".yaml,.json,.yml,.zip" // upload is handled by hook customRequest={() => {}} - onChange={info => { - onDbImport(info); - }} + onChange={info => onDbImport(info)} > diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts index c0e65b97774cc..e479c793e8789 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts @@ -218,6 +218,29 @@ export const antDErrorAlertStyles = (theme: SupersetTheme) => css` } `; +export const antdWarningAlertStyles = (theme: SupersetTheme) => css` + border: 1px solid ${theme.colors.warning.light1}; + padding: ${theme.gridUnit * 4}px; + margin: ${theme.gridUnit * 4}px 0; + color: ${theme.colors.warning.dark2}; + + .ant-alert-message { + margin: 0; + } + + .ant-alert-description { + font-size: ${theme.typography.sizes.s + 1}px; + line-height: ${theme.gridUnit * 4}px; + + .ant-alert-icon { + margin-right: ${theme.gridUnit * 2.5}px; + font-size: ${theme.typography.sizes.l + 1}px; + position: relative; + top: ${theme.gridUnit / 4}px; + } + } +`; + export const formHelperStyles = (theme: SupersetTheme) => css` .required { margin-left: ${theme.gridUnit / 2}px; From bc7ef74f7ec699b7c5701e10fcde78c8a7071e7f Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 22 Mar 2022 13:02:21 -0500 Subject: [PATCH 03/35] Fix unintended changes --- .../CRUD/data/database/DatabaseList.test.jsx | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx index 47f4907f48527..12580d8ee73fa 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.test.jsx @@ -23,6 +23,10 @@ import configureStore from 'redux-mock-store'; import fetchMock from 'fetch-mock'; import { Provider } from 'react-redux'; import { styledMount as mount } from 'spec/helpers/theming'; +import { render, screen, cleanup } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import { QueryParamProvider } from 'use-query-params'; +import * as featureFlags from 'src/featureFlags'; import DatabaseList from 'src/views/CRUD/data/database/DatabaseList'; import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal'; @@ -204,3 +208,44 @@ describe('DatabaseList', () => { ); }); }); + +describe('RTL', () => { + async function renderAndWait() { + const mounted = act(async () => { + render( + + + , + { useRedux: true }, + mockAppState, + ); + }); + + return mounted; + } + + let isFeatureEnabledMock; + beforeEach(async () => { + isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockImplementation(() => true); + await renderAndWait(); + }); + + afterEach(() => { + cleanup(); + isFeatureEnabledMock.mockRestore(); + }); + + it('renders an "Import Database" tooltip under import button', async () => { + const importButton = await screen.findByTestId('import-button'); + userEvent.hover(importButton); + + await screen.findByRole('tooltip'); + const importTooltip = screen.getByRole('tooltip', { + name: 'Import databases', + }); + + expect(importTooltip).toBeInTheDocument(); + }); +}); From 1a53bdb35a3d24b4fe90a3fab14e21dd9fd8c974 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 23 Mar 2022 22:43:33 -0500 Subject: [PATCH 04/35] DB import goes to step 3 --- .../database/DatabaseModal/ModalHeader.tsx | 19 +- .../data/database/DatabaseModal/index.tsx | 276 ++++++++++-------- superset-frontend/src/views/CRUD/hooks.ts | 2 + 3 files changed, 169 insertions(+), 128 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx index c923f425aa9e3..ba669eb630a50 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx @@ -53,6 +53,7 @@ const documentationLink = (engine: string | undefined) => { } return irregularDocumentationLinks[engine]; }; + const ModalHeader = ({ isLoading, isEditMode, @@ -62,7 +63,7 @@ const ModalHeader = ({ dbName, dbModel, editNewDb, - fileName, + file, }: { isLoading: boolean; isEditMode: boolean; @@ -72,7 +73,7 @@ const ModalHeader = ({ dbName: string; dbModel: DatabaseForm; editNewDb?: boolean; - fileName?: UploadFile[]; + file?: UploadFile[]; passwordFields?: string[]; needsOverwriteConfirm?: boolean; }) => { @@ -82,6 +83,7 @@ const ModalHeader = ({ {dbName} ); + const useSqlAlchemyFormHeader = (

STEP 2 OF 2

@@ -99,6 +101,7 @@ const ModalHeader = ({

); + const hasConnectedDbHeader = ( @@ -120,10 +123,7 @@ const ModalHeader = ({ ); - console.log( - 'findme modalHeader - fileName', - fileName ? fileName[0].name : '', - ); + const hasDbHeader = ( @@ -142,6 +142,7 @@ const ModalHeader = ({ ); + const noDbHeader = (
@@ -151,17 +152,17 @@ const ModalHeader = ({ ); - const newHeader = ( + const importDbHeader = (

STEP 2 OF 3

Enter the required {dbModel.name} credentials

-

{fileName ? fileName[0].name : ''}

+

{file ? file[0].name : ''}

); - if (fileName) return newHeader; + if (file && !hasConnectedDb) return importDbHeader; if (isLoading) return <>; if (isEditMode) return isEditHeader; if (useSqlAlchemyForm) return useSqlAlchemyFormHeader; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 47dc06c2e377a..cde2c277478f7 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -404,10 +404,15 @@ function dbReducer( return { ...action.payload, }; + case ActionType.configMethodChange: return { ...action.payload, }; + + // case ActionType.importDatabase: + // return {}; + case ActionType.reset: default: return null; @@ -440,7 +445,6 @@ const DatabaseModal: FunctionComponent = ({ const [db, setDB] = useReducer< Reducer | null, DBReducerActionType> >(dbReducer, null); - console.log('findme db root', db); const [tabKey, setTabKey] = useState(DEFAULT_TAB_KEY); const [availableDbs, getAvailableDbs] = useAvailableDatabases(); const [validationErrors, getValidation, setValidationErrors] = @@ -518,15 +522,39 @@ const DatabaseModal: FunctionComponent = ({ ); }; + const [passwordFields, setPasswordFields] = useState([]); // set in useEffect + const [needsOverwriteConfirm, setNeedsOverwriteConfirm] = + useState(false); // set in useEffect + const [passwords, setPasswords] = useState>({}); // set by user input + const [confirmedOverwrite, setConfirmedOverwrite] = useState(false); // set by user input + const [file, setFile] = useState([]); + const [importingModel, setImportingModel] = useState(false); + console.log('findme importing', importingModel); + + const { + state: { alreadyExists, passwordsNeeded }, + importResource, + } = useImportResource('database', t('database'), msg => addDangerToast(msg)); + const onClose = () => { setDB({ type: ActionType.reset }); setHasConnectedDb(false); setValidationErrors(null); // reset validation errors on close clearError(); setEditNewDb(false); + setFile(file.filter(currentFile => currentFile.uid !== file[0].uid)); + setPasswordFields([]); + setNeedsOverwriteConfirm(false); + setImportingModel(false); onHide(); }; + const onChange = (type: any, payload: any) => { + setDB({ type, payload } as DBReducerActionType); + }; + + console.log('findme DB', db); + const onSave = async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { id, ...update } = db || {}; @@ -602,9 +630,7 @@ const DatabaseModal: FunctionComponent = ({ dbToUpdate.configuration_method === CONFIGURATION_METHOD.DYNAMIC_FORM, // onShow toast on SQLA Forms ); if (result) { - if (onDatabaseAdd) { - onDatabaseAdd(); - } + if (onDatabaseAdd) onDatabaseAdd(); if (!editNewDb) { onClose(); addSuccessToast(t('Database settings updated')); @@ -619,9 +645,35 @@ const DatabaseModal: FunctionComponent = ({ ); if (dbId) { setHasConnectedDb(true); - if (onDatabaseAdd) { - onDatabaseAdd(); + if (onDatabaseAdd) onDatabaseAdd(); + if (useTabLayout) { + // tab layout only has one step + // so it should close immediately on save + onClose(); + addSuccessToast(t('Database connected')); } + } + } + + // Import - doesn't use db state + if (!db) { + setLoading(true); + setImportingModel(true); + onChange(ActionType.textChange, { + name: 'database_name', + value: 'TEST database name', + }); + + if (!(file[0].originFileObj instanceof File)) return; + const dbId = await importResource( + file[0].originFileObj, + passwords, + confirmedOverwrite, + ); + + if (dbId) { + setHasConnectedDb(true); + if (onDatabaseAdd) onDatabaseAdd(); if (useTabLayout) { // tab layout only has one step // so it should close immediately on save @@ -630,14 +682,11 @@ const DatabaseModal: FunctionComponent = ({ } } } + setEditNewDb(false); setLoading(false); }; - const onChange = (type: any, payload: any) => { - setDB({ type, payload } as DBReducerActionType); - }; - // Initialize const fetchDB = () => { if (isEditMode && databaseId) { @@ -788,7 +837,7 @@ const DatabaseModal: FunctionComponent = ({ const renderModalFooter = () => { if (db) { // if db show back + connenct - if (!hasConnectedDb || editNewDb) { + if (!hasConnectedDb || editNewDb || !importingModel) { return ( <> @@ -821,6 +870,7 @@ const DatabaseModal: FunctionComponent = ({ ); } + return []; }; @@ -834,6 +884,7 @@ const DatabaseModal: FunctionComponent = ({ ); + useEffect(() => { if (show) { setTabKey(DEFAULT_TAB_KEY); @@ -870,128 +921,103 @@ const DatabaseModal: FunctionComponent = ({ // ------------------------------ FINDME START 🚧 - const [passwordFields, setPasswordFields] = useState([]); - const [needsOverwriteConfirm, setNeedsOverwriteConfirm] = - useState(false); - const [passwords, setPasswords] = useState>({}); - const [confirmedOverwrite, setConfirmedOverwrite] = useState(false); - - // isImporting ? render modal - - const [fileName, setFileName] = useState([]); - - console.log('findme databasemodal - fileName', fileName); - - const { - state: { alreadyExists, passwordsNeeded }, - importResource, - } = useImportResource('database', t('database'), msg => addDangerToast(msg)); - useEffect(() => { - // console.log('findme UE - pw', passwordsNeeded, 'pwfields', passwordFields); setPasswordFields(passwordsNeeded); + if (passwordsNeeded.length > 0) { + setImportingModel(false); + } }, [passwordsNeeded, setPasswordFields]); useEffect(() => { - // console.log( - // 'findme UE - ow', - // alreadyExists, - // 'confirm', - // needsOverwriteConfirm, - // ); setNeedsOverwriteConfirm(alreadyExists.length > 0); + if (alreadyExists.length > 0) { + setImportingModel(false); + } }, [alreadyExists, setNeedsOverwriteConfirm]); const onDbImport = (info: UploadChangeParam) => { - console.log('findme odi entrance'); - setFileName([ + setImportingModel(true); + setFile([ { ...info.file, status: 'done', }, ]); - if (!(fileName[0]?.originFileObj instanceof File)) return; - console.log( - 'findme odi filecheck', - fileName[0].originFileObj, - passwords, - confirmedOverwrite, - ); + if (!(info.file.originFileObj instanceof File)) return; + importResource(info.file.originFileObj, passwords, confirmedOverwrite); + }; - console.log( - 'findme onDbImport', - fileName[0].originFileObj, - passwords, - confirmedOverwrite, + const passwordNeededField = () => + passwordFields.map( + password => + passwordsNeeded.length > 0 && ( + <> + + antDAlertStyles(theme)} + type="info" + showIcon + message="Database passwords" + description={t( + `The passwords for the databases below are needed in order to import them. Please not that the "Secure Extra" and "Certificate" sections of the database configuration are not present in explore files and should be added manually after the import if they are needed.`, + )} + /> + + {} }} + errorMessage={validationErrors?.password_needed} + label={t(`${db?.database_name} PASSWORD`)} + css={formScrollableStyles} + /> + + ), ); - importResource(fileName[0].originFileObj, passwords, confirmedOverwrite); + + const confirmOverwrite = (event: React.ChangeEvent) => { + const targetValue = (event.currentTarget?.value as string) ?? ''; + setConfirmedOverwrite(targetValue.toUpperCase() === t('OVERWRITE')); }; - const passwordNeededField = () => ( - // passwordFields.map(password => ( - // passwordsNeeded.length > 0 && ( - <> - - antDAlertStyles(theme)} - type="info" - showIcon - message="Database passwords" - description={t( - `The passwords for the databases below are needed in order to import them. Please not that the "Secure Extra" and "Certificate" sections of the database configuration are not present in explore files and should be added manually after the import if they are needed.`, - )} - /> - - {} }} - errorMessage={validationErrors?.password_needed} - label={t(`${db?.database_name} PASSWORD`)} - css={formScrollableStyles} - /> - - // )); - ); + const confirmOverwriteField = () => { + if (!needsOverwriteConfirm) return null; - const confirmOverwriteField = () => ( - // alreadyExists.length > 0 && ( - <> - - antdWarningAlertStyles(theme)} - type="warning" - showIcon - message="" - description={t( - 'You are importing one or more databases that already exist. Overwriting might cause you to lose some of your work. Are you sure you want to overwrite?', - )} + return ( + <> + + antdWarningAlertStyles(theme)} + type="warning" + showIcon + message="" + description={t( + 'You are importing one or more databases that already exist. Overwriting might cause you to lose some of your work. Are you sure you want to overwrite?', + )} + /> + + {} }} + errorMessage={validationErrors?.confirm_overwrite} + label={t(`TYPE "OVERWRITE" TO CONFIRM`)} + onChange={confirmOverwrite} + css={formScrollableStyles} /> - - {} }} - errorMessage={validationErrors?.confirm_overwrite} - label={t(`TYPE "OVERWRITE" TO CONFIRM`)} - css={formScrollableStyles} - /> - - ); - + + ); + }; // ------------------------------ FINDME END 🚧 - const tabChange = (key: string) => { - setTabKey(key); - }; + const tabChange = (key: string) => setTabKey(key); const renderStepTwoAlert = () => { const { hostname } = window.location; @@ -999,9 +1025,7 @@ const DatabaseModal: FunctionComponent = ({ const regionalIPs = connectionAlert?.REGIONAL_IPS || {}; Object.entries(regionalIPs).forEach(([ipRegion, ipRange]) => { const regex = new RegExp(ipRegion); - if (hostname.match(regex)) { - ipAlert = ipRange; - } + if (hostname.match(regex)) ipAlert = ipRange; }); return ( db?.engine && ( @@ -1141,10 +1165,10 @@ const DatabaseModal: FunctionComponent = ({ ); }; - // console.log('findme pwneed exists', passwordsNeeded, alreadyExists); - - if (fileName.length > 0) { - console.log('findme made it', db, db?.database_name); + if ( + (passwordsNeeded.length > 0 || alreadyExists.length > 0) && + !hasConnectedDb + ) { return ( [ @@ -1161,7 +1185,21 @@ const DatabaseModal: FunctionComponent = ({ centered show={show} title={

{t('Connect a database')}

} - footer={renderModalFooter()} + footer={ + <> + + {t('Back')} + + + {t('Connect')} + + + // renderModalFooter() + } > = ({ db={db} dbName={dbName} dbModel={dbModel} - fileName={fileName} + file={file} /> {passwordNeededField()} {confirmOverwriteField()} diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts index ae58aef0d9e0f..83c90c2597a3e 100644 --- a/superset-frontend/src/views/CRUD/hooks.ts +++ b/superset-frontend/src/views/CRUD/hooks.ts @@ -425,6 +425,8 @@ export function useImportResource( formData.append('overwrite', 'true'); } + console.log('FINDME formdata', formData); + return SupersetClient.post({ endpoint: `/api/v1/${resourceName}/import/`, body: formData, From 84dd7f5b72a6178217755cc22a7ed839a92412ad Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 24 Mar 2022 15:45:28 -0500 Subject: [PATCH 05/35] debugging --- .../data/database/DatabaseModal/index.tsx | 90 ++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index cde2c277478f7..28b61bddc2fb4 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -536,13 +536,15 @@ const DatabaseModal: FunctionComponent = ({ importResource, } = useImportResource('database', t('database'), msg => addDangerToast(msg)); + console.log('findme STATE', alreadyExists, passwordsNeeded); + const onClose = () => { setDB({ type: ActionType.reset }); setHasConnectedDb(false); setValidationErrors(null); // reset validation errors on close clearError(); setEditNewDb(false); - setFile(file.filter(currentFile => currentFile.uid !== file[0].uid)); + // setFile(file.filter(currentFile => currentFile.uid !== file[0].uid)); setPasswordFields([]); setNeedsOverwriteConfirm(false); setImportingModel(false); @@ -672,14 +674,17 @@ const DatabaseModal: FunctionComponent = ({ ); if (dbId) { - setHasConnectedDb(true); - if (onDatabaseAdd) onDatabaseAdd(); - if (useTabLayout) { - // tab layout only has one step - // so it should close immediately on save - onClose(); - addSuccessToast(t('Database connected')); - } + // setHasConnectedDb(true); + // if (onDatabaseAdd) onDatabaseAdd(); + // if (useTabLayout) { + // // tab layout only has one step + // // so it should close immediately on save + // onClose(); + // addSuccessToast(t('Database connected')); + // } + + onClose(); + addSuccessToast(t('Database connected')); } } @@ -948,37 +953,42 @@ const DatabaseModal: FunctionComponent = ({ importResource(info.file.originFileObj, passwords, confirmedOverwrite); }; - const passwordNeededField = () => - passwordFields.map( - password => - passwordsNeeded.length > 0 && ( - <> - - antDAlertStyles(theme)} - type="info" - showIcon - message="Database passwords" - description={t( - `The passwords for the databases below are needed in order to import them. Please not that the "Secure Extra" and "Certificate" sections of the database configuration are not present in explore files and should be added manually after the import if they are needed.`, - )} - /> - - {} }} - errorMessage={validationErrors?.password_needed} - label={t(`${db?.database_name} PASSWORD`)} - css={formScrollableStyles} - /> - - ), - ); + console.log('findme PASSWORDS', passwords); + console.log('findme PASSWORDFIELDS', passwordFields); + + const passwordNeededField = () => { + // if (passwordFields.length === 0) return null; + + passwordFields.map(database => ( + <> + + antDAlertStyles(theme)} + type="info" + showIcon + message="Database passwords" + description={t( + `The passwords for the databases below are needed in order to import them. Please not that the "Secure Extra" and "Certificate" sections of the database configuration are not present in explore files and should be added manually after the import if they are needed.`, + )} + /> + + ) => + setPasswords({ ...passwords, [database]: event.target.value }) + } + validationMethods={{ onBlur: () => {} }} + errorMessage={validationErrors?.password_needed} + label={t(`${database.slice(10)} PASSWORD`)} + css={formScrollableStyles} + /> + + )); + }; const confirmOverwrite = (event: React.ChangeEvent) => { const targetValue = (event.currentTarget?.value as string) ?? ''; From 3a86dea84f4c70e7d0e5bad78379a368dc77af80 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Mon, 28 Mar 2022 15:57:00 -0500 Subject: [PATCH 06/35] DB list refreshing properly --- .../database/DatabaseModal/ModalHeader.tsx | 2 +- .../data/database/DatabaseModal/index.tsx | 47 ++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx index ba669eb630a50..f13e1b734d130 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx @@ -155,7 +155,7 @@ const ModalHeader = ({ const importDbHeader = ( -

STEP 2 OF 3

+

STEP 2 OF 2

Enter the required {dbModel.name} credentials

{file ? file[0].name : ''}

diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 28b61bddc2fb4..3bc18a28f5d9a 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -538,19 +538,34 @@ const DatabaseModal: FunctionComponent = ({ console.log('findme STATE', alreadyExists, passwordsNeeded); + const blankFile = { + lastModified: 0, + lastModifiedDate: undefined, + name: '', + originFileObj: undefined, + percent: 0, + size: 0, + status: undefined, + type: '', + uid: '', + }; + const onClose = () => { setDB({ type: ActionType.reset }); setHasConnectedDb(false); setValidationErrors(null); // reset validation errors on close clearError(); setEditNewDb(false); - // setFile(file.filter(currentFile => currentFile.uid !== file[0].uid)); + setFile([blankFile]); setPasswordFields([]); setNeedsOverwriteConfirm(false); setImportingModel(false); + if (onDatabaseAdd) onDatabaseAdd(); onHide(); }; + console.log('findme file', file); + const onChange = (type: any, payload: any) => { setDB({ type, payload } as DBReducerActionType); }; @@ -661,10 +676,10 @@ const DatabaseModal: FunctionComponent = ({ if (!db) { setLoading(true); setImportingModel(true); - onChange(ActionType.textChange, { - name: 'database_name', - value: 'TEST database name', - }); + // onChange(ActionType.textChange, { + // name: 'database_name', + // value: 'TEST database name', + // }); if (!(file[0].originFileObj instanceof File)) return; const dbId = await importResource( @@ -674,14 +689,8 @@ const DatabaseModal: FunctionComponent = ({ ); if (dbId) { - // setHasConnectedDb(true); - // if (onDatabaseAdd) onDatabaseAdd(); - // if (useTabLayout) { - // // tab layout only has one step - // // so it should close immediately on save - // onClose(); - // addSuccessToast(t('Database connected')); - // } + console.log('findme onsave !db && dbId'); + setHasConnectedDb(true); onClose(); addSuccessToast(t('Database connected')); @@ -928,16 +937,10 @@ const DatabaseModal: FunctionComponent = ({ useEffect(() => { setPasswordFields(passwordsNeeded); - if (passwordsNeeded.length > 0) { - setImportingModel(false); - } }, [passwordsNeeded, setPasswordFields]); useEffect(() => { setNeedsOverwriteConfirm(alreadyExists.length > 0); - if (alreadyExists.length > 0) { - setImportingModel(false); - } }, [alreadyExists, setNeedsOverwriteConfirm]); const onDbImport = (info: UploadChangeParam) => { @@ -1175,9 +1178,11 @@ const DatabaseModal: FunctionComponent = ({ ); }; + console.log('findme importing', importingModel); + if ( - (passwordsNeeded.length > 0 || alreadyExists.length > 0) && - !hasConnectedDb + importingModel && + (passwordsNeeded.length > 0 || alreadyExists.length > 0) ) { return ( Date: Mon, 28 Mar 2022 16:26:58 -0500 Subject: [PATCH 07/35] import screens flowing properly --- .../database/DatabaseModal/ModalHeader.tsx | 10 ++++--- .../data/database/DatabaseModal/index.tsx | 26 +++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx index f13e1b734d130..2537d2fcc6548 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/ModalHeader.tsx @@ -64,7 +64,8 @@ const ModalHeader = ({ dbModel, editNewDb, file, -}: { +}: // importingModel, +{ isLoading: boolean; isEditMode: boolean; useSqlAlchemyForm: boolean; @@ -76,6 +77,7 @@ const ModalHeader = ({ file?: UploadFile[]; passwordFields?: string[]; needsOverwriteConfirm?: boolean; + // importingModel?: boolean; }) => { const isEditHeader = ( @@ -152,17 +154,19 @@ const ModalHeader = ({ ); + const fileCheck = file && file?.length > 0; + const importDbHeader = (

STEP 2 OF 2

Enter the required {dbModel.name} credentials

-

{file ? file[0].name : ''}

+

{fileCheck ? file[0].name : ''}

); - if (file && !hasConnectedDb) return importDbHeader; + if (fileCheck) return importDbHeader; if (isLoading) return <>; if (isEditMode) return isEditHeader; if (useSqlAlchemyForm) return useSqlAlchemyFormHeader; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 3bc18a28f5d9a..c9b2dc4670593 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -538,16 +538,9 @@ const DatabaseModal: FunctionComponent = ({ console.log('findme STATE', alreadyExists, passwordsNeeded); - const blankFile = { - lastModified: 0, - lastModifiedDate: undefined, - name: '', - originFileObj: undefined, - percent: 0, - size: 0, - status: undefined, - type: '', - uid: '', + const removeFile = (removedFile: UploadFile) => { + setFile(file.filter(file => file.uid !== removedFile.uid)); + return false; }; const onClose = () => { @@ -556,7 +549,7 @@ const DatabaseModal: FunctionComponent = ({ setValidationErrors(null); // reset validation errors on close clearError(); setEditNewDb(false); - setFile([blankFile]); + setFile([]); setPasswordFields([]); setNeedsOverwriteConfirm(false); setImportingModel(false); @@ -940,6 +933,7 @@ const DatabaseModal: FunctionComponent = ({ }, [passwordsNeeded, setPasswordFields]); useEffect(() => { + console.log('findme UE', needsOverwriteConfirm, alreadyExists); setNeedsOverwriteConfirm(alreadyExists.length > 0); }, [alreadyExists, setNeedsOverwriteConfirm]); @@ -999,7 +993,8 @@ const DatabaseModal: FunctionComponent = ({ }; const confirmOverwriteField = () => { - if (!needsOverwriteConfirm) return null; + console.log('findme AE', alreadyExists); + if (alreadyExists.length === 0) return null; return ( <> @@ -1180,10 +1175,7 @@ const DatabaseModal: FunctionComponent = ({ console.log('findme importing', importingModel); - if ( - importingModel && - (passwordsNeeded.length > 0 || alreadyExists.length > 0) - ) { + if (file.length > 0 && importingModel) { return ( [ @@ -1225,6 +1217,7 @@ const DatabaseModal: FunctionComponent = ({ dbName={dbName} dbModel={dbModel} file={file} + // importingModal={importingModel} /> {passwordNeededField()} {confirmOverwriteField()} @@ -1480,6 +1473,7 @@ const DatabaseModal: FunctionComponent = ({ // upload is handled by hook customRequest={() => {}} onChange={info => onDbImport(info)} + onRemove={removeFile} > diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts index e479c793e8789..a5eff0267cb35 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts @@ -422,6 +422,13 @@ export const buttonLinkStyles = (theme: SupersetTheme) => css` padding-right: ${theme.gridUnit * 2}px; `; +export const importDbButtonLinkStyles = (theme: SupersetTheme) => css` + font-size: ${theme.gridUnit * 3.5}px; + font-weight: ${theme.typography.weights.normal}; + text-transform: initial; + padding-right: ${theme.gridUnit * 2}px; +`; + export const alchemyButtonLinkStyles = (theme: SupersetTheme) => css` font-weight: ${theme.typography.weights.normal}; text-transform: initial; From 677c546151b5e5965900722990688a7bfa6c79ec Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Mon, 4 Apr 2022 11:23:18 -0500 Subject: [PATCH 24/35] Removed animation from import db button --- superset-frontend/src/components/Button/index.tsx | 8 ++++++++ .../src/views/CRUD/data/database/DatabaseModal/index.tsx | 1 + 2 files changed, 9 insertions(+) diff --git a/superset-frontend/src/components/Button/index.tsx b/superset-frontend/src/components/Button/index.tsx index bfe8bdb6597a3..9f8dceda745cf 100644 --- a/superset-frontend/src/components/Button/index.tsx +++ b/superset-frontend/src/components/Button/index.tsx @@ -66,6 +66,14 @@ export interface ButtonProps { cta?: boolean; loading?: boolean | { delay?: number | undefined } | undefined; showMarginRight?: boolean; + type?: + | 'default' + | 'text' + | 'link' + | 'primary' + | 'dashed' + | 'ghost' + | undefined; } export default function Button(props: ButtonProps) { diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index b426ad7d47d06..50f5f92b4adf1 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -1454,6 +1454,7 @@ const DatabaseModal: FunctionComponent = ({ - + + + ) : ( <> diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts index a5eff0267cb35..3fcf93665836b 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts @@ -613,3 +613,9 @@ export const StyledCatalogTable = styled.div` width: 95%; } `; + +export const StyledUploadWrapper = styled.div` + .ant-progress-inner { + display: none; + } +`; From 5a4ee8b3f72c38a8a4493e9cd9d16b3e421be245 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 7 Apr 2022 13:29:35 -0500 Subject: [PATCH 32/35] Hide trashcan icon on import filename --- .../src/views/CRUD/data/database/DatabaseModal/styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts index 3fcf93665836b..39302168b2f07 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/styles.ts @@ -618,4 +618,8 @@ export const StyledUploadWrapper = styled.div` .ant-progress-inner { display: none; } + + .ant-upload-list-item-card-actions { + display: none; + } `; From 87f4cac6463324bd89129e7721b7764ff62e5f57 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 7 Apr 2022 14:08:33 -0500 Subject: [PATCH 33/35] Modal scroll behavior fixed for importing filename --- .../src/views/CRUD/data/database/DatabaseModal/index.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 6b7b9d466f0ad..728c92bd5f4d2 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -969,6 +969,15 @@ const DatabaseModal: FunctionComponent = ({ } }, [availableDbs]); + // This forces the modal to scroll until the importing filename is in view + useEffect(() => { + if (importingModal) { + document + .getElementsByClassName('ant-upload-list-item-name')[0] + .scrollIntoView(); + } + }, [importingModal]); + const onDbImport = async (info: UploadChangeParam) => { setImportingModal(true); setFileList([ From b61afd4abeaebd7a5da13359891d443dea323c94 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 7 Apr 2022 14:19:31 -0500 Subject: [PATCH 34/35] Changed errored to failed --- .../views/CRUD/data/database/DatabaseModal/index.tsx | 2 +- superset-frontend/src/views/CRUD/hooks.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx index 728c92bd5f4d2..fab0e34830402 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx @@ -553,7 +553,7 @@ const DatabaseModal: FunctionComponent = ({ alreadyExists, passwordsNeeded, loading: importLoading, - errored: importErrored, + failed: importErrored, }, importResource, } = useImportResource('database', t('database'), msg => { diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts index abafb7c9de0bf..3c563c2e06ab5 100644 --- a/superset-frontend/src/views/CRUD/hooks.ts +++ b/superset-frontend/src/views/CRUD/hooks.ts @@ -381,7 +381,7 @@ interface ImportResourceState { loading: boolean; passwordsNeeded: string[]; alreadyExists: string[]; - errored: boolean; + failed: boolean; } export function useImportResource( @@ -393,7 +393,7 @@ export function useImportResource( loading: false, passwordsNeeded: [], alreadyExists: [], - errored: false, + failed: false, }); function updateState(update: Partial) { @@ -409,7 +409,7 @@ export function useImportResource( // Set loading state updateState({ loading: true, - errored: false, + failed: false, }); const formData = new FormData(); @@ -437,14 +437,14 @@ export function useImportResource( updateState({ passwordsNeeded: [], alreadyExists: [], - errored: false, + failed: false, }); return true; }) .catch(response => getClientErrorObject(response).then(error => { updateState({ - errored: true, + failed: true, }); if (!error.errors) { handleErrorMsg( From b4c6b6a26ae0d7afd5323e0f3853f99910b84e1e Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 7 Apr 2022 18:56:35 -0500 Subject: [PATCH 35/35] RTL testing for db import --- .../data/database/DatabaseModal/index.test.jsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx index 9db2333573dfa..79a11b0b13438 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx @@ -1028,7 +1028,24 @@ describe('DatabaseModal', () => { */ }); }); + + describe('Import database flow', () => { + it('imports a file', () => { + const importDbButton = screen.getByTestId('import-database-btn'); + expect(importDbButton).toBeVisible(); + + const testFile = new File([new ArrayBuffer(1)], 'model_export.zip'); + + userEvent.click(importDbButton); + userEvent.upload(importDbButton, testFile); + + expect(importDbButton.files[0]).toStrictEqual(testFile); + expect(importDbButton.files.item(0)).toStrictEqual(testFile); + expect(importDbButton.files).toHaveLength(1); + }); + }); }); + describe('DatabaseModal w/ Deeplinking Engine', () => { const renderAndWait = async () => { const mounted = act(async () => {