ydb-embedded-ui 6.10.2 → 6.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  ],