ydb-embedded-ui 1.9.0 → 1.10.2

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +10 -7
  3. package/dist/components/InfoViewer/InfoViewer.scss +1 -2
  4. package/dist/components/InfoViewer/utils.ts +18 -10
  5. package/dist/containers/Storage/Pdisk/Pdisk.tsx +25 -33
  6. package/dist/containers/Storage/Vdisk/Vdisk.js +2 -0
  7. package/dist/containers/Tablet/Tablet.js +2 -2
  8. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +15 -14
  9. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +24 -14
  10. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  11. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +20 -13
  12. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +80 -10
  13. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -2
  14. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +164 -42
  15. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.scss +18 -0
  16. package/dist/containers/Tenant/utils/schema.ts +73 -28
  17. package/dist/containers/Tenant/utils/schemaActions.ts +45 -32
  18. package/dist/services/api.js +13 -9
  19. package/dist/store/reducers/executeQuery.js +4 -3
  20. package/dist/store/reducers/executeTopQueries.js +1 -1
  21. package/dist/store/reducers/olapStats.js +5 -1
  22. package/dist/store/reducers/preview.js +1 -1
  23. package/dist/store/reducers/settings.js +20 -13
  24. package/dist/store/reducers/shardsWorkload.js +32 -4
  25. package/dist/types/api/schema.ts +123 -4
  26. package/dist/types/api/storage.ts +1 -1
  27. package/dist/utils/constants.js +4 -0
  28. package/dist/utils/index.js +7 -3
  29. package/dist/utils/pdisk.ts +2 -2
  30. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.10.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.1...v1.10.2) (2022-08-17)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * convert bytes on decimal scale ([db9b0a7](https://github.com/ydb-platform/ydb-embedded-ui/commit/db9b0a71fc5334f5a40992cc6abc0688782ad5d2))
9
+ * display HDD instead of ROT as pdisk type ([bd9e5ba](https://github.com/ydb-platform/ydb-embedded-ui/commit/bd9e5ba4e594cb3a1f6a964f619f9824e083ae7c))
10
+ * **InfoViewer:** accept default value formatter ([e03d8cc](https://github.com/ydb-platform/ydb-embedded-ui/commit/e03d8cc5de76e4ac00b05586ae6f6522a9708fb0))
11
+ * **InfoViewer:** allow longer labels ([89060a3](https://github.com/ydb-platform/ydb-embedded-ui/commit/89060a381858b5beaa3c3cf3402c13c917705676))
12
+ * **Overview:** display table r/o replicas ([6dbe0b4](https://github.com/ydb-platform/ydb-embedded-ui/commit/6dbe0b45fc5e3867f9d6141d270c15508a693e35))
13
+ * **Overview:** format & group table info in overview ([1a35cfc](https://github.com/ydb-platform/ydb-embedded-ui/commit/1a35cfcd2075454c4a1f1fc4961a4b3106b6d225))
14
+ * **QueryEditor:** save chosen run action ([b0fb436](https://github.com/ydb-platform/ydb-embedded-ui/commit/b0fb43651e0c6d1dc5d6a25f92716703402b556d))
15
+ * use current i18n lang for numeral formatting ([5d58fcf](https://github.com/ydb-platform/ydb-embedded-ui/commit/5d58fcffde21924f3cbe6c28946c7a9f755a8490))
16
+
17
+ ## [1.10.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.0...v1.10.1) (2022-08-10)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **Tenant:** fix actions set for topics ([0c75bf4](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c75bf4561966dd663ab1cd7c7b81ef6b4632e50))
23
+
24
+ ## [1.10.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.9.0...v1.10.0) (2022-08-10)
25
+
26
+
27
+ ### Features
28
+
29
+ * **TopShards:** add DataSize column ([cbcd047](https://github.com/ydb-platform/ydb-embedded-ui/commit/cbcd047d277f699a67bc002a5542f3b9f6a0c942))
30
+ * **TopShards:** sort table data on backend ([dc28c5c](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc28c5c75b0036480bf804d49f82fc54eac98c8e))
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * add concurrentId for sendQuery request ([dc6b32a](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc6b32a8fd51064ddeca2fc60a0f08a725216334))
36
+ * **Storage:** display pdisk type in tooltip ([2b03a35](https://github.com/ydb-platform/ydb-embedded-ui/commit/2b03a35fc11ddeae3bdd30a0690b324ae917f5c3))
37
+ * **Tablet:** change Kill to Restart ([dd585b1](https://github.com/ydb-platform/ydb-embedded-ui/commit/dd585b1d1a6a5ddb484a702523773b169900f582))
38
+ * **Tenant:** add missing schema node types ([62a0ecb](https://github.com/ydb-platform/ydb-embedded-ui/commit/62a0ecb848dbcee53e18535cbf7c03a731d0cfeb))
39
+ * **Tenant:** ensure correct behavior for new schema node types ([f80c381](https://github.com/ydb-platform/ydb-embedded-ui/commit/f80c38152656e8bbbe51ec38b29fc0d954c361cc))
40
+ * **Tenant:** use new schema icons ([389a921](https://github.com/ydb-platform/ydb-embedded-ui/commit/389a9214c64b1adb183fa0c6caa6f2ec536dbef3))
41
+ * **TopShards:** disable virtualization for table ([006d3d9](https://github.com/ydb-platform/ydb-embedded-ui/commit/006d3d9fb9a4744b8bb4ad03e53693199213f80e))
42
+ * **TopShards:** format DataSize value ([c51ce66](https://github.com/ydb-platform/ydb-embedded-ui/commit/c51ce66286f6454f7252d1194628ee5a50aafba2))
43
+ * **TopShards:** only allow DESC sort ([6aa326f](https://github.com/ydb-platform/ydb-embedded-ui/commit/6aa326fc4b8165f00f8b3ecf5becdb0943ed57af))
44
+ * **TopShards:** substring tenant name out of shards path ([9e57672](https://github.com/ydb-platform/ydb-embedded-ui/commit/9e5767222c7dac7734c68abd08067cea507b1e15))
45
+
3
46
  ## [1.9.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.8.8...v1.9.0) (2022-07-29)
4
47
 
5
48
 
@@ -10,13 +10,16 @@ const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
10
10
  ]);
11
11
 
12
12
  const formatItem = createInfoFormatter<TIndexDescription>({
13
- Type: (value) => value?.substring(10), // trims EIndexType prefix
14
- State: (value) => value?.substring(11), // trims EIndexState prefix
15
- KeyColumnNames: (value) => value?.join(', '),
16
- DataColumnNames: (value) => value?.join(', '),
17
- }, {
18
- KeyColumnNames: 'Columns',
19
- DataColumnNames: 'Includes',
13
+ values: {
14
+ Type: (value) => value?.substring(10), // trims EIndexType prefix
15
+ State: (value) => value?.substring(11), // trims EIndexState prefix
16
+ KeyColumnNames: (value) => value?.join(', '),
17
+ DataColumnNames: (value) => value?.join(', '),
18
+ },
19
+ labels: {
20
+ KeyColumnNames: 'Columns',
21
+ DataColumnNames: 'Includes',
22
+ },
20
23
  });
21
24
 
22
25
  interface IndexInfoViewerProps {
@@ -27,11 +27,10 @@
27
27
 
28
28
  &__label {
29
29
  display: flex;
30
- flex: 1 1 auto;
30
+ flex: 0 1 auto;
31
31
  align-items: baseline;
32
32
 
33
33
  min-width: 200px;
34
- max-width: 200px;
35
34
 
36
35
  white-space: nowrap;
37
36
 
@@ -2,7 +2,7 @@ type LabelMap<T> = {
2
2
  [label in keyof T]?: string;
3
3
  }
4
4
 
5
- type FieldMappers<T> = {
5
+ type ValueFormatters<T> = {
6
6
  [label in keyof T]?: (value: T[label]) => string | undefined;
7
7
  }
8
8
 
@@ -13,20 +13,28 @@ function formatLabel<Shape>(label: keyof Shape, map: LabelMap<Shape>) {
13
13
  function formatValue<Shape, Key extends keyof Shape>(
14
14
  label: Key,
15
15
  value: Shape[Key],
16
- mappers: FieldMappers<Shape>,
16
+ formatters: ValueFormatters<Shape>,
17
+ defaultFormatter?: (value: Shape[Key]) => string | undefined,
17
18
  ) {
18
- const mapper = mappers[label];
19
- const mappedValue = mapper ? mapper(value) : value;
19
+ const formatter = formatters[label] || defaultFormatter;
20
+ const formattedValue = formatter ? formatter(value) : value;
20
21
 
21
- return String(mappedValue ?? '');
22
+ return String(formattedValue ?? '');
22
23
  }
23
24
 
24
- export function createInfoFormatter<Shape extends Record<string, any>>(
25
- fieldMappers?: FieldMappers<Shape>,
26
- labelMap?: LabelMap<Shape>,
27
- ) {
25
+ interface CreateInfoFormatterOptions<Shape> {
26
+ values?: ValueFormatters<Shape>,
27
+ labels?: LabelMap<Shape>,
28
+ defaultValueFormatter?: (value: Shape[keyof Shape]) => string | undefined,
29
+ }
30
+
31
+ export function createInfoFormatter<Shape extends Record<string, any>>({
32
+ values: valueFormatters,
33
+ labels: labelMap,
34
+ defaultValueFormatter,
35
+ }: CreateInfoFormatterOptions<Shape>) {
28
36
  return <Key extends keyof Shape>(label: Key, value: Shape[Key]) => ({
29
37
  label: formatLabel(label, labelMap || {}),
30
- value: formatValue(label, value, fieldMappers || {}),
38
+ value: formatValue(label, value, valueFormatters || {}, defaultValueFormatter),
31
39
  });
32
40
  }
@@ -8,6 +8,8 @@ import {bytesToGB} from '../../../utils/utils';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  //@ts-ignore
10
10
  import {getPDiskId} from '../../../utils';
11
+ import {getPDiskType} from '../../../utils/pdisk';
12
+ import {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
11
13
  import DiskStateProgressBar, {
12
14
  diskProgressColors,
13
15
  } from '../DiskStateProgressBar/DiskStateProgressBar';
@@ -20,38 +22,29 @@ import './Pdisk.scss';
20
22
  const b = cn('pdisk-storage');
21
23
 
22
24
  const stateSeverity = {
23
- Initial: 0,
24
- Normal: 1,
25
- InitialFormatRead: 3,
26
- InitialSysLogRead: 3,
27
- InitialCommonLogRead: 3,
28
- InitialFormatReadError: 5,
29
- InitialSysLogReadError: 5,
30
- InitialSysLogParseError: 5,
31
- InitialCommonLogReadError: 5,
32
- InitialCommonLogParseError: 5,
33
- CommonLoggerInitError: 5,
34
- OpenFileError: 5,
35
- ChunkQuotaError: 5,
36
- DeviceIoError: 5,
25
+ [TPDiskState.Initial]: 0,
26
+ [TPDiskState.Normal]: 1,
27
+ [TPDiskState.InitialFormatRead]: 3,
28
+ [TPDiskState.InitialSysLogRead]: 3,
29
+ [TPDiskState.InitialCommonLogRead]: 3,
30
+ [TPDiskState.InitialFormatReadError]: 5,
31
+ [TPDiskState.InitialSysLogReadError]: 5,
32
+ [TPDiskState.InitialSysLogParseError]: 5,
33
+ [TPDiskState.InitialCommonLogReadError]: 5,
34
+ [TPDiskState.InitialCommonLogParseError]: 5,
35
+ [TPDiskState.CommonLoggerInitError]: 5,
36
+ [TPDiskState.OpenFileError]: 5,
37
+ [TPDiskState.ChunkQuotaError]: 5,
38
+ [TPDiskState.DeviceIoError]: 5,
37
39
  };
38
40
 
39
- type PDiskState = keyof typeof stateSeverity;
40
-
41
- interface PDiskProps {
42
- NodeId: number;
43
- Host?: string;
44
- Path?: string;
45
- Realtime?: string;
46
- Device?: string;
47
- AvailableSize?: string;
48
- TotalSize?: string;
49
- State?: PDiskState;
50
- PDiskId: number;
51
- }
41
+ type PDiskProps = TPDiskStateInfo;
42
+
43
+ const isSeverityKey = (key?: TPDiskState): key is keyof typeof stateSeverity =>
44
+ key !== undefined && key in stateSeverity;
52
45
 
53
- const getStateSeverity = (pDiskState?: PDiskState) => {
54
- return pDiskState ? stateSeverity[pDiskState] : NOT_AVAILABLE_SEVERITY;
46
+ const getStateSeverity = (pDiskState?: TPDiskState) => {
47
+ return isSeverityKey(pDiskState) ? stateSeverity[pDiskState] : NOT_AVAILABLE_SEVERITY;
55
48
  };
56
49
 
57
50
  function Pdisk(props: PDiskProps) {
@@ -76,8 +69,7 @@ function Pdisk(props: PDiskProps) {
76
69
  };
77
70
  /* eslint-disable */
78
71
  const preparePdiskData = () => {
79
- const {AvailableSize, TotalSize, State, PDiskId, NodeId, Host, Path, Realtime, Device} =
80
- props;
72
+ const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Device} = props;
81
73
  const errorColors = [
82
74
  diskProgressColors[colorSeverity.Orange as keyof typeof diskProgressColors],
83
75
  diskProgressColors[colorSeverity.Red as keyof typeof diskProgressColors],
@@ -89,9 +81,9 @@ function Pdisk(props: PDiskProps) {
89
81
  ];
90
82
 
91
83
  pdiskData.push({property: 'State', value: State || 'not available'});
84
+ pdiskData.push({property: 'Type', value: getPDiskType(props) || 'unknown'});
92
85
  NodeId && pdiskData.push({property: 'Node Id', value: NodeId});
93
86
 
94
- Host && pdiskData.push({property: 'Host', value: Host});
95
87
  Path && pdiskData.push({property: 'Path', value: Path});
96
88
  pdiskData.push({
97
89
  property: 'Available',
@@ -151,7 +143,7 @@ function Pdisk(props: PDiskProps) {
151
143
  href={createHref(
152
144
  routes.node,
153
145
  {id: props.NodeId, activeTab: STRUCTURE},
154
- {pdiskId: props.PDiskId},
146
+ {pdiskId: props.PDiskId || ''},
155
147
  )}
156
148
  />
157
149
  </div>
@@ -7,6 +7,7 @@ import {Popup} from '@yandex-cloud/uikit';
7
7
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  import {stringifyVdiskId, getPDiskId} from '../../../utils';
10
+ import {getPDiskType} from '../../../utils/pdisk';
10
11
  import DiskStateProgressBar, {
11
12
  diskProgressColors,
12
13
  } from '../DiskStateProgressBar/DiskStateProgressBar';
@@ -169,6 +170,7 @@ function Vdisk(props) {
169
170
  property: 'State',
170
171
  value: PDisk.State || 'not available',
171
172
  });
173
+ pdiskData.push({property: 'Type', value: getPDiskType(PDisk) || 'unknown'});
172
174
  PDisk.NodeId && pdiskData.push({property: 'Node Id', value: PDisk.NodeId});
173
175
  PDisk.NodeId &&
174
176
  nodes[PDisk.NodeId] &&
@@ -199,7 +199,7 @@ class Tablet extends React.Component {
199
199
  return (
200
200
  <CriticalActionDialog
201
201
  visible={dialogVisible}
202
- text="The tablet will be killed. Do you want to proceed?"
202
+ text="The tablet will be restarted. Do you want to proceed?"
203
203
  onClose={this.hideDialog}
204
204
  onConfirm={this._onKillClick}
205
205
  />
@@ -363,7 +363,7 @@ class Tablet extends React.Component {
363
363
  disabled={this.isDisabledKill()}
364
364
  className={b('control')}
365
365
  >
366
- Kill
366
+ Restart
367
367
  </Button>
368
368
  {this.hasHiveId() ? (
369
369
  <React.Fragment>
@@ -60,20 +60,21 @@ function DetailedOverview(props: DetailedOverviewProps) {
60
60
  const isTenant = tenantName === currentSchemaPath;
61
61
  return (
62
62
  <div className={b()}>
63
- <div className={b('section')}>
64
- {!isTenant && (
65
- <Overview type={type} tenantName={tenantName} />
66
- )}
67
- {isTenant && <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo}/>}
68
- </div>
69
- {isTenant && (
70
- <div className={b('section')}>
71
- <Healthcheck
72
- tenant={tenantName}
73
- preview={true}
74
- showMoreHandler={openModalHandler}
75
- />
76
- </div>
63
+ {isTenant ? (
64
+ <>
65
+ <div className={b('section')}>
66
+ <TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
67
+ </div>
68
+ <div className={b('section')}>
69
+ <Healthcheck
70
+ tenant={tenantName}
71
+ preview={true}
72
+ showMoreHandler={openModalHandler}
73
+ />
74
+ </div>
75
+ </>
76
+ ) : (
77
+ <Overview type={type} tenantName={tenantName} />
77
78
  )}
78
79
  </div>
79
80
  );
@@ -13,6 +13,11 @@ export enum GeneralPagesIds {
13
13
  'graph' = 'graph',
14
14
  }
15
15
 
16
+ type Page = {
17
+ id: GeneralPagesIds,
18
+ title: string,
19
+ };
20
+
16
21
  const overview = {
17
22
  id: GeneralPagesIds.overview,
18
23
  title: 'Overview',
@@ -76,17 +81,22 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
76
81
 
77
82
  export const DIR_PAGES = [overview, topShards, describe];
78
83
 
79
- export const getPagesByType = (type?: EPathType) => {
80
- switch (type) {
81
- case EPathType.EPathTypeColumnStore:
82
- case EPathType.EPathTypeSubDomain:
83
- return DATABASE_PAGES;
84
- case EPathType.EPathTypeColumnTable:
85
- case EPathType.EPathTypeTable:
86
- return TABLE_PAGES;
87
- case EPathType.EPathTypeDir:
88
- case EPathType.EPathTypeTableIndex:
89
- default:
90
- return DIR_PAGES;
91
- }
92
- }
84
+ // verbose mapping to guarantee correct tabs for new path types
85
+ // TS will error when a new type is added but not mapped here
86
+ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
87
+ [EPathType.EPathTypeInvalid]: undefined,
88
+
89
+ [EPathType.EPathTypeSubDomain]: DATABASE_PAGES,
90
+ [EPathType.EPathTypeExtSubDomain]: DATABASE_PAGES,
91
+ [EPathType.EPathTypeColumnStore]: DATABASE_PAGES,
92
+
93
+ [EPathType.EPathTypeTable]: TABLE_PAGES,
94
+ [EPathType.EPathTypeColumnTable]: TABLE_PAGES,
95
+
96
+ [EPathType.EPathTypeDir]: DIR_PAGES,
97
+ [EPathType.EPathTypeTableIndex]: DIR_PAGES,
98
+ [EPathType.EPathTypeCdcStream]: DIR_PAGES,
99
+ };
100
+
101
+ export const getPagesByType = (type?: EPathType) =>
102
+ (type && pathTypeToPages[type]) || DIR_PAGES;
@@ -8,9 +8,10 @@ import Icon from '../../../../components/Icon/Icon';
8
8
 
9
9
  import {AutoFetcher} from '../../../../utils/autofetcher';
10
10
  import {getHotKeys, setHotKeysOptions} from '../../../../store/reducers/hotKeys';
11
- import {EPathType} from '../../../../types/api/schema';
12
11
  import {prepareQueryError} from '../../../../utils';
13
12
 
13
+ import {isColumnEntityType, isTableType} from '../../utils/schema';
14
+
14
15
  import './HotKeys.scss';
15
16
 
16
17
  const b = cn('hot-keys');
@@ -42,8 +43,7 @@ function HotKeys({
42
43
  type,
43
44
  }) {
44
45
  const fetchData = () => {
45
- // ColumnTables excluded intentionally
46
- if (type === EPathType.EPathTypeTable) {
46
+ if (isTableType(type) && !isColumnEntityType(type)) {
47
47
  getHotKeys(currentSchemaPath);
48
48
  }
49
49
  };
@@ -1,4 +1,4 @@
1
- import {useEffect, useMemo} from 'react';
1
+ import {ReactNode, useEffect, useMemo} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
@@ -8,8 +8,8 @@ import {Loader} from '@yandex-cloud/uikit';
8
8
  import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
9
  import {IndexInfoViewer} from '../../../../components/IndexInfoViewer/IndexInfoViewer';
10
10
 
11
- import type {EPathType} from '../../../../types/api/schema';
12
- import {isColumnEntityType, isTableType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
11
+ import {EPathType} from '../../../../types/api/schema';
12
+ import {isColumnEntityType, isTableType} from '../../utils/schema';
13
13
  import {AutoFetcher} from '../../../../utils/autofetcher';
14
14
  //@ts-ignore
15
15
  import {getSchema} from '../../../../store/reducers/schema';
@@ -114,16 +114,23 @@ function Overview(props: OverviewProps) {
114
114
  };
115
115
 
116
116
  const renderContent = () => {
117
- switch (mapPathTypeToNavigationTreeType(props.type)) {
118
- case 'index':
119
- return (
120
- <IndexInfoViewer data={schemaData} />
121
- );
122
- default:
123
- return (
124
- <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
125
- );
126
- }
117
+ // verbose mapping to guarantee a correct render for new path types
118
+ // TS will error when a new type is added but not mapped here
119
+ const pathTypeToComponent: Record<EPathType, (() => ReactNode) | undefined> = {
120
+ [EPathType.EPathTypeInvalid]: undefined,
121
+ [EPathType.EPathTypeDir]: undefined,
122
+ [EPathType.EPathTypeTable]: undefined,
123
+ [EPathType.EPathTypeSubDomain]: undefined,
124
+ [EPathType.EPathTypeTableIndex]: () => <IndexInfoViewer data={schemaData} />,
125
+ [EPathType.EPathTypeExtSubDomain]: undefined,
126
+ [EPathType.EPathTypeColumnStore]: undefined,
127
+ [EPathType.EPathTypeColumnTable]: undefined,
128
+ [EPathType.EPathTypeCdcStream]: undefined,
129
+ };
130
+
131
+ return (props.type && pathTypeToComponent[props.type]?.()) || (
132
+ <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
133
+ );
127
134
  }
128
135
 
129
136
  return loading && !wasLoaded ? (
@@ -1,4 +1,4 @@
1
- import {useContext, useEffect, useMemo} from 'react';
1
+ import {useState, useContext, useEffect, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
4
  import {Loader} from '@yandex-cloud/uikit';
@@ -14,15 +14,25 @@ import HistoryContext from '../../../../contexts/HistoryContext';
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {isColumnEntityType} from '../../utils/schema';
16
16
  import {prepareQueryError} from '../../../../utils';
17
+ import {i18n} from '../../../../utils/i18n';
17
18
 
18
19
  import './TopShards.scss';
19
20
 
20
21
  const b = cn('top-shards');
21
22
  const bLink = cn('yc-link');
22
23
 
24
+ const TABLE_SETTINGS = {
25
+ ...DEFAULT_TABLE_SETTINGS,
26
+ dynamicRender: false, // no more than 20 rows
27
+ externalSort: true,
28
+ disableSortReset: true,
29
+ defaultOrder: DataTable.DESCENDING,
30
+ };
31
+
23
32
  const tableColumnsNames = {
24
33
  TabletId: 'TabletId',
25
34
  CPUCores: 'CPUCores',
35
+ DataSize: 'DataSize',
26
36
  Path: 'Path',
27
37
  };
28
38
 
@@ -32,6 +42,28 @@ function prepareCPUWorkloadValue(value) {
32
42
  return `${(value * 100).toFixed(2)}%`;
33
43
  }
34
44
 
45
+ function prepareDateSizeValue(value) {
46
+ return new Intl.NumberFormat(i18n.lang).format(value);
47
+ }
48
+
49
+ function stringToDataTableSortOrder(value) {
50
+ return value && value.split(',').map((columnId) => ({
51
+ columnId,
52
+ order: DataTable.DESCENDING,
53
+ }));
54
+ }
55
+
56
+ function stringToQuerySortOrder(value) {
57
+ return value && value.split(',').map((columnId) => ({
58
+ columnId,
59
+ order: 'DESC',
60
+ }));
61
+ }
62
+
63
+ function dataTableToStringSortOrder(value = []) {
64
+ return value.map(({columnId}) => columnId).join(',');
65
+ }
66
+
35
67
  function TopShards({
36
68
  sendShardQuery,
37
69
  currentSchemaPath,
@@ -46,25 +78,40 @@ function TopShards({
46
78
  setShardQueryOptions,
47
79
  type,
48
80
  }) {
81
+ const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
82
+
49
83
  useEffect(() => {
84
+ autofetcher.stop();
85
+
50
86
  if (autorefresh) {
51
87
  autofetcher.start();
52
- autofetcher.fetch(() => sendShardQuery({database: path, path: currentSchemaPath}));
53
- } else {
54
- autofetcher.stop();
88
+ autofetcher.fetch(() => sendShardQuery({
89
+ database: path,
90
+ path: currentSchemaPath,
91
+ sortOrder: stringToQuerySortOrder(sortOrder),
92
+ }));
55
93
  }
94
+
56
95
  return () => {
57
96
  autofetcher.stop();
58
97
  };
59
- }, [autorefresh]);
98
+ }, [autorefresh, currentSchemaPath, path, sendShardQuery, sortOrder]);
60
99
 
100
+ // don't show loader for requests triggered by table sort, only for path change
61
101
  useEffect(() => {
62
- sendShardQuery({database: path, path: currentSchemaPath});
63
102
  setShardQueryOptions({
64
103
  wasLoaded: false,
65
104
  data: undefined,
66
105
  });
67
- }, [currentSchemaPath]);
106
+ }, [currentSchemaPath, path, setShardQueryOptions]);
107
+
108
+ useEffect(() => {
109
+ sendShardQuery({
110
+ database: path,
111
+ path: currentSchemaPath,
112
+ sortOrder: stringToQuerySortOrder(sortOrder),
113
+ });
114
+ }, [currentSchemaPath, path, sendShardQuery, sortOrder]);
68
115
 
69
116
  const history = useContext(HistoryContext);
70
117
 
@@ -76,6 +123,13 @@ function TopShards({
76
123
  };
77
124
  };
78
125
 
126
+ const onSort = (newSortOrder) => {
127
+ // omit information about sort order to disable ASC order, only DESC makes sense for top shards
128
+ // use a string (and not the DataTable default format) to prevent reference change,
129
+ // which would cause an excess state change, to avoid repeating requests
130
+ setSortOrder(dataTableToStringSortOrder(newSortOrder));
131
+ };
132
+
79
133
  const tableColumns = useMemo(() => {
80
134
  return [
81
135
  {
@@ -83,11 +137,16 @@ function TopShards({
83
137
  // eslint-disable-next-line
84
138
  render: ({value}) => {
85
139
  return (
86
- <span onClick={onSchemaClick(value)} className={bLink({view: 'normal'})}>
140
+ <span
141
+ // tenant name is substringed out in sql query but is needed here
142
+ onClick={onSchemaClick(path + value)}
143
+ className={bLink({view: 'normal'})}
144
+ >
87
145
  {value}
88
146
  </span>
89
147
  );
90
148
  },
149
+ sortable: false,
91
150
  },
92
151
  {
93
152
  name: tableColumnsNames.CPUCores,
@@ -97,6 +156,14 @@ function TopShards({
97
156
  },
98
157
  align: DataTable.RIGHT,
99
158
  },
159
+ {
160
+ name: tableColumnsNames.DataSize,
161
+ header: 'DataSize (B)',
162
+ render: ({value}) => {
163
+ return prepareDateSizeValue(value);
164
+ },
165
+ align: DataTable.RIGHT,
166
+ },
100
167
  {
101
168
  name: tableColumnsNames.TabletId,
102
169
  // eslint-disable-next-line
@@ -107,6 +174,7 @@ function TopShards({
107
174
  </InternalLink>
108
175
  );
109
176
  },
177
+ sortable: false,
110
178
  },
111
179
  ];
112
180
  }, []);
@@ -123,7 +191,7 @@ function TopShards({
123
191
  if (isColumnEntityType(type)) {
124
192
  return 'No data';
125
193
  }
126
- if (error) {
194
+ if (error && !error.isCancelled) {
127
195
  return prepareQueryError(error);
128
196
  }
129
197
 
@@ -132,9 +200,11 @@ function TopShards({
132
200
  <DataTable
133
201
  columns={tableColumns}
134
202
  data={data}
135
- settings={DEFAULT_TABLE_SETTINGS}
203
+ settings={TABLE_SETTINGS}
136
204
  className={b('table')}
137
205
  theme="yandex-cloud"
206
+ onSort={onSort}
207
+ sortOrder={stringToDataTableSortOrder(sortOrder)}
138
208
  />
139
209
  </div>
140
210
  ) : (
@@ -33,6 +33,7 @@ import {
33
33
  DEFAULT_SIZE_RESULT_PANE_KEY,
34
34
  DEFAULT_TABLE_SETTINGS,
35
35
  SAVED_QUERIES_KEY,
36
+ QUERY_INITIAL_RUN_ACTION_KEY,
36
37
  } from '../../../utils/constants';
37
38
  import {prepareQueryResponse} from '../../../utils/index';
38
39
 
@@ -538,7 +539,13 @@ function QueryEditor(props) {
538
539
  };
539
540
 
540
541
  const renderControls = () => {
541
- const {executeQuery, explainQuery, savedQueries, selectRunAction} = props;
542
+ const {
543
+ executeQuery,
544
+ explainQuery,
545
+ savedQueries,
546
+ selectRunAction,
547
+ setSettingValue,
548
+ } = props;
542
549
  const {runAction} = executeQuery;
543
550
  const runIsDisabled = !executeQuery.input || executeQuery.loading;
544
551
  const runText = _.find(RUN_ACTIONS, {value: runAction}).content;
@@ -546,7 +553,10 @@ function QueryEditor(props) {
546
553
  const menuItems = RUN_ACTIONS.map((action) => {
547
554
  return {
548
555
  text: action.content,
549
- action: () => selectRunAction(action.value),
556
+ action: () => {
557
+ selectRunAction(action.value);
558
+ setSettingValue(QUERY_INITIAL_RUN_ACTION_KEY, action.value);
559
+ },
550
560
  };
551
561
  });
552
562