From 3767a947e045f88c88f7202274ced80da96f6129 Mon Sep 17 00:00:00 2001 From: Stephen Cui <229063661@qq.com> Date: Mon, 7 Mar 2022 15:10:05 +0800 Subject: [PATCH 1/4] refactor(view): change database data source binding --- .../ViewPage/Main/Properties/Resource.tsx | 109 ++++++------------ .../MainPage/pages/ViewPage/slice/index.ts | 14 ++- .../pages/ViewPage/slice/selectors.ts | 1 + .../MainPage/pages/ViewPage/slice/thunks.ts | 4 +- .../MainPage/pages/ViewPage/slice/types.ts | 2 +- .../pages/MainPage/pages/ViewPage/utils.tsx | 18 +++ 6 files changed, 68 insertions(+), 80 deletions(-) diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Resource.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Resource.tsx index 8d19cec92..ed379bdd4 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Resource.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Resource.tsx @@ -28,58 +28,71 @@ import { Col, Input, Row } from 'antd'; import { Tree } from 'app/components'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; import { useSearchAndExpand } from 'app/hooks/useSearchAndExpand'; -import { selectDataProviderDatabaseListLoading } from 'app/pages/MainPage/slice/selectors'; -import { getDataProviderDatabases } from 'app/pages/MainPage/slice/thunks'; import { DEFAULT_DEBOUNCE_WAIT } from 'globalConstants'; -import { memo, useCallback, useContext, useEffect } from 'react'; +import { memo, useCallback, useContext, useEffect, useMemo } from 'react'; import { monaco } from 'react-monaco-editor'; import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components/macro'; import { SPACE_MD, SPACE_XS } from 'styles/StyleConstants'; import { RootState } from 'types'; -import { request } from 'utils/request'; -import { errorHandle } from 'utils/utils'; import { ColumnTypes } from '../../constants'; import { EditorContext } from '../../EditorContext'; -import { useViewSlice } from '../../slice'; import { selectCurrentEditingViewAttr, - selectDatabases, + selectSourceDatabaseSchemas, } from '../../slice/selectors'; import { getEditorProvideCompletionItems } from '../../slice/thunks'; -import { Schema } from '../../slice/types'; +import { DatabaseSchema } from '../../slice/types'; +import { buildAntdTreeNodeModel } from '../../utils'; import Container from './Container'; export const Resource = memo(() => { - const { actions } = useViewSlice(); + const t = useI18NPrefix('view.resource'); const dispatch = useDispatch(); const { editorCompletionItemProviderRef } = useContext(EditorContext); const sourceId = useSelector(state => selectCurrentEditingViewAttr(state, { name: 'sourceId' }), ) as string; - const databases = useSelector(state => - selectDatabases(state, { name: sourceId }), - ); - const databaseListLoading = useSelector( - selectDataProviderDatabaseListLoading, + const databaseSchemas = useSelector(state => + selectSourceDatabaseSchemas(state, { id: sourceId }), ); - const t = useI18NPrefix('view.resource'); + + const buildTableNode = useCallback((database: DatabaseSchema) => { + const children = + database?.tables?.map(table => { + return buildTableColumnNode([database.dbName], table); + }) || []; + + return buildAntdTreeNodeModel([], database.dbName, children, false); + }, []); + + const buildTableColumnNode = (ancestors: string[] = [], table) => { + const children = + table?.columns?.map(column => { + return buildAntdTreeNodeModel( + ancestors.concat(table.tableName), + column?.name, + [], + true, + ); + }) || []; + return buildAntdTreeNodeModel(ancestors, table.tableName, children, false); + }; + + const databaseTreeModel = useMemo(() => { + return (databaseSchemas || []).map(buildTableNode); + }, [buildTableNode, databaseSchemas]); const { filteredData, onExpand, debouncedSearch, expandedRowKeys } = useSearchAndExpand( - databases, + databaseTreeModel, (keywords, data) => (data.title as string).includes(keywords), DEFAULT_DEBOUNCE_WAIT, true, ); - useEffect(() => { - if (sourceId && !databases) { - dispatch(getDataProviderDatabases(sourceId)); - } - }, [dispatch, sourceId, databases]); useEffect(() => { - if (databases && editorCompletionItemProviderRef) { + if (databaseTreeModel && editorCompletionItemProviderRef) { editorCompletionItemProviderRef.current?.dispose(); dispatch( getEditorProvideCompletionItems({ @@ -93,54 +106,7 @@ export const Resource = memo(() => { }), ); } - }, [dispatch, sourceId, databases, editorCompletionItemProviderRef]); - - const loadData = useCallback( - ({ value }) => - new Promise((resolve, reject) => { - try { - const [database, table] = value; - if (table) { - request( - `/data-provider/${sourceId}/${database}/${table}/columns`, - ).then(({ data }) => { - dispatch( - actions.addSchema({ - sourceId, - databaseName: database, - tableName: table, - schema: data, - }), - ); - resolve(); - }); - } else { - request( - `/data-provider/${sourceId}/${database}/tables`, - ).then(({ data }) => { - dispatch( - actions.addTables({ - sourceId, - databaseName: database, - tables: data.sort((a, b) => - a.toLowerCase() < b.toLowerCase() - ? -1 - : a.toLowerCase() > b.toLowerCase() - ? 1 - : 0, - ), - }), - ); - resolve(); - }); - } - } catch (error) { - errorHandle(error); - reject(); - } - }), - [dispatch, sourceId, actions], - ); + }, [dispatch, sourceId, databaseTreeModel, editorCompletionItemProviderRef]); const renderIcon = useCallback(({ value }) => { if (Array.isArray(value)) { @@ -179,8 +145,7 @@ export const Resource = memo(() => { { - if (!action.payload?.data) { + if (!action.payload?.data?.schemaItems) { return; } - if (!state.sourceDatabaseSchema) { - state.sourceDatabaseSchema = {}; - } state.sourceDatabaseSchema[action.payload?.sourceId] = - action.payload.data; + action.payload.data.schemaItems; }); + + builder.addMatcher( + isMySliceRejectedAction(slice.name), + rejectedActionMessageHandler, + ); }, }); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts index 3fbf199c1..f56a4029f 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts @@ -22,6 +22,7 @@ import { listToTree } from 'utils/utils'; import { initialState } from '.'; import { ResourceTypes } from '../../PermissionPage/constants'; import { + DatabaseSchema, SelectViewFolderTreeProps, SelectViewTreeProps, ViewViewModel, diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts index eb5425295..447829a51 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts @@ -359,10 +359,10 @@ export const getEditorProvideCompletionItems = createAsyncThunk< const variableKeywords = new Set(); if (sourceId) { - const databaseSchemas = selectSourceDatabaseSchemas(getState(), { + const currentDBSchemas = selectSourceDatabaseSchemas(getState(), { id: sourceId, }); - databaseSchemas?.schemaItems?.forEach(db => { + currentDBSchemas?.forEach(db => { dbKeywords.add(db.dbName); db.tables?.forEach(table => { tableKeywords.add(table.tableName); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts index 97c935049..0f2ffde23 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts @@ -39,7 +39,7 @@ export interface ViewState { [name: string]: TreeDataNode[]; }; sourceDatabaseSchema: { - schemaItems?: DatabaseSchema[]; + [name: string]: DatabaseSchema[]; }; saveViewLoading: boolean; unarchiveLoading: boolean; diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx index d2e95ad89..87ca238b0 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx @@ -16,6 +16,7 @@ * limitations under the License. */ +import { TreeDataNode } from 'antd'; import { FONT_WEIGHT_MEDIUM, SPACE_UNIT } from 'styles/StyleConstants'; import { Nullable } from 'types'; import { isEmptyArray } from 'utils/object'; @@ -376,3 +377,20 @@ export const diffMergeHierarchyModel = (model: HierarchyModel) => { model.hierarchy = newHierarchy; return model; }; + +export function buildAntdTreeNodeModel( + ancestors: string[] = [], + nodeName: string, + children?: T[], + isLeaf?: boolean, +): T { + const TREE_HIERARCHY_SEPERATOR = String.fromCharCode(0); + const fullNames = ancestors.concat(nodeName); + return { + key: fullNames.join(TREE_HIERARCHY_SEPERATOR), + title: nodeName, + value: fullNames, + children, + isLeaf, + } as any; +} From d554a1d0fb0961fed10672fac99e12535412e3f3 Mon Sep 17 00:00:00 2001 From: Stephen Cui <229063661@qq.com> Date: Mon, 7 Mar 2022 17:16:55 +0800 Subject: [PATCH 2/4] fix(migration): console log fommater --- frontend/src/app/migration/MigrationEvent.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/migration/MigrationEvent.ts b/frontend/src/app/migration/MigrationEvent.ts index b56e1f864..c34511285 100644 --- a/frontend/src/app/migration/MigrationEvent.ts +++ b/frontend/src/app/migration/MigrationEvent.ts @@ -20,7 +20,7 @@ import { APP_VERSION_INIT } from './constants'; import { IDomainEvent, Task } from './types'; /** - * Migrtion Event + * Migration Event * @class MigrationTaskEvent * @template TDomainModel */ @@ -48,12 +48,12 @@ class MigrationEvent } return result; } catch (error) { - // TODO(Stephen): beautiful console format - console.error( - 'Migration Event Error | Version: %s | Stack Trace: %o', + console.log( + '%c Datart Migration Error | Version: %s | Please Contract Administrator! ', + 'background: red; color: #fafafa', this.version, - error, ); + console.log('Migration Event Error | Stack Trace: %o', error); throw error; } } From b12319f557acfcbd696f61f55bbfffac13c88b42 Mon Sep 17 00:00:00 2001 From: Stephen Cui <229063661@qq.com> Date: Tue, 8 Mar 2022 13:22:02 +0800 Subject: [PATCH 3/4] fix(view): refetch database schema when change source --- .../pages/MainPage/pages/ViewPage/Main/Workbench.tsx | 12 ++++++------ .../app/pages/MainPage/pages/ViewPage/slice/index.ts | 2 +- .../pages/MainPage/pages/ViewPage/slice/thunks.ts | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Workbench.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Workbench.tsx index 2ac162a29..55fa36d41 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Workbench.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Workbench.tsx @@ -51,15 +51,15 @@ export const Workbench = memo(() => { const parentId = useSelector(state => selectCurrentEditingViewAttr(state, { name: 'parentId' }), ) as string; + const sourceId = useSelector(state => + selectCurrentEditingViewAttr(state, { name: 'sourceId' }), + ) as string; useEffect(() => { - if (Boolean(id)) { - const sourceId = views?.find(v => v.id === id)?.sourceId; - if (sourceId) { - dispatch(getSchemaBySourceId(sourceId)); - } + if (sourceId) { + dispatch(getSchemaBySourceId(sourceId)); } - }, [dispatch, id, views]); + }, [dispatch, sourceId]); const path = useMemo( () => diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/index.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/index.ts index 15088c9d4..811b60afb 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/index.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/index.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { createSlice, current, PayloadAction } from '@reduxjs/toolkit'; import { getDataProviderDatabases } from 'app/pages/MainPage/slice/thunks'; import { useInjectReducer } from 'utils/@reduxjs/injectReducer'; import { isMySliceRejectedAction } from 'utils/@reduxjs/toolkit'; diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts index 447829a51..3357e5746 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts @@ -141,7 +141,6 @@ export const getSchemaBySourceId = createAsyncThunk( if (sourceSchemas) { return; } - const { data } = await request2({ url: `/sources/schemas/${sourceId}`, method: 'GET', From 1e443c89185668eb226b2bb5cf2a21f2c382ca59 Mon Sep 17 00:00:00 2001 From: Stephen Cui <229063661@qq.com> Date: Tue, 8 Mar 2022 15:16:13 +0800 Subject: [PATCH 4/4] fix(view): add loading to resouce panel and hierarchy panel --- .../ViewPage/Main/Properties/Container.tsx | 7 +++-- .../DataModelTree/DataModelTree.tsx | 27 ++++++++++--------- .../ViewPage/Main/Properties/Resource.tsx | 4 ++- .../MainPage/pages/ViewPage/slice/index.ts | 11 +++++++- .../pages/ViewPage/slice/selectors.ts | 5 ++++ .../MainPage/pages/ViewPage/slice/types.ts | 1 + 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Container.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Container.tsx index 7a48c9086..10b0eaf6a 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Container.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Container.tsx @@ -16,6 +16,7 @@ * limitations under the License. */ +import { Skeleton } from 'antd'; import { ListTitle } from 'app/components'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; import { FC, memo } from 'react'; @@ -24,12 +25,14 @@ import { SPACE_TIMES } from 'styles/StyleConstants'; const Container: FC = memo(props => { const t = useI18NPrefix('view.properties'); - const { title, children, ...rest } = props; + const { title, children, isLoading, ...rest } = props; return ( - {children} + + {children} + ); }); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx index 69444b59b..5afb9cf13 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx @@ -32,7 +32,7 @@ import { FONT_SIZE_BASE, INFO } from 'styles/StyleConstants'; import { Nullable } from 'types'; import { CloneValueDeep, isEmpty, isEmptyArray } from 'utils/object'; import { uuidv4 } from 'utils/utils'; -import { ColumnTypes } from '../../../constants'; +import { ColumnTypes, ViewViewModelStages } from '../../../constants'; import { useViewSlice } from '../../../slice'; import { selectCurrentEditingView, @@ -63,6 +63,9 @@ const DataModelTree: FC = memo(() => { selectCurrentEditingViewAttr(state, { name: 'id' }), ) as string; const currentEditingView = useSelector(selectCurrentEditingView); + const stage = useSelector(state => + selectCurrentEditingViewAttr(state, { name: 'stage' }), + ) as ViewViewModelStages; const roles = useSelector(selectRoles); const columnPermissions = useSelector(state => selectCurrentEditingViewAttr(state, { name: 'columnPermissions' }), @@ -305,7 +308,7 @@ const DataModelTree: FC = memo(() => { }; const openMoveToHierarchyModal = (node: Column) => { - const currrentHierarchies = tableColumns?.filter( + const currentHierarchies = tableColumns?.filter( c => c.role === ColumnRole.Hierarchy && !c?.children?.find(cn => cn.name === node.name), @@ -315,11 +318,11 @@ const DataModelTree: FC = memo(() => { title: t('model.addToHierarchy'), modalSize: StateModalSize.XSMALL, onOk: hierarchyName => { - if (currrentHierarchies?.find(h => h.name === hierarchyName)) { + if (currentHierarchies?.find(h => h.name === hierarchyName)) { let newHierarchy = moveNode( tableColumns, node, - currrentHierarchies, + currentHierarchies, hierarchyName, ); handleDataModelHierarchyChange(newHierarchy); @@ -333,7 +336,7 @@ const DataModelTree: FC = memo(() => { rules={[{ required: true }]} > @@ -471,7 +474,7 @@ const DataModelTree: FC = memo(() => { const moveNode = ( columns: Column[], node: Column, - currrentHierarchies: Column[], + currentHierarchies: Column[], hierarchyName, ) => { const nodeIndex = columns?.findIndex(c => c.name === node.name); @@ -486,15 +489,15 @@ const DataModelTree: FC = memo(() => { branch.children?.filter(bc => bc.name !== node.name) || []; } } - const targetHierarchy = currrentHierarchies?.find( + const targetHierarchy = currentHierarchies?.find( h => h.name === hierarchyName, ); - const clonedhierarchy = CloneValueDeep(targetHierarchy!); - clonedhierarchy.children = (clonedhierarchy.children || []).concat([node]); + const clonedHierarchy = CloneValueDeep(targetHierarchy!); + clonedHierarchy.children = (clonedHierarchy.children || []).concat([node]); return updateNode( columns, - clonedhierarchy, - columns.findIndex(c => c.name === clonedhierarchy.name), + clonedHierarchy, + columns.findIndex(c => c.name === clonedHierarchy.name), ); }; @@ -583,7 +586,7 @@ const DataModelTree: FC = memo(() => { ); return ( - + { const t = useI18NPrefix('view.resource'); const dispatch = useDispatch(); const { editorCompletionItemProviderRef } = useContext(EditorContext); + const isDatabaseSchemaLoading = useSelector(selectDatabaseSchemaLoading); const sourceId = useSelector(state => selectCurrentEditingViewAttr(state, { name: 'sourceId' }), ) as string; @@ -145,7 +147,7 @@ export const Resource = memo(() => { { + state.databaseSchemaLoading = true; + }); + builder.addCase(getSchemaBySourceId.rejected, state => { + state.databaseSchemaLoading = false; + }); builder.addCase(getSchemaBySourceId.fulfilled, (state, action) => { + state.databaseSchemaLoading = false; if (!action.payload?.data?.schemaItems) { return; } diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts index f56a4029f..ef22e117e 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/selectors.ts @@ -114,3 +114,8 @@ export const selectArchivedListLoading = createSelector( [selectDomain], viewState => viewState.archivedListLoading, ); + +export const selectDatabaseSchemaLoading = createSelector( + [selectDomain], + viewState => viewState.databaseSchemaLoading, +); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts index 0f2ffde23..d5a41c00a 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts @@ -43,6 +43,7 @@ export interface ViewState { }; saveViewLoading: boolean; unarchiveLoading: boolean; + databaseSchemaLoading: boolean; } export type DatabaseSchema = {