/** * 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 } from 'react'; import Alert from 'src/components/Alert'; import Button from 'src/components/Button'; import { styled, t, SupersetClient } from '@superset-ui/core'; import Modal from 'src/components/Modal'; import AsyncEsmComponent from 'src/components/AsyncEsmComponent'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import { getClientErrorObject } from 'src/utils/getClientErrorObject'; import withToasts from 'src/components/MessageToasts/withToasts'; const DatasourceEditor = AsyncEsmComponent(() => import('./DatasourceEditor')); const StyledDatasourceModal = styled(Modal)` .modal-content { height: 900px; display: flex; flex-direction: column; align-items: stretch; } .modal-header { flex: 0 1 auto; } .modal-body { flex: 1 1 auto; overflow: auto; } .modal-footer { flex: 0 1 auto; } `; interface DatasourceModalProps { addSuccessToast: (msg: string) => void; datasource: any; onChange: () => {}; onDatasourceSave: (datasource: object, errors?: Array) => {}; onHide: () => {}; show: boolean; } function buildExtraJsonObject(item: Record) { const certification = item?.certified_by || item?.certification_details ? { certified_by: item?.certified_by, details: item?.certification_details, } : undefined; return JSON.stringify({ certification, warning_markdown: item?.warning_markdown, }); } const DatasourceModal: FunctionComponent = ({ addSuccessToast, datasource, onDatasourceSave, onHide, show, }) => { const [currentDatasource, setCurrentDatasource] = useState(datasource); const [errors, setErrors] = useState([]); const [isSaving, setIsSaving] = useState(false); const [isEditing, setIsEditing] = useState(false); const dialog = useRef(null); const [modal, contextHolder] = Modal.useModal(); const onConfirmSave = () => { // Pull out extra fields into the extra object const schema = currentDatasource.tableSelector?.schema || currentDatasource.databaseSelector?.schema || currentDatasource.schema; setIsSaving(true); SupersetClient.post({ endpoint: '/datasource/save/', postPayload: { data: { ...currentDatasource, schema, metrics: currentDatasource?.metrics?.map( (metric: Record) => ({ ...metric, extra: buildExtraJsonObject(metric), }), ), columns: currentDatasource?.columns?.map( (column: Record) => ({ ...column, extra: buildExtraJsonObject(column), }), ), type: currentDatasource.type || currentDatasource.datasource_type, owners: currentDatasource.owners.map( (o: Record) => o.value || o.id, ), }, }, }) .then(({ json }) => { addSuccessToast(t('The dataset has been saved')); onDatasourceSave({ ...json, owners: currentDatasource.owners, }); onHide(); }) .catch(response => { setIsSaving(false); getClientErrorObject(response).then(({ error }) => { modal.error({ title: t('Error'), content: error || t('An error has occurred'), okButtonProps: { danger: true, className: 'btn-danger' }, }); }); }); }; const onDatasourceChange = (data: Record, err: Array) => { setCurrentDatasource({ ...data, metrics: data?.metrics.map((metric: Record) => ({ ...metric, is_certified: metric?.certified_by || metric?.certification_details, })), }); setErrors(err); }; const renderSaveDialog = () => (
({ marginTop: theme.gridUnit * 4, marginBottom: theme.gridUnit * 4, })} type="warning" showIcon message={t(`The dataset configuration exposed here affects all the charts using this dataset. Be mindful that changing settings here may affect other charts in undesirable ways.`)} /> {t('Are you sure you want to save and apply changes?')}
); const onClickSave = () => { dialog.current = modal.confirm({ title: t('Confirm save'), content: renderSaveDialog(), onOk: onConfirmSave, icon: null, okText: t('OK'), cancelText: t('Cancel'), }); }; const showLegacyDatasourceEditor = !isFeatureEnabled( FeatureFlag.DISABLE_LEGACY_DATASOURCE_EDITOR, ); return ( {t('Edit Dataset ')} {currentDatasource.table_name} } maskClosable={!isEditing} footer={ <> {showLegacyDatasourceEditor && ( )} } responsive > {contextHolder} ); }; export default withToasts(DatasourceModal);