ydb-embedded-ui 6.10.2 → 6.10.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,7 +6,7 @@ import { Button, Card, Icon } from '@gravity-ui/uikit';
6
6
  import { ResponseError } from '../../../../components/Errors/ResponseError';
7
7
  import { ResizeableDataTable } from '../../../../components/ResizeableDataTable/ResizeableDataTable';
8
8
  import { hotKeysApi } from '../../../../store/reducers/hotKeys/hotKeys';
9
- import { schemaApi } from '../../../../store/reducers/schema/schema';
9
+ import { useGetSchemaQuery } from '../../../../store/reducers/schema/schema';
10
10
  import { cn } from '../../../../utils/cn';
11
11
  import { DEFAULT_TABLE_SETTINGS, IS_HOTKEYS_HELP_HIDDEN_KEY } from '../../../../utils/constants';
12
12
  import { useSetting } from '../../../../utils/hooks';
@@ -41,8 +41,7 @@ export function HotKeys({ path }) {
41
41
  var _a, _b;
42
42
  const { currentData: data, isFetching, error } = hotKeysApi.useGetHotKeysQuery({ path });
43
43
  const loading = isFetching && data === undefined;
44
- const { currentData: schemaData, isFetching: schemaIsFetching } = schemaApi.endpoints.getSchema.useQueryState({ path });
45
- const schemaLoading = schemaIsFetching && schemaData === undefined;
44
+ const { data: schemaData, isLoading: schemaLoading } = useGetSchemaQuery({ path });
46
45
  const keyColumnsIds = (_b = (_a = schemaData === null || schemaData === void 0 ? void 0 : schemaData.PathDescription) === null || _a === void 0 ? void 0 : _a.Table) === null || _b === void 0 ? void 0 : _b.KeyColumnNames;
47
46
  const tableColumns = React.useMemo(() => {
48
47
  return getHotKeysColumns(keyColumnsIds);
@@ -4,25 +4,31 @@ import { shallowEqual } from 'react-redux';
4
4
  import { ResponseError } from '../../../../components/Errors/ResponseError';
5
5
  import { TableIndexInfo } from '../../../../components/InfoViewer/schemaInfo';
6
6
  import { Loader } from '../../../../components/Loader';
7
- import { olapApi } from '../../../../store/reducers/olapStats';
8
7
  import { overviewApi } from '../../../../store/reducers/overview/overview';
9
- import { schemaApi, selectSchemaMergedChildrenPaths } from '../../../../store/reducers/schema/schema';
8
+ import { selectSchemaMergedChildrenPaths, useGetSchemaQuery, } from '../../../../store/reducers/schema/schema';
10
9
  import { EPathType } from '../../../../types/api/schema';
11
10
  import { useAutoRefreshInterval, useTypedSelector } from '../../../../utils/hooks';
12
11
  import { ExternalDataSourceInfo } from '../../Info/ExternalDataSource/ExternalDataSource';
13
12
  import { ExternalTableInfo } from '../../Info/ExternalTable/ExternalTable';
14
13
  import { ViewInfo } from '../../Info/View/View';
15
- import { isColumnEntityType, isEntityWithMergedImplementation, isTableType, } from '../../utils/schema';
14
+ import { isEntityWithMergedImplementation } from '../../utils/schema';
16
15
  import { AsyncReplicationInfo } from './AsyncReplicationInfo';
17
16
  import { ChangefeedInfo } from './ChangefeedInfo';
18
17
  import { TableInfo } from './TableInfo';
19
18
  import { TopicInfo } from './TopicInfo';
20
19
  function Overview({ type, path }) {
21
20
  const [autoRefreshInterval] = useAutoRefreshInterval();
22
- const olapParams = isTableType(type) && isColumnEntityType(type) ? { path } : skipToken;
23
- const { currentData: olapData, isFetching: olapIsFetching } = olapApi.useGetOlapStatsQuery(olapParams, { pollingInterval: autoRefreshInterval });
24
- const olapStatsLoading = olapIsFetching && olapData === undefined;
25
- const { result: olapStats } = olapData || { result: undefined };
21
+ // FIXME: The request is too heavy, stats table may have millions of items
22
+ // Disabled until fixed
23
+ // https://github.com/ydb-platform/ydb-embedded-ui/issues/907
24
+ // https://github.com/ydb-platform/ydb-embedded-ui/issues/908
25
+ // const olapParams = isTableType(type) && isColumnEntityType(type) ? {path} : skipToken;
26
+ // const {currentData: olapData, isFetching: olapIsFetching} = olapApi.useGetOlapStatsQuery(
27
+ // olapParams,
28
+ // {pollingInterval: autoRefreshInterval},
29
+ // );
30
+ // const olapStatsLoading = olapIsFetching && olapData === undefined;
31
+ // const {result: olapStats} = olapData || {result: undefined};
26
32
  const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
27
33
  // shallowEqual prevents rerenders when new schema data is loaded
28
34
  const mergedChildrenPaths = useTypedSelector((state) => selectSchemaMergedChildrenPaths(state, path, type), shallowEqual);
@@ -38,8 +44,9 @@ function Overview({ type, path }) {
38
44
  });
39
45
  const overviewLoading = isFetching && currentData === undefined;
40
46
  const { data: rawData, additionalData } = currentData || {};
41
- const { error: schemaError } = schemaApi.endpoints.getSchema.useQueryState({ path });
42
- const entityLoading = overviewLoading || olapStatsLoading;
47
+ const { error: schemaError } = useGetSchemaQuery({ path });
48
+ // overviewLoading || olapStatsLoading
49
+ const entityLoading = overviewLoading;
43
50
  const entityNotReady = isEntityWithMergedImpl && !mergedChildrenPaths;
44
51
  const renderContent = () => {
45
52
  var _a;
@@ -65,7 +72,7 @@ function Overview({ type, path }) {
65
72
  [EPathType.EPathTypeView]: () => _jsx(ViewInfo, { data: data }),
66
73
  [EPathType.EPathTypeReplication]: () => _jsx(AsyncReplicationInfo, { data: data }),
67
74
  };
68
- return ((type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || (_jsx(TableInfo, { data: data, type: type, olapStats: olapStats })));
75
+ return ((type && ((_a = pathTypeToComponent[type]) === null || _a === void 0 ? void 0 : _a.call(pathTypeToComponent))) || (_jsx(TableInfo, { data: data, type: type })));
69
76
  };
70
77
  if (entityLoading || entityNotReady) {
71
78
  return _jsx(Loader, { size: "m" });
@@ -9,6 +9,6 @@ import './TableInfo.scss';
9
9
  const b = cn('ydb-diagnostics-table-info');
10
10
  export const TableInfo = ({ data, type, olapStats }) => {
11
11
  const title = _jsx(EntityTitle, { data: data === null || data === void 0 ? void 0 : data.PathDescription });
12
- const { generalInfo = [], tableStatsInfo = [], tabletMetricsInfo = [], partitionConfigInfo = [], } = React.useMemo(() => prepareTableInfo(data, type, olapStats), [data, type, olapStats]);
13
- return (_jsxs("div", { className: b(), children: [_jsx(InfoViewer, { info: generalInfo, title: title, className: b('info-block'), renderEmptyState: () => _jsx("div", { className: b('title'), children: title }) }), _jsxs("div", { className: b('row'), children: [_jsx("div", { className: b('col'), children: tableStatsInfo.map((info, index) => (_jsx(InfoViewer, { info: info, title: index === 0 ? i18n('tableStats') : undefined, className: b('info-block'), renderEmptyState: () => null }, index))) }), tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (_jsxs("div", { className: b('col'), children: [_jsx(InfoViewer, { info: tabletMetricsInfo, title: i18n('tabletMetrics'), className: b('info-block'), renderEmptyState: () => null }), _jsx(InfoViewer, { info: partitionConfigInfo, title: i18n('partitionConfig'), className: b('info-block'), renderEmptyState: () => null })] })) : null] })] }));
12
+ const { generalInfo, tableStatsInfo, tabletMetricsInfo = [], partitionConfigInfo = [], } = React.useMemo(() => prepareTableInfo(data, type, olapStats), [data, type, olapStats]);
13
+ return (_jsxs("div", { className: b(), children: [_jsx(InfoViewer, { info: generalInfo, title: title, className: b('info-block'), renderEmptyState: () => _jsx("div", { className: b('title'), children: title }) }), _jsxs("div", { className: b('row'), children: [tableStatsInfo ? (_jsx("div", { className: b('col'), children: tableStatsInfo.map((info, index) => (_jsx(InfoViewer, { info: info, title: index === 0 ? i18n('tableStats') : undefined, className: b('info-block'), renderEmptyState: () => null }, index))) })) : null, tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (_jsxs("div", { className: b('col'), children: [_jsx(InfoViewer, { info: tabletMetricsInfo, title: i18n('tabletMetrics'), className: b('info-block'), renderEmptyState: () => null }), _jsx(InfoViewer, { info: partitionConfigInfo, title: i18n('partitionConfig'), className: b('info-block'), renderEmptyState: () => null })] })) : null] })] }));
14
14
  };
@@ -10,7 +10,7 @@ export declare const prepareTableInfo: (data?: TEvDescribeSchemeResult, type?: E
10
10
  partitionConfigInfo?: undefined;
11
11
  } | {
12
12
  generalInfo: InfoViewerItem[];
13
- tableStatsInfo: InfoViewerItem[][];
13
+ tableStatsInfo: InfoViewerItem[][] | undefined;
14
14
  tabletMetricsInfo: InfoViewerItem[];
15
15
  partitionConfigInfo: InfoViewerItem[];
16
16
  };
@@ -120,7 +120,9 @@ export const prepareTableInfo = (data, type, olapStats) => {
120
120
  // There is no TableStats and TabletMetrics for ColumnTables inside ColumnStore
121
121
  // Therefore we parse olapStats
122
122
  if (type === EPathType.EPathTypeColumnTable && isInStoreColumnTable(ColumnTableDescription)) {
123
- tableStatsInfo = [prepareOlapStats(olapStats)];
123
+ if (olapStats) {
124
+ tableStatsInfo = [prepareOlapStats(olapStats)];
125
+ }
124
126
  }
125
127
  else {
126
128
  tableStatsInfo = [
@@ -13,7 +13,7 @@ import { LinkWithIcon } from '../../../components/LinkWithIcon/LinkWithIcon';
13
13
  import { Loader } from '../../../components/Loader';
14
14
  import SplitPane from '../../../components/SplitPane';
15
15
  import routes, { createExternalUILink, createHref } from '../../../routes';
16
- import { schemaApi, setShowPreview } from '../../../store/reducers/schema/schema';
16
+ import { setShowPreview, useGetSchemaQuery } from '../../../store/reducers/schema/schema';
17
17
  import { TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID, TENANT_SUMMARY_TABS_IDS, } from '../../../store/reducers/tenant/constants';
18
18
  import { setQueryTab, setSummaryTab, setTenantPage } from '../../../store/reducers/tenant/tenant';
19
19
  import { EPathSubType, EPathType } from '../../../types/api/schema';
@@ -48,7 +48,7 @@ export function ObjectSummary({ type, subType, tenantName, path, onCollapseSumma
48
48
  const queryParams = qs.parse(location.search, {
49
49
  ignoreQueryPrefix: true,
50
50
  });
51
- const { currentData: currentObjectData } = schemaApi.endpoints.getSchema.useQueryState({ path });
51
+ const { data: currentObjectData } = useGetSchemaQuery({ path });
52
52
  const currentSchemaData = (_a = currentObjectData === null || currentObjectData === void 0 ? void 0 : currentObjectData.PathDescription) === null || _a === void 0 ? void 0 : _a.Self;
53
53
  React.useEffect(() => {
54
54
  const isTable = isTableType(type);
@@ -255,12 +255,12 @@ export function ObjectSummary({ type, subType, tenantName, path, onCollapseSumma
255
255
  }
256
256
  function ObjectTree({ tenantName, path }) {
257
257
  var _a;
258
- const { currentData: tenantData = {}, isFetching } = schemaApi.useGetSchemaQuery({
258
+ const { data: tenantData = {}, isLoading } = useGetSchemaQuery({
259
259
  path: tenantName,
260
260
  });
261
261
  const pathData = (_a = tenantData === null || tenantData === void 0 ? void 0 : tenantData.PathDescription) === null || _a === void 0 ? void 0 : _a.Self;
262
262
  const [, setCurrentPath] = useQueryParam('schema', StringParam);
263
- if (!pathData && isFetching) {
263
+ if (!pathData && isLoading) {
264
264
  // If Loader isn't wrapped with div, SplitPane doesn't calculate panes height correctly
265
265
  return (_jsx("div", { children: _jsx(Loader, {}) }));
266
266
  }
@@ -20,13 +20,23 @@ export function SchemaTree(props) {
20
20
  const [parentPath, setParentPath] = React.useState('');
21
21
  const [schemaTreeKey, setSchemaTreeKey] = React.useState('');
22
22
  const fetchPath = async (path) => {
23
- const promise = dispatch(schemaApi.endpoints.getSchema.initiate({ path }, { forceRefetch: true }));
24
- const { data } = await promise;
25
- promise.unsubscribe();
26
- if (!data) {
23
+ let schemaData;
24
+ do {
25
+ const promise = dispatch(schemaApi.endpoints.getSchema.initiate({ path }, { forceRefetch: true }));
26
+ const { data, originalArgs } = await promise;
27
+ promise.unsubscribe();
28
+ // Check if the result from the current request is received. rtk-query may skip the current request and
29
+ // return data from a parallel request, due to the same cache key.
30
+ if ((originalArgs === null || originalArgs === void 0 ? void 0 : originalArgs.path) === path) {
31
+ schemaData = data === null || data === void 0 ? void 0 : data[path];
32
+ break;
33
+ }
34
+ // eslint-disable-next-line no-constant-condition
35
+ } while (true);
36
+ if (!schemaData) {
27
37
  throw new Error(`no describe data about path ${path}`);
28
38
  }
29
- const { PathDescription: { Children = [] } = {} } = data;
39
+ const { PathDescription: { Children = [] } = {} } = schemaData;
30
40
  const childItems = Children.map((childData) => {
31
41
  const { Name = '', PathType, PathSubType } = childData;
32
42
  return {
@@ -4,7 +4,7 @@ import DataTable from '@gravity-ui/react-data-table';
4
4
  import { skipToken } from '@reduxjs/toolkit/query';
5
5
  import { ResizeableDataTable } from '../../../../components/ResizeableDataTable/ResizeableDataTable';
6
6
  import { TableSkeleton } from '../../../../components/TableSkeleton/TableSkeleton';
7
- import { schemaApi } from '../../../../store/reducers/schema/schema';
7
+ import { useGetSchemaQuery } from '../../../../store/reducers/schema/schema';
8
8
  import { viewSchemaApi } from '../../../../store/reducers/viewSchema/viewSchema';
9
9
  import { DEFAULT_TABLE_SETTINGS } from '../../../../utils/constants';
10
10
  import { isColumnEntityType, isExternalTableType, isRowTableType, isViewType, } from '../../utils/schema';
@@ -13,10 +13,7 @@ import { prepareSchemaData, prepareViewSchema } from './prepareData';
13
13
  import { b } from './shared';
14
14
  import './SchemaViewer.scss';
15
15
  export const SchemaViewer = ({ type, path, tenantName, extended = false }) => {
16
- const { currentData: schemaData, isFetching } = schemaApi.endpoints.getSchema.useQueryState({
17
- path,
18
- });
19
- const loading = isFetching && schemaData === undefined;
16
+ const { data: schemaData, isLoading: loading } = useGetSchemaQuery({ path });
20
17
  const viewSchemaRequestParams = isViewType(type) ? { path, database: tenantName } : skipToken;
21
18
  const { data: viewColumnsData, isLoading: isViewSchemaLoading } = viewSchemaApi.useGetViewSchemaQuery(viewSchemaRequestParams);
22
19
  const tableData = React.useMemo(() => {
@@ -6,7 +6,7 @@ import { AccessDenied } from '../../components/Errors/403';
6
6
  import { Loader } from '../../components/Loader';
7
7
  import SplitPane from '../../components/SplitPane';
8
8
  import { setHeaderBreadcrumbs } from '../../store/reducers/header/header';
9
- import { schemaApi } from '../../store/reducers/schema/schema';
9
+ import { useGetSchemaQuery } from '../../store/reducers/schema/schema';
10
10
  import { cn } from '../../utils/cn';
11
11
  import { DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY } from '../../utils/constants';
12
12
  import { useTypedDispatch } from '../../utils/hooks';
@@ -47,7 +47,7 @@ export function Tenant(props) {
47
47
  dispatch(setHeaderBreadcrumbs('tenant', { tenantName }));
48
48
  }, [tenantName, dispatch]);
49
49
  const path = schema !== null && schema !== void 0 ? schema : tenantName;
50
- const { currentData: currentItem, error, isLoading, } = schemaApi.useGetSchemaQuery({ path }, { refetchOnMountOrArgChange: true });
50
+ const { data: currentItem, error, isLoading } = useGetSchemaQuery({ path });
51
51
  const { PathType: currentPathType, PathSubType: currentPathSubType } = ((_a = currentItem === null || currentItem === void 0 ? void 0 : currentItem.PathDescription) === null || _a === void 0 ? void 0 : _a.Self) || {};
52
52
  let showBlockingError = false;
53
53
  if (error && typeof error === 'object' && 'status' in error) {
@@ -21,11 +21,22 @@ export declare const schemaApi: import("@reduxjs/toolkit/query").Api<import("@re
21
21
  }, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All", unknown, "api">;
22
22
  getSchema: import("@reduxjs/toolkit/query").QueryDefinition<{
23
23
  path: string;
24
- }, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All", TEvDescribeSchemeResult & {
25
- partial?: boolean;
24
+ }, import("@reduxjs/toolkit/query").BaseQueryFn<void, typeof import("../api")._NEVER, unknown, {}>, "All", {
25
+ [path: string]: TEvDescribeSchemeResult & {
26
+ partial?: boolean;
27
+ };
26
28
  }, "api">;
27
29
  }, "api", "All", typeof import("@reduxjs/toolkit/query").coreModuleName | typeof import("@reduxjs/toolkit/query/react").reactHooksModuleName>;
28
30
  export declare const selectSchemaMergedChildrenPaths: Selector<RootState, string[] | undefined, [
29
31
  string | undefined,
30
32
  EPathType | undefined
31
33
  ]>;
34
+ export declare function useGetSchemaQuery({ path }: {
35
+ path: string;
36
+ }): {
37
+ data: (TEvDescribeSchemeResult & {
38
+ partial?: boolean;
39
+ }) | undefined;
40
+ isLoading: boolean;
41
+ error: unknown;
42
+ };
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { createSelector } from '@reduxjs/toolkit';
2
3
  import { isEntityWithMergedImplementation } from '../../../containers/Tenant/utils/schema';
3
4
  import { api } from '../api';
@@ -44,42 +45,44 @@ export const schemaApi = api.injectEndpoints({
44
45
  queryFn: async ({ path }, { signal }) => {
45
46
  try {
46
47
  const data = await window.api.getSchema({ path }, { signal });
47
- return { data: data !== null && data !== void 0 ? data : {} };
48
+ return { data: data ? { [path]: data, ...getSchemaChildren(data) } : {} };
48
49
  }
49
50
  catch (error) {
50
51
  return { error };
51
52
  }
52
53
  },
53
54
  keepUnusedDataFor: Infinity,
54
- forceRefetch: ({ endpointState }) => {
55
- const data = endpointState === null || endpointState === void 0 ? void 0 : endpointState.data;
56
- if (data && typeof data === 'object' && 'partial' in data && data.partial) {
57
- return true;
58
- }
59
- return false;
55
+ serializeQueryArgs: ({ queryArgs: { path } }) => {
56
+ const parts = path.split('/');
57
+ return { path: parts[0] || parts[1] };
60
58
  },
61
- onQueryStarted: async ({ path }, { dispatch, getState, queryFulfilled }) => {
62
- const { data } = await queryFulfilled;
59
+ merge: (existing, incoming, { arg: { path } }) => {
60
+ const { [path]: data, ...children } = incoming;
63
61
  if (data) {
64
- const state = getState();
65
- const { PathDescription: { Children = [] } = {} } = data;
66
- for (const child of Children) {
67
- const { Name = '' } = child;
68
- const childPath = `${path}/${Name}`;
69
- const cachedData = schemaApi.endpoints.getSchema.select({ path: childPath })(state).data;
70
- if (!cachedData) {
71
- // not full data, but it contains PathType, which ensures seamless switch between nodes
72
- dispatch(schemaApi.util.upsertQueryData('getSchema', { path: childPath }, { PathDescription: { Self: child }, partial: true }));
73
- }
74
- }
62
+ return {
63
+ ...children,
64
+ ...existing,
65
+ [path]: data,
66
+ };
75
67
  }
68
+ return existing;
76
69
  },
77
70
  }),
78
71
  }),
79
72
  overrideExisting: 'throw',
80
73
  });
74
+ function getSchemaChildren(data) {
75
+ const children = {};
76
+ const { PathDescription: { Children = [] } = {}, Path: path } = data;
77
+ for (const child of Children) {
78
+ const { Name = '' } = child;
79
+ const childPath = `${path}/${Name}`;
80
+ children[childPath] = { PathDescription: { Self: child }, Path: childPath, partial: true };
81
+ }
82
+ return children;
83
+ }
81
84
  const getSchemaSelector = createSelector((path) => path, (path) => schemaApi.endpoints.getSchema.select({ path }));
82
- const selectGetSchema = createSelector((state) => state, (_state, path) => getSchemaSelector(path), (state, selectTabletsInfo) => selectTabletsInfo(state).data);
85
+ const selectGetSchema = createSelector((state) => state, (_state, path) => path, (_state, path) => getSchemaSelector(path), (state, path, selectTabletsInfo) => { var _a; return (_a = selectTabletsInfo(state).data) === null || _a === void 0 ? void 0 : _a[path]; });
83
86
  const selectSchemaChildren = (state, path) => { var _a, _b; return (_b = (_a = selectGetSchema(state, path)) === null || _a === void 0 ? void 0 : _a.PathDescription) === null || _b === void 0 ? void 0 : _b.Children; };
84
87
  export const selectSchemaMergedChildrenPaths = createSelector([
85
88
  (_, path) => path,
@@ -90,3 +93,15 @@ export const selectSchemaMergedChildrenPaths = createSelector([
90
93
  ? children === null || children === void 0 ? void 0 : children.map(({ Name }) => path + '/' + Name)
91
94
  : undefined;
92
95
  });
96
+ export function useGetSchemaQuery({ path }) {
97
+ const { currentData, isFetching, error, refetch } = schemaApi.useGetSchemaQuery({ path });
98
+ const data = currentData === null || currentData === void 0 ? void 0 : currentData[path];
99
+ const isLoading = isFetching && data === undefined;
100
+ const shouldLoad = !isLoading && ((!data && !error) || (data === null || data === void 0 ? void 0 : data.partial));
101
+ React.useEffect(() => {
102
+ if (shouldLoad) {
103
+ refetch();
104
+ }
105
+ }, [refetch, path, shouldLoad]);
106
+ return { data, isLoading, error };
107
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "6.10.2",
3
+ "version": "6.10.3",
4
4
  "files": [
5
5
  "dist"
6
6
  ],