/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React, { FunctionComponent, useState, useRef, useEffect, useCallback, } from 'react'; import { FormControl, FormControlProps } from 'react-bootstrap'; import Alert from 'src/components/Alert'; import { SupersetClient, t, styled } from '@superset-ui/core'; import TableView, { EmptyWrapperType } from 'src/components/TableView'; import StyledModal from 'src/common/components/Modal'; import Button from 'src/components/Button'; import { useListViewResource } from 'src/views/CRUD/hooks'; import Dataset from 'src/types/Dataset'; import { useDebouncedEffect } from 'src/explore/exploreUtils'; import { getClientErrorObject } from '../utils/getClientErrorObject'; import Loading from '../components/Loading'; import withToasts from '../messageToasts/enhancers/withToasts'; const CONFIRM_WARNING_MESSAGE = t( 'Warning! Changing the dataset may break the chart if the metadata does not exist.', ); const CHANGE_WARNING_MSG = t( 'Changing the dataset may break the chart if the chart relies ' + 'on columns or metadata that does not exist in the target dataset', ); interface Datasource { type: string; id: number; uid: string; } interface ChangeDatasourceModalProps { addDangerToast: (msg: string) => void; addSuccessToast: (msg: string) => void; onChange: (uid: string) => void; onDatasourceSave: (datasource: object, errors?: Array) => {}; onHide: () => void; show: boolean; } const ConfirmModalStyled = styled.div` .btn-container { display: flex; justify-content: flex-end; padding: 0px 15px; margin: 10px 0 0 0; } .confirm-modal-container { margin: 9px; } `; const StyledSpan = styled.span` cursor: pointer; color: ${({ theme }) => theme.colors.primary.dark1}; &: hover { color: ${({ theme }) => theme.colors.primary.dark2}; } `; const TABLE_COLUMNS = [ 'name', 'type', 'schema', 'connection', 'creator', ].map(col => ({ accessor: col, Header: col })); const emptyRequest = { pageIndex: 0, pageSize: 20, filters: [], sortBy: [{ id: 'changed_on_delta_humanized' }], }; const ChangeDatasourceModal: FunctionComponent = ({ addDangerToast, addSuccessToast, onChange, onDatasourceSave, onHide, show, }) => { const [filter, setFilter] = useState(undefined); const [confirmChange, setConfirmChange] = useState(false); const [confirmedDataset, setConfirmedDataset] = useState(); let searchRef = useRef(null); const { state: { loading, resourceCollection }, fetchData, } = useListViewResource('dataset', t('dataset'), addDangerToast); const selectDatasource = useCallback((datasource: Datasource) => { setConfirmChange(true); setConfirmedDataset(datasource); }, []); useDebouncedEffect( () => { fetchData({ ...emptyRequest, ...(filter && { filters: [ { id: 'table_name', operator: 'ct', value: filter, }, ], }), }); }, 300, [filter], ); useEffect(() => { const onEnterModal = async () => { if (searchRef && searchRef.current) { searchRef.current.focus(); } }; if (show) { onEnterModal(); } }, [ addDangerToast, fetchData, onChange, onDatasourceSave, onHide, selectDatasource, show, ]); const setSearchRef = (ref: any) => { searchRef = ref; }; const changeSearch = ( event: React.FormEvent, ) => { const searchValue = (event.currentTarget?.value as string) ?? ''; setFilter(searchValue); }; const handleChangeConfirm = () => { SupersetClient.get({ endpoint: `/datasource/get/${confirmedDataset?.type}/${confirmedDataset?.id}`, }) .then(({ json }) => { onDatasourceSave(json); onChange(`${confirmedDataset?.id}__table`); }) .catch(response => { getClientErrorObject(response).then( ({ error, message }: { error: any; message: string }) => { const errorMessage = error ? error.error || error.statusText || error : message; addDangerToast(errorMessage); }, ); }); onHide(); addSuccessToast('Successfully changed dataset!'); }; const handlerCancelConfirm = () => { setConfirmChange(false); }; const renderTableView = () => { const data = resourceCollection.map((ds: any) => ({ rawName: ds.table_name, connection: ds.database.database_name, schema: ds.schema, name: ( selectDatasource({ type: 'table', ...ds })} > {ds.table_name} ), type: ds.kind, })); return data; }; return ( {confirmChange && (
)} } > <> {!confirmChange && ( <> {t('Warning!')} {CHANGE_WARNING_MSG} } />
{ setSearchRef(ref); }} type="text" bsSize="sm" value={filter} placeholder={t('Search / Filter')} onChange={changeSearch} />
{loading && } {!loading && ( )} )} {confirmChange && <>{CONFIRM_WARNING_MESSAGE}}
); }; export default withToasts(ChangeDatasourceModal);