diff --git a/src/shared/schema/bi/types/datasets.ts b/src/shared/schema/bi/types/datasets.ts index 78cdf3f5e7..05d9bf3b65 100644 --- a/src/shared/schema/bi/types/datasets.ts +++ b/src/shared/schema/bi/types/datasets.ts @@ -200,20 +200,20 @@ export type GetDataSetFieldsByIdArgs = WorkbookIdArg & { }; type CreateDatasetBaseArgs = { - dataset: Dataset['dataset']; + dataset: Partial; name: string; created_via?: string; }; -type CreateDirDatasetArgs = CreateDatasetBaseArgs & { +export type CreateDirDatasetArgs = CreateDatasetBaseArgs & { dir_path: string; }; -type CreateWorkbookDatsetArgs = CreateDatasetBaseArgs & { +export type CreateWorkbookDatasetArgs = CreateDatasetBaseArgs & { workbook_id: string; }; -export type CreateDatasetArgs = CreateDirDatasetArgs | CreateWorkbookDatsetArgs; +export type CreateDatasetArgs = CreateDirDatasetArgs | CreateWorkbookDatasetArgs; export type CreateDatasetResponse = Id & DatasetWithOptions; @@ -222,7 +222,7 @@ export type UpdateDatasetResponse = DatasetWithOptions; export type UpdateDatasetArgs = { version: DatasetVersion; data: { - dataset: Dataset['dataset']; + dataset: Partial; }; } & DatasetId; diff --git a/src/shared/types/dataset.ts b/src/shared/types/dataset.ts index c01f55d6de..a740dcc01c 100644 --- a/src/shared/types/dataset.ts +++ b/src/shared/types/dataset.ts @@ -265,7 +265,7 @@ export interface DatasetOptions { }[]; max: number; }; - source_listing: { + source_listing?: { supports_source_search: boolean; supports_source_pagination: boolean; supports_db_name_listing: boolean; diff --git a/src/ui/store/actions/editHistory.ts b/src/ui/store/actions/editHistory.ts index 62186ce44a..1bef91688d 100644 --- a/src/ui/store/actions/editHistory.ts +++ b/src/ui/store/actions/editHistory.ts @@ -69,7 +69,9 @@ interface ResetEditHistoryUnitAction { unitId: string; } -export function resetEditHistoryUnit({unitId}: Omit) { +export function resetEditHistoryUnit({ + unitId, +}: Omit): ResetEditHistoryUnitAction { return { type: RESET_EDIT_HISTORY_UNIT, unitId, diff --git a/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx b/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx index 33502a1fa3..19059a6bc5 100644 --- a/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx +++ b/src/ui/units/datasets/components/DatasetPanel/DatasetPanel.tsx @@ -38,7 +38,7 @@ function TabSwitch(props: TabSwitchProps) { switchTab(e.target.value)} + onUpdate={(value) => switchTab(value)} > {tabs.map(({value, label, disabled}) => ( void; + switchTab: (tab: DatasetTab) => void; refreshSources: () => void; openDialogFieldEditor: () => void; togglePreview: () => void; diff --git a/src/ui/units/datasets/components/DialogCreateDataset/DialogCreateDataset.tsx b/src/ui/units/datasets/components/DialogCreateDataset/DialogCreateDataset.tsx index 8075ac9118..8a6198df10 100644 --- a/src/ui/units/datasets/components/DialogCreateDataset/DialogCreateDataset.tsx +++ b/src/ui/units/datasets/components/DialogCreateDataset/DialogCreateDataset.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {I18n} from '../../../../../i18n'; -import type {CreateDatasetResponse} from '../../../../../shared/schema/types'; import type { DialogCreateWorkbookEntryProps, EntryDialogBaseProps, @@ -17,18 +16,19 @@ type DialogCreateDatasetBaseProps = { visible: boolean; }; -type DialogCreateDatasetInNavigationProps = { - onApply: EntryDialogBaseProps['onApply']; +export type DialogCreateDatasetInNavigationProps = { + onApply: EntryDialogBaseProps['onApply']; creationScope: 'navigation'; }; -type DialogCreateDatasetInWorkbookProps = { - onApply: DialogCreateWorkbookEntryProps['onApply']; +export type DialogCreateDatasetInWorkbookProps = { + onApply: DialogCreateWorkbookEntryProps['onApply']; creationScope: 'workbook'; }; -type DialogCreateDatasetProps = DialogCreateDatasetBaseProps & - (DialogCreateDatasetInNavigationProps | DialogCreateDatasetInWorkbookProps); +export type DialogCreateDatasetProps = + | (DialogCreateDatasetBaseProps & DialogCreateDatasetInNavigationProps) + | (DialogCreateDatasetBaseProps & DialogCreateDatasetInWorkbookProps); const DialogCreateDataset = (props: DialogCreateDatasetProps) => { const {visible, onClose} = props; diff --git a/src/ui/units/datasets/constants/index.ts b/src/ui/units/datasets/constants/index.ts index 8fe37241b3..fba9e14660 100644 --- a/src/ui/units/datasets/constants/index.ts +++ b/src/ui/units/datasets/constants/index.ts @@ -1,5 +1,5 @@ import {i18n} from 'i18n'; -import type {EntryScope} from 'shared'; +import type {EntryScope, WorkbookId} from 'shared'; import {DL} from 'ui'; import {getFakeEntry as genericGetFakeEntry} from '../../../components/ActionPanel'; @@ -50,7 +50,7 @@ export const getAppMetricGroupNameI18n = (key: string) => _getSelectItemTitle()[ export const getFakeEntry = ( scope: EntryScope.Connection | EntryScope.Dataset, - workbookId?: string, + workbookId?: WorkbookId, searchCurrentPath?: string, ) => { let path = searchCurrentPath || DL.USER_FOLDER; diff --git a/src/ui/units/datasets/containers/Dataset/Dataset.js b/src/ui/units/datasets/containers/Dataset/Dataset.tsx similarity index 82% rename from src/ui/units/datasets/containers/Dataset/Dataset.js rename to src/ui/units/datasets/containers/Dataset/Dataset.tsx index c067c0f9fa..40aa45fd90 100644 --- a/src/ui/units/datasets/containers/Dataset/Dataset.js +++ b/src/ui/units/datasets/containers/Dataset/Dataset.tsx @@ -1,15 +1,21 @@ import React from 'react'; import {dateTimeUtc} from '@gravity-ui/date-utils'; +import type {ButtonView} from '@gravity-ui/uikit'; import block from 'bem-cn-lite'; +import type {History, Location} from 'history'; import {I18n} from 'i18n'; import omit from 'lodash/omit'; -import PropTypes from 'prop-types'; +import type {HotkeysContextType} from 'react-hotkeys-hook/dist/HotkeysProvider'; +import type {ConnectedProps} from 'react-redux'; import {connect} from 'react-redux'; import SplitPane from 'react-split-pane'; -import {compose} from 'recompose'; import {createStructuredSelector} from 'reselect'; -import {ErrorCode, ErrorContentTypes} from 'shared'; +import type {DatasetSource, DatasetSourceAvatar} from 'shared'; +import {EntryScope, ErrorCode, ErrorContentTypes} from 'shared'; +import type {GetEntryResponse, GetRevisionsEntry} from 'shared/schema'; +import type {DataLensApiError, SDK} from 'ui'; +import type {DialogUnlockProps} from 'ui/components/EntryDialogues/DialogUnlock'; import {SPLIT_PANE_RESIZER_CLASSNAME, URL_QUERY} from 'ui/constants/common'; import {HOTKEYS_SCOPES} from 'ui/constants/misc'; import {withHotkeysContext} from 'ui/hoc/withHotkeysContext'; @@ -21,13 +27,10 @@ import {initEditHistoryUnit} from 'ui/store/actions/editHistory'; import { addAvatar, addSource, - changeAmountPreviewRows, closePreview, - fetchDataset, fetchFieldTypes, initialFetchDataset, initializeDataset, - openPreview, refreshSources, saveDataset, setActualDataset, @@ -50,7 +53,13 @@ import {SlugifyUrl} from '../../../../components/SlugifyUrl'; import UIUtils from '../../../../utils/utils'; import ContainerLoader from '../../components/ContainerLoader/ContainerLoader'; import DatasetPanel from '../../components/DatasetPanel/DatasetPanel'; +import type { + DialogCreateDatasetInNavigationProps, + DialogCreateDatasetInWorkbookProps, + DialogCreateDatasetProps, +} from '../../components/DialogCreateDataset/DialogCreateDataset'; import DialogCreateDataset from '../../components/DialogCreateDataset/DialogCreateDataset'; +import type {DatasetTab} from '../../constants'; import { DATASETS_EDIT_HISTORY_UNIT_ID, DATASET_DATE_AVAILABLE_FORMAT, @@ -81,11 +90,14 @@ import { isLoadingDatasetSelector, isRefetchingDatasetSelector, previewEnabledSelector, - sortedSourcePrototypesSelector, + sourcePrototypesSelector, sourceTemplateSelector, workbookIdSelector, } from '../../store/selectors'; +import type {DatasetReduxState} from '../../store/types'; +import type {DatasetEditor} from '../DatasetEditor/DatasetEditor'; import DatasetPreview from '../DatasetPreview/DatasetPreview'; +import type {DatasetSources} from '../DatasetSources/DatasetSources'; import {ActionPanelRightItems} from './ActionPanelRightItems'; @@ -95,12 +107,47 @@ const b = block('dataset'); const i18n = I18n.keyset('dataset.dataset-editor.modify'); const i18nError = I18n.keyset('component.view-error.view'); const i18nActionPanel = I18n.keyset('component.action-panel.view'); +const i18nDialogRevisions = I18n.keyset('component.dialog-revisions.view'); const RIGHT_PREVIEW_PANEL_MIN_SIZE = 500; const BOTTOM_PREVIEW_PANEL_MIN_SIZE = 48; const BOTTOM_PREVIEW_PANEL_DEFAULT_SIZE = 200; -class Dataset extends React.Component { - constructor(props) { +type StateProps = ReturnType; + +interface OwnProps { + sdk: SDK; + isAuto: boolean; + isCreationProcess: boolean; + datasetId: string; + connectionId: string; + ytPath?: string; + workbookIdFromPath?: string; + hotkeysContext?: HotkeysContextType; + location: Location; + history: History; +} + +type ReduxProps = ConnectedProps; +type Props = ReduxProps & OwnProps & StateProps; + +type State = { + isAuto: boolean; + isVisibleDialogCreateDataset: boolean; + viewId: string | null; + progress: boolean; + connectionId: string; + connectionType: string; + previewPanelSize: number | string; +}; + +class Dataset extends React.Component { + static defaultProps = { + isCreationProcess: false, + }; + _datasetEditorRef = React.createRef(); + _askAccessRightsDialogRef = React.createRef(); + + constructor(props: Props) { super(props); this.state = { @@ -110,11 +157,10 @@ class Dataset extends React.Component { progress: false, connectionId: '', connectionType: '', - propsDatasetCreationDialog: {}, previewPanelSize: BOTTOM_PREVIEW_PANEL_DEFAULT_SIZE, }; - this.props.initEditHistoryUnit({ + this.props.initEditHistoryUnit({ unitId: DATASETS_EDIT_HISTORY_UNIT_ID, setState: (args) => { this.props.setEditHistoryState(args); @@ -149,7 +195,7 @@ class Dataset extends React.Component { this.props.hotkeysContext?.enableScope(HOTKEYS_SCOPES.DATASETS); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { const { datasetId: prevDatasetId, datasetPreview: {view: prevView}, @@ -186,7 +232,7 @@ class Dataset extends React.Component { } if (prevView !== curView) { - let previewPanelSize = + let previewPanelSize: string | number = curView === VIEW_PREVIEW.RIGHT ? RIGHT_PREVIEW_PANEL_MIN_SIZE : BOTTOM_PREVIEW_PANEL_DEFAULT_SIZE; @@ -218,9 +264,6 @@ class Dataset extends React.Component { this.props.hotkeysContext?.disableScope(HOTKEYS_SCOPES.DATASETS); } - _datasetEditorRef = React.createRef(); - _askAccessRightsDialogRef = React.createRef(); - async autoCreateYTDataset() { const {ytPath, isCreationProcess, history} = this.props; const {isAuto} = this.state; @@ -231,13 +274,13 @@ class Dataset extends React.Component { ...this.props.sourceTemplate, id: sourceId, parameters: {table_name: ytPath}, - }; + } as DatasetSource; const avatar = { id: avatarId, is_root: true, title: DatasetUtils.getSourceTitle(source), source_id: sourceId, - }; + } as DatasetSourceAvatar; this.props.addSource({source}); this.props.addAvatar({avatar}); @@ -263,7 +306,7 @@ class Dataset extends React.Component { const {sourcePrototypes} = this.props; const datasetSourcesRef = this._datasetEditorRef.current; - if (datasetSourcesRef) { + if (datasetSourcesRef && 'addAvatarOnMapAutoIfNeeds' in datasetSourcesRef) { datasetSourcesRef.addAvatarOnMapAutoIfNeeds(sourcePrototypes); } }; @@ -285,17 +328,23 @@ class Dataset extends React.Component { askAccessRights = () => { const {datasetId} = this.props; - this._askAccessRightsDialogRef.current.open({ + this._askAccessRightsDialogRef.current?.open?.({ dialog: EntryDialogName.Unlock, dialogProps: { entry: { entryId: datasetId, - }, + } as DialogUnlockProps['entry'], }, }); }; - getErrorMessageByCode = ({status, data = {}}) => { + getErrorMessageByCode = ({ + status, + data = {}, + }: { + status: number | null | string; + data: {message?: string; code?: string}; + }) => { const {message, code} = data; switch (status) { case 400: @@ -321,7 +370,7 @@ class Dataset extends React.Component { text: i18nError('button_details'), handler: this.openDialogDetails, buttonProps: { - view: 'outlined', + view: 'outlined' as ButtonView, }, }, }; @@ -351,7 +400,7 @@ class Dataset extends React.Component { } }; - getEntry() { + getEntry(): GetEntryResponse & {fake?: boolean} { const { isCreationProcess, datasetId, @@ -367,7 +416,7 @@ class Dataset extends React.Component { const searchParams = new URLSearchParams(location.search); const searchCurrentPath = searchParams.get(URL_QUERY.CURRENT_PATH); - return getFakeEntry('dataset', workbookId, searchCurrentPath); + return getFakeEntry(EntryScope.Dataset, workbookId, searchCurrentPath!); } return { @@ -379,17 +428,17 @@ class Dataset extends React.Component { key: datasetKey, scope: 'dataset', permissions: datasetPermissions, - }; + } as GetEntryResponse; } - switchTab = (currentTab) => { + switchTab = (currentTab: DatasetTab) => { this.props.setCurrentTab({currentTab}); }; openDialogFieldEditor = () => { const datasetEditorRef = this._datasetEditorRef.current; - if (datasetEditorRef) { + if (datasetEditorRef && 'openDialogFieldEditor' in datasetEditorRef) { datasetEditorRef.openDialogFieldEditor(); } }; @@ -404,21 +453,12 @@ class Dataset extends React.Component { this.props.closePreview(); }; - /** - * @param {string} key - * @returns {Promise} - */ - createDatasetInNavigation = (key) => { + createDatasetInNavigation: DialogCreateDatasetInNavigationProps['onApply'] = (key) => { const {isCreationProcess, history} = this.props; return this.props.saveDataset({key, history, isCreationProcess, isErrorThrows: true}); }; - /** - * @param {object} data - * @param {string} data.name - * @returns {Promise} - */ - createDatasetInWorkbook = ({name}) => { + createDatasetInWorkbook: DialogCreateDatasetInWorkbookProps['onApply'] = ({name}) => { const {isCreationProcess, history} = this.props; return this.props.saveDataset({ name, @@ -442,7 +482,7 @@ class Dataset extends React.Component { }; getRightItems = () => { - const {isCreationProcess} = this.props; + const {isCreationProcess, history} = this.props; return ( { - const {datasetId, history} = this.props; + const {history} = this.props; this.props.openDialogSaveDraftInstanceAsActualConfirm({ onApply: () => { - this.props.setActualDataset({history, datasetId}); + this.props.setActualDataset({history}); }, }); }; @@ -479,7 +519,7 @@ class Dataset extends React.Component { const {sdk, datasetError} = this.props; const {status, requestId, traceId, message, code} = UIUtils.parseErrorResponse(datasetError); - const {type, title, description, action} = this.getErrorMessageByCode({ + const {type, title, action} = this.getErrorMessageByCode({ status, data: {message, code}, }); @@ -489,7 +529,6 @@ class Dataset extends React.Component { { + const getRevisionRowExtendedProps = (item: GetRevisionsEntry) => { const updatedTime = dateTimeUtc({input: item.updatedAt}); const disabled = (updatedTime?.isValid() && updatedTime?.isBefore(DATASET_DISABLED_DATE)) ?? false; - const disabledText = disabled ? i18n('label_status-tooltip-disable') : ''; + const disabledText = disabled + ? i18nDialogRevisions('label_status-tooltip-disable') + : ''; return { disabled, disabledText, @@ -595,18 +638,18 @@ class Dataset extends React.Component { const {isVisibleDialogCreateDataset} = this.state; const workbookId = this.getWorkbookId(); + const dialogProps = { + creationScope: workbookId ? 'workbook' : 'navigation', + visible: isVisibleDialogCreateDataset, + onApply: workbookId ? this.createDatasetInWorkbook : this.createDatasetInNavigation, + onClose: this.closeDialogCreateDataset, + } as DialogCreateDatasetProps; + return (
{this.renderControls()} {isRefetchingDataset ? this.renderLoader() : this.renderPanels()} - +
); } @@ -661,56 +704,6 @@ class Dataset extends React.Component { } } -Dataset.propTypes = { - ui: PropTypes.object.isRequired, - sdk: PropTypes.object.isRequired, - isCreationProcess: PropTypes.bool.isRequired, - isLoading: PropTypes.bool.isRequired, - isFavorite: PropTypes.bool.isRequired, - isAuto: PropTypes.bool.isRequired, - previewEnabled: PropTypes.bool.isRequired, - isDatasetRevisionMismatch: PropTypes.bool.isRequired, - fetchFieldTypes: PropTypes.func.isRequired, - initializeDataset: PropTypes.func.isRequired, - initialFetchDataset: PropTypes.func.isRequired, - fetchDataset: PropTypes.func.isRequired, - openPreview: PropTypes.func.isRequired, - closePreview: PropTypes.func.isRequired, - togglePreview: PropTypes.func.isRequired, - updateDatasetByValidation: PropTypes.func.isRequired, - saveDataset: PropTypes.func.isRequired, - changeAmountPreviewRows: PropTypes.func.isRequired, - refreshSources: PropTypes.func.isRequired, - openDialogErrorWithTabs: PropTypes.func.isRequired, - initEditHistoryUnit: PropTypes.func.isRequired, - currentTab: PropTypes.string.isRequired, - datasetId: PropTypes.string, - connectionId: PropTypes.string, - datasetKey: PropTypes.string, - ytPath: PropTypes.string, - datasetError: PropTypes.object, - validationError: PropTypes.object, - savingError: PropTypes.object, - selectedConnection: PropTypes.object, - sourceTemplate: PropTypes.object, - sourcePrototypes: PropTypes.array, - datasetPreview: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - location: PropTypes.object.isRequired, - datasetPermissions: PropTypes.object, - workbookId: PropTypes.string, - workbookIdFromPath: PropTypes.string, - isRefetchingDataset: PropTypes.bool.isRequired, - publishedId: PropTypes.string, - currentRevId: PropTypes.string, - openDialogSaveDraftInstanceAsActualConfirm: PropTypes.func, - setActualDataset: PropTypes.func, -}; - -Dataset.defaultProps = { - isCreationProcess: false, -}; - const mapStateToProps = createStructuredSelector({ datasetKey: datasetKeySelector, datasetPermissions: datasetPermissionsSelector, @@ -726,7 +719,7 @@ const mapStateToProps = createStructuredSelector({ isFavorite: isFavoriteDatasetSelector, isDatasetRevisionMismatch: isDatasetRevisionMismatchSelector, previewEnabled: previewEnabledSelector, - sourcePrototypes: sortedSourcePrototypesSelector, + sourcePrototypes: sourcePrototypesSelector, sourceTemplate: sourceTemplateSelector, ui: UISelector, workbookId: workbookIdSelector, @@ -736,13 +729,10 @@ const mapDispatchToProps = { fetchFieldTypes, initializeDataset, initialFetchDataset, - fetchDataset, saveDataset, - openPreview, closePreview, togglePreview, updateDatasetByValidation, - changeAmountPreviewRows, refreshSources, addSource, addAvatar, @@ -753,5 +743,6 @@ const mapDispatchToProps = { setCurrentTab, setActualDataset, }; +const connector = connect(mapStateToProps, mapDispatchToProps); -export default compose(connect(mapStateToProps, mapDispatchToProps))(withHotkeysContext(Dataset)); +export default withHotkeysContext(connector(Dataset)); diff --git a/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js b/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js index d71fefbefe..0d5c777937 100644 --- a/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js +++ b/src/ui/units/datasets/containers/DatasetEditor/DatasetEditor.js @@ -53,7 +53,7 @@ import './DatasetEditor.scss'; const b = block('dataset-editor'); -class DatasetEditor extends React.Component { +export class DatasetEditor extends React.Component { state = { isFieldEditorVisible: false, isShowHiddenFieldsAlreadyClicked: false, diff --git a/src/ui/units/datasets/containers/DatasetSources/DatasetSources.js b/src/ui/units/datasets/containers/DatasetSources/DatasetSources.js index fc446c212c..dc596b1e42 100644 --- a/src/ui/units/datasets/containers/DatasetSources/DatasetSources.js +++ b/src/ui/units/datasets/containers/DatasetSources/DatasetSources.js @@ -72,7 +72,7 @@ const b = block('dataset-sources'); const SOURCES_PANEL_MIN_SIZE = 256; const SOURCES_PANEL_MAX_SIZE = 512; -class DatasetSources extends React.Component { +export class DatasetSources extends React.Component { state = { connectionId: null, dragSource: null, diff --git a/src/ui/units/datasets/containers/DatasetTabViewer/DatasetTabViewer.tsx b/src/ui/units/datasets/containers/DatasetTabViewer/DatasetTabViewer.tsx index f88c89400a..da7a6e6c1a 100644 --- a/src/ui/units/datasets/containers/DatasetTabViewer/DatasetTabViewer.tsx +++ b/src/ui/units/datasets/containers/DatasetTabViewer/DatasetTabViewer.tsx @@ -14,7 +14,7 @@ type Props = { sdk: SDK; datasetId?: string; forwardedRef?: React.ForwardedRef; - workbookId?: string; + workbookId?: string | null; }; function DatasetTabViewer(props: Props) { diff --git a/src/ui/units/datasets/helpers/dataset-error-helpers.ts b/src/ui/units/datasets/helpers/dataset-error-helpers.ts index c0e1cf0bfd..319c90bc1b 100644 --- a/src/ui/units/datasets/helpers/dataset-error-helpers.ts +++ b/src/ui/units/datasets/helpers/dataset-error-helpers.ts @@ -37,16 +37,18 @@ function getMessageText() { }; } +export type ActionTypeNotification = + | 'create' + | 'duplicate' + | 'remove' + | 'save' + | 'preview' + | 'validate' + | 'types'; + export function getToastTitle( type: 'NOTIFICATION_SUCCESS' | 'NOTIFICATION_FAILURE', - actionTypeNotification: - | 'create' - | 'duplicate' - | 'remove' - | 'save' - | 'preview' - | 'validate' - | 'types', + actionTypeNotification: ActionTypeNotification, ) { switch (actionTypeNotification) { case 'create': { diff --git a/src/ui/units/datasets/helpers/datasets.ts b/src/ui/units/datasets/helpers/datasets.ts index be458f5355..8b2178d75c 100644 --- a/src/ui/units/datasets/helpers/datasets.ts +++ b/src/ui/units/datasets/helpers/datasets.ts @@ -23,8 +23,8 @@ const getCurrentPath = () => { const shapeYTPath = () => getCurrentPath() || `${DL.USER_FOLDER}${YT_FOLDER_NAME}`; -export const getAutoCreatedYTDatasetKey = (ytPath: string) => { - const entryName = ytPath.split('/').pop()?.replace(DISALLOWED_SYMBOLS, ''); +export const getAutoCreatedYTDatasetKey = (ytPath?: string) => { + const entryName = ytPath?.split('/').pop()?.replace(DISALLOWED_SYMBOLS, ''); const postfix = _random(1000, 9999); return `${shapeYTPath()}/${entryName}_${postfix}`; diff --git a/src/ui/units/datasets/store/actions/creators/dataset.js b/src/ui/units/datasets/store/actions/creators/dataset.js deleted file mode 100644 index 57ca7e193e..0000000000 --- a/src/ui/units/datasets/store/actions/creators/dataset.js +++ /dev/null @@ -1,535 +0,0 @@ -import {toaster} from '@gravity-ui/uikit/toaster-singleton'; -import {i18n} from 'i18n'; -import {Feature} from 'shared'; -import {URL_QUERY} from 'ui'; -import {resetEditHistoryUnit} from 'ui/store/actions/editHistory'; -import {loadRevisions, setEntryContent} from 'ui/store/actions/entryContent'; -import {RevisionsMode} from 'ui/store/typings/entryContent'; - -import logger from '../../../../../libs/logger'; -import {getSdk} from '../../../../../libs/schematic-sdk'; -import {ComponentErrorType, DATASETS_EDIT_HISTORY_UNIT_ID} from '../../../constants'; -import {getToastTitle} from '../../../helpers/dataset-error-helpers'; -import {getComponentErrorsByType} from '../../../helpers/datasets'; -import DatasetUtils, {getSourceListingValues} from '../../../helpers/utils'; -import {workbookIdSelector} from '../../selectors'; -import * as DATASET_ACTION_TYPES from '../types/dataset'; - -import { - addEditHistoryPointDs, - clearDatasetPreview, - clearToasters, - closePreview, - fetchPreviewDataset, - getDbNames, - getSources, - queuePreviewToOpen, - setValidationData, - setValidationState, - toggleSaveDataset, - validateDataset, -} from './datasetTyped'; - -function checkFetchingPreview({updatePreview, updates}) { - return ( - !updatePreview && - updates && - updates.length && - !updates.every((update) => { - const {field: {description, title} = {}} = update; - - if (description || title) { - return true; - } - - return false; - }) - ); -} - -/** - * @param {Object} args - * @param {string|undefined} [args.actionTypeNotification] - * @param {boolean|undefined} [args.updatePreview] - * @param {boolean|undefined} [args.validateEnabled] - * @returns {ThunkAction} - **/ -export function updateDatasetByValidation({ - actionTypeNotification, - compareContent = false, - updatePreview = false, - validateEnabled = true, -} = {}) { - return async (dispatch, getState) => { - let fetchingPreviewEnabled, updates; - - clearToasters(toaster); - dispatch(setValidationState({validation: {isPending: false}})); - - if (validateEnabled) { - updates = await dispatch(validateDataset({compareContent})); - fetchingPreviewEnabled = checkFetchingPreview({updatePreview, updates}); - } - - const { - dataset: { - id: datasetId, - content: {result_schema: resultSchema, component_errors: componentErrors}, - preview: {previewEnabled, amountPreviewRows} = {}, - } = {}, - } = getState(); - - const sourceErrors = getComponentErrorsByType( - componentErrors, - ComponentErrorType.DataSource, - ); - const fieldErrors = getComponentErrorsByType(componentErrors, ComponentErrorType.Field); - - if (previewEnabled && (fetchingPreviewEnabled || updatePreview)) { - if (fieldErrors.length) { - dispatch(clearDatasetPreview()); - } else { - const workbookId = workbookIdSelector(getState()); - - dispatch( - fetchPreviewDataset({ - datasetId, - workbookId, - resultSchema, - limit: amountPreviewRows, - }), - ); - } - } - - if (actionTypeNotification) { - toaster.add({ - name: 'success_update_dataset', - title: getToastTitle('NOTIFICATION_SUCCESS', actionTypeNotification), - theme: 'success', - }); - } - - return Promise.resolve({ - updates, - sourceErrors, - }); - }; -} - -function setInitialSources(ids) { - return async (dispatch) => { - try { - let initialConnections = []; - - if (ids.length) { - const result = await Promise.allSettled( - ids.map((id) => - getSdk().sdk.us.getEntry({ - entryId: id, - includePermissionsInfo: true, - }), - ), - ); - const entries = result - .filter(({status}) => status === 'fulfilled') - .map(({value}) => value); - - initialConnections = ids.map((id) => { - const connectionEntry = entries.find((entry) => entry.entryId === id); - - if (connectionEntry) { - return connectionEntry; - } - - return { - entryId: id, - key: i18n('dataset.sources-tab.modify', 'label_deleted-connection'), - deleted: true, - data: {}, - // TODO[1]: Uncomment when there will be an icon [YCDESIGN-719] - // type: 'deleted' - }; - }); - - dispatch({ - type: DATASET_ACTION_TYPES.SET_INITIAL_SOURCES, - payload: { - selectedConnections: initialConnections, - selectedConnection: initialConnections[0], - }, - }); - } - } catch (e) { - logger.logError('dataset: setInitialSources failed', e); - console.error(`setInitialSources action failed: ${e}`); - } - }; -} - -export function initializeDataset({connectionId}) { - return async (dispatch) => { - if (connectionId) { - await dispatch(setInitialSources([connectionId])); - } - - dispatch(_getSources()); - - dispatch({ - type: DATASET_ACTION_TYPES.INITIALIZE_DATASET, - payload: {}, - }); - }; -} - -export function initialFetchDataset({datasetId, rev_id, isInitialFetch = true}) { - return async (dispatch, getState) => { - try { - if (isInitialFetch) { - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_REQUEST, - payload: {}, - }); - } else { - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_FETCH_REQUEST, - payload: {}, - }); - } - - const meta = await getSdk().sdk.us.getEntryMeta({entryId: datasetId}); - const workbookId = meta.workbookId ?? null; - - const dataset = await getSdk().sdk.bi.getDatasetByVersion({ - datasetId, - workbookId, - rev_id, - version: 'draft', - }); - - if (!DatasetUtils.isEnabledFeature(Feature.EnableDatasetSourcesPagination)) { - dataset.options.source_listing = undefined; - } - - const { - dataset: {sources = []}, - options: { - preview: {enabled: previewEnabled}, - source_listing, - }, - } = dataset; - const {dbNameRequiredForSearch, supportsDbNameListing} = - getSourceListingValues(source_listing); - - const connectionsIds = new Set( - sources - .filter(DatasetUtils.filterVirtual) - .map(({connection_id: connectionId}) => connectionId), - ); - const ids = Array.from(connectionsIds); - - await dispatch(setInitialSources(ids)); - - const publishedId = meta.publishedId ?? null; - const currentRevId = rev_id ?? publishedId; - - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_SUCCESS, - payload: { - dataset, - publishedId, - currentRevId, - }, - }); - - if (dbNameRequiredForSearch || supportsDbNameListing) { - await dispatch(getDbNames(ids)); - } - - dispatch(_getSources()); - - dispatch(validateDataset({initial: true})); - - const { - dataset: { - content: { - result_schema: resultSchema, - load_preview_by_default: loadPreviewByDefault, - }, - preview: {amountPreviewRows} = {}, - } = {}, - } = getState(); - - if (previewEnabled) { - if (loadPreviewByDefault) { - dispatch( - fetchPreviewDataset({ - datasetId, - workbookId, - resultSchema, - limit: amountPreviewRows, - }), - ); - } else { - dispatch(closePreview()); - dispatch(queuePreviewToOpen(true)); - } - } - } catch (error) { - logger.logError('dataset: initialFetchDataset failed', error); - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_FAILURE, - payload: { - error, - }, - }); - } - }; -} - -export function fetchDataset({datasetId}) { - return async (dispatch, getState) => { - try { - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_FETCH_REQUEST, - payload: {}, - }); - - const workbookId = workbookIdSelector(getState()); - - const dataset = await getSdk().sdk.bi.getDatasetByVersion({ - datasetId, - workbookId, - version: 'draft', - }); - - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_FETCH_SUCCESS, - payload: { - dataset, - }, - }); - } catch (error) { - logger.logError('dataset: fetchDataset failed', error); - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_FETCH_FAILURE, - payload: { - error, - }, - }); - } - }; -} - -// There is no key field in the workbooks when creating -export function saveDataset({ - key, - workbookId, - name, - history, - isCreationProcess, - isAuto = false, - isErrorThrows = false, -}) { - return async (dispatch, getState) => { - try { - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_SAVE_REQUEST, - payload: {}, - }); - - const {entryContent, dataset: {id, content: dataset} = {}} = getState(); - let datasetId = id; - - if (isCreationProcess) { - const creationData = { - dataset, - ...(isAuto && {created_via: 'yt_to_dl'}), - }; - - if (workbookId) { - creationData.workbook_id = workbookId; - creationData.name = name; - } else { - const dividedKey = DatasetUtils.divideKey(key); - const nameFromKey = dividedKey.pop(); - creationData.dir_path = `${dividedKey.join('/')}/`; - creationData.name = nameFromKey; - } - - const {id: createdDatasetId} = await getSdk().sdk.bi.createDataset(creationData); - - datasetId = createdDatasetId; - } else { - const validation = await getSdk().sdk.bi.updateDataset({ - datasetId, - version: 'draft', - data: { - dataset, - }, - }); - - dispatch(setValidationData(validation)); - } - - if (!isCreationProcess) { - const meta = await getSdk().sdk.us.getEntryMeta({entryId: datasetId}); - const publishedId = meta.publishedId ?? null; - const entryId = meta.entryId; - - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_SAVE_SUCCESS, - payload: { - publishedId, - }, - }); - - dispatch( - setEntryContent({ - publishedId, - revId: publishedId, - }), - ); - - if (entryContent.revisionsMode === RevisionsMode.Opened) { - await dispatch( - loadRevisions({ - entryId, - page: 0, - }), - ); - } - } else { - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_SAVE_SUCCESS, - payload: {}, - }); - } - - if (!isAuto) { - toaster.add({ - name: 'success_save_dataset', - title: getToastTitle('NOTIFICATION_SUCCESS', 'save'), - theme: 'success', - }); - } - - dispatch(toggleSaveDataset({enable: false})); - - if (isAuto) { - history.replace(`/datasets/${datasetId}`); - DatasetUtils.openCreationWidgetPage({datasetId, target: '_self'}); - } else if (isCreationProcess) { - history.push(`/datasets/${datasetId}`); - } - - dispatch(resetEditHistoryUnit({unitId: DATASETS_EDIT_HISTORY_UNIT_ID})); - // Set initial history point, this is necessary so that the first change after saving can be reversed - dispatch(addEditHistoryPointDs()); - } catch (error) { - logger.logError('dataset: saveDataset failed', error); - dispatch({ - type: DATASET_ACTION_TYPES.DATASET_SAVE_FAILURE, - payload: { - error, - }, - }); - - if (isErrorThrows) { - throw error; - } - } - }; -} - -export function setActualDataset({history}) { - return async (dispatch) => { - await dispatch(saveDataset({history})); - - const searchParams = new URLSearchParams(location.search); - searchParams.delete(URL_QUERY.REV_ID); - history.push({ - ...location, - search: `?${searchParams.toString()}`, - }); - }; -} - -export function fetchFieldTypes() { - return async (dispatch) => { - let types; - - try { - const response = await getSdk().sdk.bi.getFieldTypes(); - - types = response.types - .map((type) => { - const {name, aggregations} = type; - - return { - ...type, - title: i18n('component.field-editor.view', `value_${name}`), - aggregations: aggregations.sort((current, next) => { - if (next === 'none') { - return 1; - } else { - return DatasetUtils.sortStrings(current, next); - } - }), - }; - }) - .sort(DatasetUtils.sortObjectBy('title')); - - dispatch({ - type: DATASET_ACTION_TYPES.FIELD_TYPES_FETCH_SUCCESS, - payload: { - types, - }, - }); - - return types; - } catch (error) { - logger.logError('dataset: fetchFieldTypes failed', error); - } - - toaster.add({ - name: 'error_fetch_types', - title: getToastTitle('NOTIFICATION_FAILURE', 'types'), - theme: 'danger', - }); - - return types; - }; -} - -function _getSources() { - return (dispatch, getState) => { - const { - dataset: { - sourcesPagination, - selectedConnections, - currentDbName, - options: {source_listing}, - ui: {selectedConnectionId}, - }, - } = getState(); - const {serverPagination, dbNameRequiredForSearch} = getSourceListingValues(source_listing); - const workbookId = workbookIdSelector(getState()); - - const selectedConnection = selectedConnections.find( - ({entryId}) => entryId === selectedConnectionId, - ); - - if (selectedConnection && !selectedConnection.deleted) { - const {entryId} = selectedConnection; - dispatch( - getSources({ - connectionId: entryId, - workbookId, - limit: serverPagination ? sourcesPagination.limit : undefined, - currentDbName: dbNameRequiredForSearch ? currentDbName : undefined, - }), - ); - } - }; -} diff --git a/src/ui/units/datasets/store/actions/creators/datasetTyped.ts b/src/ui/units/datasets/store/actions/creators/datasetTyped.ts index 918ff34722..610b518595 100644 --- a/src/ui/units/datasets/store/actions/creators/datasetTyped.ts +++ b/src/ui/units/datasets/store/actions/creators/datasetTyped.ts @@ -1,10 +1,13 @@ import type {Toaster} from '@gravity-ui/uikit'; +import {toaster} from '@gravity-ui/uikit/toaster-singleton'; +import type {History} from 'history'; +import {i18n} from 'i18n'; import type {DatalensGlobalState} from 'index'; +import {URL_QUERY} from 'index'; import _debounce from 'lodash/debounce'; import get from 'lodash/get'; import {batch} from 'react-redux'; import type {Dispatch} from 'redux'; -import type {ThunkDispatch} from 'redux-thunk'; import type { Dataset, DatasetAvatarRelation, @@ -13,31 +16,43 @@ import type { DatasetSourceAvatar, WorkbookId, } from 'shared'; -import {TIMEOUT_100_SEC, TIMEOUT_65_SEC} from 'shared'; +import {Feature, TIMEOUT_100_SEC, TIMEOUT_65_SEC} from 'shared'; import type { BaseSource, + CreateDatasetArgs, + CreateDirDatasetArgs, + CreateWorkbookDatasetArgs, + GetEntryResponse, GetPreviewResponse, GetSourceResponse, ValidateDatasetResponse, } from 'shared/schema'; import {sdk} from 'ui'; import {BI_ERRORS} from 'ui/constants'; +import type {AppDispatch} from 'ui/store'; import {addEditHistoryPoint, resetEditHistoryUnit} from 'ui/store/actions/editHistory'; +import {loadRevisions, setEntryContent} from 'ui/store/actions/entryContent'; import {EDIT_HISTORY_ACTION} from 'ui/store/constants/editHistory'; import type {EditHistoryUnit} from 'ui/store/reducers/editHistory'; -import Utils from 'ui/utils'; +import type {EntryGlobalState} from 'ui/store/typings/entryContent'; +import {RevisionsMode} from 'ui/store/typings/entryContent'; +import Utils, {getFilteredObject} from 'ui/utils'; import type {ApplyData} from '../../../../../components/DialogFilter/DialogFilter'; import logger from '../../../../../libs/logger'; import {getSdk} from '../../../../../libs/schematic-sdk'; -import {getFilteredObject} from '../../../../../utils'; import { + ComponentErrorType, DATASETS_EDIT_HISTORY_UNIT_ID, TAB_DATASET, TAB_FILTERS, TAB_SOURCES, TOASTERS_NAMES, } from '../../../constants'; +import type {ActionTypeNotification} from '../../../helpers/dataset-error-helpers'; +import {getToastTitle} from '../../../helpers/dataset-error-helpers'; +import {getComponentErrorsByType} from '../../../helpers/datasets'; +import DatasetUtils, {getSourceListingValues} from '../../../helpers/utils'; import {EDIT_HISTORY_OPTIONS_KEY, initialState} from '../../constants'; import { datasetContentSelector, @@ -69,10 +84,9 @@ import type { } from '../../types'; import * as DATASET_ACTION_TYPES from '../types/dataset'; -import {updateDatasetByValidation} from './dataset'; -import {filterSources, isContendChanged, prepareUpdates} from './utils'; +import {checkFetchingPreview, filterSources, isContendChanged, prepareUpdates} from './utils'; -export type DatasetDispatch = ThunkDispatch; +export type DatasetDispatch = AppDispatch; export type GetState = () => DatalensGlobalState; type ValidateDatasetArgs = { @@ -1165,7 +1179,7 @@ export function updateSetting( value: boolean, editHistoryOptions?: EditHistoryOptions, ) { - return (dispatch: Dispatch) => { + return (dispatch: DatasetDispatch) => { const update: UpdateSetting = { action: 'update_setting', setting: {name, value}, @@ -1462,3 +1476,479 @@ export function getDbNames(connectionIds: string[]) { } }; } + +interface SaveDatasetProps { + key?: string; + workbookId?: string | null; + name?: string; + history: History; + isCreationProcess?: boolean; + isAuto?: boolean; + isErrorThrows?: boolean; +} + +export function saveDataset({ + key, + workbookId, + name, + history, + isCreationProcess, + isAuto = false, + isErrorThrows = false, +}: SaveDatasetProps) { + return async (dispatch: DatasetDispatch, getState: GetState) => { + try { + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_SAVE_REQUEST, + payload: {}, + }); + + const {entryContent, dataset: {id, content: dataset} = {}} = getState(); + let datasetId = id; + + if (isCreationProcess) { + const creationData: Partial = { + dataset: dataset, + ...(isAuto && {created_via: 'yt_to_dl'}), + }; + + if (workbookId) { + (creationData as CreateWorkbookDatasetArgs).workbook_id = workbookId; + creationData.name = name; + } else { + const dividedKey = DatasetUtils.divideKey(key); + const nameFromKey = dividedKey.pop(); + (creationData as CreateDirDatasetArgs).dir_path = `${dividedKey.join('/')}/`; + creationData.name = nameFromKey; + } + + const {id: createdDatasetId} = await getSdk().sdk.bi.createDataset( + creationData as CreateDatasetArgs, + ); + + datasetId = createdDatasetId; + } else { + const validation = await getSdk().sdk.bi.updateDataset({ + datasetId: datasetId!, + version: 'draft', + data: { + dataset: dataset!, + }, + }); + + dispatch(setValidationData(validation as unknown as ValidateDatasetResponse)); + } + + if (!isCreationProcess) { + const meta = await getSdk().sdk.us.getEntryMeta({entryId: datasetId!}); + const publishedId = meta.publishedId ?? null; + const entryId = meta.entryId; + + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_SAVE_SUCCESS, + payload: { + publishedId, + }, + }); + + dispatch( + setEntryContent({ + publishedId, + revId: publishedId, + } as EntryGlobalState), + ); + + if (entryContent.revisionsMode === RevisionsMode.Opened) { + await dispatch( + loadRevisions({ + entryId, + page: 0, + }), + ); + } + } else { + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_SAVE_SUCCESS, + payload: {}, + }); + } + + if (!isAuto) { + toaster.add({ + name: 'success_save_dataset', + title: getToastTitle('NOTIFICATION_SUCCESS', 'save'), + theme: 'success', + }); + } + + dispatch(toggleSaveDataset({enable: false})); + + if (isAuto) { + history.replace(`/datasets/${datasetId}`); + DatasetUtils.openCreationWidgetPage({datasetId: datasetId!, target: '_self'}); + } else if (isCreationProcess) { + history.push(`/datasets/${datasetId}`); + } + + dispatch(resetEditHistoryUnit({unitId: DATASETS_EDIT_HISTORY_UNIT_ID})); + // Set initial history point, this is necessary so that the first change after saving can be reversed + dispatch(addEditHistoryPointDs()); + } catch (error) { + logger.logError('dataset: saveDataset failed', error); + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_SAVE_FAILURE, + payload: { + error, + }, + }); + + if (isErrorThrows) { + throw error; + } + } + }; +} + +export function setActualDataset({history}: {history: History}) { + return async (dispatch: DatasetDispatch) => { + await dispatch(saveDataset({history})); + + const searchParams = new URLSearchParams(location.search); + searchParams.delete(URL_QUERY.REV_ID); + history.push({ + ...location, + search: `?${searchParams.toString()}`, + }); + }; +} + +export function fetchFieldTypes() { + return async (dispatch: DatasetDispatch) => { + let types: DatasetReduxState['types']['data'] | undefined; + + try { + const response = await getSdk().sdk.bi.getFieldTypes(); + + types = response.types + .map((type) => { + const {name, aggregations} = type; + const key = `value_${name}`; + + return { + ...type, + title: i18n('component.field-editor.view', key), + aggregations: aggregations.sort((current, next) => { + if (next === 'none') { + return 1; + } else { + return DatasetUtils.sortStrings(current, next); + } + }), + }; + }) + .sort(DatasetUtils.sortObjectBy('title')); + + dispatch({ + type: DATASET_ACTION_TYPES.FIELD_TYPES_FETCH_SUCCESS, + payload: { + types, + }, + }); + + return types; + } catch (error) { + logger.logError('dataset: fetchFieldTypes failed', error); + } + + toaster.add({ + name: 'error_fetch_types', + title: getToastTitle('NOTIFICATION_FAILURE', 'types'), + theme: 'danger', + }); + + return types; + }; +} + +interface UpdateDatasetByValidationProps { + actionTypeNotification?: ActionTypeNotification; + compareContent?: boolean; + updatePreview?: boolean; + validateEnabled?: boolean; +} + +export function updateDatasetByValidation({ + actionTypeNotification, + compareContent = false, + updatePreview = false, + validateEnabled = true, +}: UpdateDatasetByValidationProps = {}) { + return async (dispatch: DatasetDispatch, getState: GetState) => { + let fetchingPreviewEnabled, updates; + + clearToasters(toaster); + dispatch(setValidationState({validation: {isPending: false}})); + + if (validateEnabled) { + updates = await dispatch(validateDataset({compareContent})); + fetchingPreviewEnabled = checkFetchingPreview({updatePreview, updates}); + } + + const { + dataset: { + id: datasetId, + content: {result_schema: resultSchema, component_errors: componentErrors} = {}, + preview: {previewEnabled, amountPreviewRows} = {}, + } = {}, + } = getState(); + + const sourceErrors = getComponentErrorsByType( + componentErrors!, + ComponentErrorType.DataSource, + ); + const fieldErrors = getComponentErrorsByType(componentErrors!, ComponentErrorType.Field); + + if (previewEnabled && (fetchingPreviewEnabled || updatePreview)) { + if (fieldErrors.length) { + dispatch(clearDatasetPreview()); + } else { + const workbookId = workbookIdSelector(getState()); + + dispatch( + fetchPreviewDataset({ + datasetId: datasetId!, + workbookId, + resultSchema: resultSchema!, + limit: amountPreviewRows!, + }), + ); + } + } + + if (actionTypeNotification) { + toaster.add({ + name: 'success_update_dataset', + title: getToastTitle('NOTIFICATION_SUCCESS', actionTypeNotification), + theme: 'success', + }); + } + + return Promise.resolve({ + updates, + sourceErrors, + }); + }; +} + +function setInitialSources(ids: string[]) { + return async (dispatch: DatasetDispatch) => { + try { + let initialConnections = []; + + if (ids.length) { + const result = await Promise.allSettled( + ids.map((id) => + getSdk().sdk.us.getEntry({ + entryId: id, + includePermissionsInfo: true, + }), + ), + ); + const entries = result + .filter( + (promise): promise is PromiseFulfilledResult => + promise.status === 'fulfilled', + ) + .map(({value}) => value); + + initialConnections = ids.map((id) => { + const connectionEntry = entries.find((entry) => entry.entryId === id); + + if (connectionEntry) { + return connectionEntry; + } + + return { + entryId: id, + key: i18n('dataset.sources-tab.modify', 'label_deleted-connection'), + deleted: true, + data: {}, + // TODO[1]: Uncomment when there will be an icon [YCDESIGN-719] + // type: 'deleted' + }; + }); + + dispatch({ + type: DATASET_ACTION_TYPES.SET_INITIAL_SOURCES, + payload: { + selectedConnections: initialConnections as ConnectionEntry[], + selectedConnection: initialConnections[0] as ConnectionEntry, + }, + }); + } + } catch (e) { + logger.logError('dataset: setInitialSources failed', e); + console.error(`setInitialSources action failed: ${e}`); + } + }; +} + +export function initializeDataset({connectionId}: {connectionId: string}) { + return async (dispatch: DatasetDispatch) => { + if (connectionId) { + await dispatch(setInitialSources([connectionId])); + } + + dispatch(_getSources()); + + dispatch({ + type: DATASET_ACTION_TYPES.INITIALIZE_DATASET, + payload: {}, + }); + }; +} + +export function initialFetchDataset({ + datasetId, + rev_id, + isInitialFetch = true, +}: { + datasetId: string; + rev_id?: string; + isInitialFetch?: boolean; +}) { + return async (dispatch: DatasetDispatch, getState: GetState) => { + try { + if (isInitialFetch) { + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_REQUEST, + payload: {}, + }); + } else { + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_FETCH_REQUEST, + payload: {}, + }); + } + + const meta = await getSdk().sdk.us.getEntryMeta({entryId: datasetId}); + const workbookId = meta.workbookId ?? null; + + const dataset = await getSdk().sdk.bi.getDatasetByVersion({ + datasetId, + workbookId, + rev_id, + version: 'draft', + }); + + if (!DatasetUtils.isEnabledFeature(Feature.EnableDatasetSourcesPagination)) { + dataset.options.source_listing = undefined; + } + + const { + dataset: {sources = []}, + options: { + preview: {enabled: previewEnabled}, + source_listing, + }, + } = dataset; + const {dbNameRequiredForSearch, supportsDbNameListing} = + getSourceListingValues(source_listing); + + const connectionsIds = new Set( + sources + .filter(DatasetUtils.filterVirtual) + .map(({connection_id: connectionId}) => connectionId), + ); + const ids = Array.from(connectionsIds); + + await dispatch(setInitialSources(ids)); + + const publishedId = meta.publishedId ?? null; + const currentRevId = rev_id ?? publishedId; + + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_SUCCESS, + payload: { + dataset: dataset as Dataset, + publishedId, + currentRevId, + }, + }); + + if (dbNameRequiredForSearch || supportsDbNameListing) { + await dispatch(getDbNames(ids)); + } + + dispatch(_getSources()); + + dispatch(validateDataset({initial: true})); + + const { + dataset: { + content: { + result_schema: resultSchema, + load_preview_by_default: loadPreviewByDefault, + } = {}, + preview: {amountPreviewRows} = {}, + } = {}, + } = getState(); + + if (previewEnabled) { + if (loadPreviewByDefault) { + dispatch( + fetchPreviewDataset({ + datasetId, + workbookId, + resultSchema: resultSchema!, + limit: amountPreviewRows!, + }), + ); + } else { + dispatch(closePreview()); + dispatch(queuePreviewToOpen(true)); + } + } + } catch (error) { + logger.logError('dataset: initialFetchDataset failed', error); + dispatch({ + type: DATASET_ACTION_TYPES.DATASET_INITIAL_FETCH_FAILURE, + payload: { + error, + }, + }); + } + }; +} + +function _getSources() { + return (dispatch: DatasetDispatch, getState: GetState) => { + const { + dataset: { + sourcesPagination, + selectedConnections, + currentDbName, + options: {source_listing}, + ui: {selectedConnectionId}, + }, + } = getState(); + const {serverPagination, dbNameRequiredForSearch} = getSourceListingValues(source_listing); + const workbookId = workbookIdSelector(getState()); + + const selectedConnection = selectedConnections.find( + ({entryId}) => entryId === selectedConnectionId, + ); + + if (selectedConnection && !selectedConnection.deleted) { + const {entryId} = selectedConnection; + dispatch( + getSources({ + connectionId: entryId, + workbookId: workbookId!, + limit: serverPagination ? sourcesPagination.limit : undefined, + currentDbName: dbNameRequiredForSearch ? currentDbName : undefined, + }), + ); + } + }; +} diff --git a/src/ui/units/datasets/store/actions/creators/index.ts b/src/ui/units/datasets/store/actions/creators/index.ts index 4ecc0a7be0..81f2432451 100644 --- a/src/ui/units/datasets/store/actions/creators/index.ts +++ b/src/ui/units/datasets/store/actions/creators/index.ts @@ -1,3 +1,2 @@ -export * from './dataset'; export * from './datasetTyped'; export * from './dialogs'; diff --git a/src/ui/units/datasets/store/actions/creators/utils.ts b/src/ui/units/datasets/store/actions/creators/utils.ts index 789a0ca03b..c616652726 100644 --- a/src/ui/units/datasets/store/actions/creators/utils.ts +++ b/src/ui/units/datasets/store/actions/creators/utils.ts @@ -39,3 +39,28 @@ export function filterSources(sources: GetSourceResponse['sources']) { ({source_type: sourceType}) => !SUBSELECT_SOURCE_TYPES.includes(sourceType), ); } + +export function checkFetchingPreview({ + updatePreview, + updates, +}: { + updatePreview: boolean; + updates?: Update[]; +}) { + return ( + !updatePreview && + updates && + updates.length && + !updates.every((update) => { + if ('field' in update) { + const {field: {description, title} = {}} = update; + + if (description || title) { + return true; + } + } + + return false; + }) + ); +} diff --git a/src/ui/units/datasets/store/types/dataset.ts b/src/ui/units/datasets/store/types/dataset.ts index 30d2e52202..6f7cb83ebd 100644 --- a/src/ui/units/datasets/store/types/dataset.ts +++ b/src/ui/units/datasets/store/types/dataset.ts @@ -1,4 +1,5 @@ import type {ApplyData} from 'components/DialogFilter/DialogFilter'; +import type {EditHistoryAction} from 'ui/store/actions/editHistory'; import type { ConnectionData, @@ -16,6 +17,7 @@ import type { FormOptions as SchemaFormOptions, ValidateDatasetResponse, } from '../../../../../shared/schema'; +import type {EntryContentAction} from '../../../../store/actions/entryContent'; import type {DatasetTab} from '../../constants'; import type { ADD_AVATAR_PROTOTYPES, @@ -108,6 +110,7 @@ export type ConnectionEntry = { type: string; permissions?: Permissions; workbookId: WorkbookId; + deleted?: boolean; }; export type TranslatedItem = { @@ -297,8 +300,8 @@ export type DatasetReduxState = { error: DatasetError; }; types: { - // TODO: the same type is in the scheme, it is necessary to sleep properly data: { + title: string; name: string; aggregations: string[]; }[]; @@ -684,7 +687,7 @@ type DatasetInitialFetchSuccess = { type: typeof DATASET_INITIAL_FETCH_SUCCESS; payload: { dataset: Dataset & { - connection: ConnectionEntry | null; + connection?: ConnectionEntry | null; }; publishedId: EntryFieldPublishedId; currentRevId: string | null; @@ -720,12 +723,10 @@ type FieldTypesFetchSuccess = { type: typeof FIELD_TYPES_FETCH_SUCCESS; payload: { types: { - data: { - name: string; - aggregations: string[]; - }[]; - error: DatasetError; - }; + title: string; + name: string; + aggregations: string[]; + }[]; }; }; @@ -908,4 +909,6 @@ export type DatasetReduxAction = | SetSourcesPagination | SourcesNextPageRequest | SourcesNextPageSuccess - | SetSourcesSearchLoading; + | SetSourcesSearchLoading + | EntryContentAction + | EditHistoryAction;