ydb-embedded-ui 3.1.0 → 3.2.0
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.
- package/CHANGELOG.md +19 -0
- package/README.md +2 -0
- package/dist/components/DateRange/DateRange.scss +11 -0
- package/dist/{containers/Tenant/Diagnostics/TopShards → components}/DateRange/DateRange.tsx +7 -7
- package/dist/{containers/Tenant/Diagnostics/TopShards → components}/DateRange/index.ts +0 -0
- package/dist/components/Illustration/Illustration.tsx +4 -11
- package/dist/components/InfoViewer/InfoViewer.scss +2 -0
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -1
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +16 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -5
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
- package/dist/containers/Tenant/Diagnostics/{TopShards/TopShards.scss → OverloadedShards/OverloadedShards.scss} +1 -1
- package/dist/containers/Tenant/Diagnostics/{TopShards/TopShards.tsx → OverloadedShards/OverloadedShards.tsx} +10 -11
- package/dist/containers/Tenant/Diagnostics/{TopShards → OverloadedShards}/i18n/en.json +0 -0
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/{TopShards → OverloadedShards}/i18n/ru.json +0 -0
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.scss +16 -19
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +202 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/en.json +4 -0
- package/dist/containers/Tenant/Diagnostics/{TopShards → TopQueries}/i18n/index.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/TopQueries/i18n/ru.json +4 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/index.ts +1 -0
- package/dist/containers/UserSettings/UserSettings.tsx +1 -1
- package/dist/services/api.d.ts +7 -0
- package/dist/store/reducers/describe.ts +4 -1
- package/dist/store/reducers/executeTopQueries.ts +170 -0
- package/dist/store/reducers/settings.js +1 -1
- package/dist/store/reducers/shardsWorkload.ts +9 -9
- package/dist/store/reducers/storage.js +2 -0
- package/dist/store/reducers/{tablets.js → tablets.ts} +30 -17
- package/dist/store/state-url-mapping.js +10 -2
- package/dist/types/api/compute.ts +52 -0
- package/dist/types/api/consumer.ts +257 -0
- package/dist/types/api/enums.ts +2 -2
- package/dist/types/api/nodes.ts +5 -2
- package/dist/types/api/pdisk.ts +3 -0
- package/dist/types/api/schema.ts +1 -0
- package/dist/types/api/storage.ts +31 -28
- package/dist/types/api/tablet.ts +18 -2
- package/dist/types/api/tenant.ts +4 -1
- package/dist/types/api/topic.ts +157 -0
- package/dist/types/api/vdisk.ts +3 -0
- package/dist/types/store/executeTopQueries.ts +29 -0
- package/dist/types/store/schema.ts +3 -3
- package/dist/types/store/shardsWorkload.ts +3 -3
- package/dist/types/store/tablets.ts +42 -0
- package/dist/utils/getNodesColumns.js +8 -1
- package/package.json +3 -3
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +0 -188
- package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.scss +0 -13
- package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +0 -1
- package/dist/store/reducers/executeTopQueries.js +0 -66
- package/dist/types/api/consumers.ts +0 -3
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.1.0...v3.2.0) (2023-01-09)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **Nodes:** display rack in table ([3b8cdd5](https://github.com/ydb-platform/ydb-embedded-ui/commit/3b8cdd5b472f98132b2faaa9b71b8911750545a6))
|
9
|
+
* **StorageNodes:** display datacenter in table ([4507bfd](https://github.com/ydb-platform/ydb-embedded-ui/commit/4507bfde839b0aafa3722828b7528885c6ac8f84))
|
10
|
+
* **TopQueries:** date range filter ([b9a8e95](https://github.com/ydb-platform/ydb-embedded-ui/commit/b9a8e9504fa68556a724b214ee91b73ec900d37e))
|
11
|
+
* **TopQueries:** filter by query text ([2c8ea97](https://github.com/ydb-platform/ydb-embedded-ui/commit/2c8ea97dd215ea59165cf05315bc5809cf7fafd7))
|
12
|
+
|
13
|
+
|
14
|
+
### Bug Fixes
|
15
|
+
|
16
|
+
* **InfoViewer:** min width for values ([64a4fd4](https://github.com/ydb-platform/ydb-embedded-ui/commit/64a4fd4de16738a9e2fac9cb4fba94eafc938762))
|
17
|
+
* **Nodes:** open external link in new tab ([b7c3ddd](https://github.com/ydb-platform/ydb-embedded-ui/commit/b7c3ddd1e611f2b61466e3eda51f3341f8407588))
|
18
|
+
* **TopQueries:** proper table dynamic render type ([9add6ca](https://github.com/ydb-platform/ydb-embedded-ui/commit/9add6ca9fbfe0475caf1586070a800210320cee6))
|
19
|
+
* **TopShards:** rename to overloaded shards ([d9978bd](https://github.com/ydb-platform/ydb-embedded-ui/commit/d9978bdd84b9a883e4eefcac7f85f856da55d770))
|
20
|
+
* **UserSettings:** treat invertedDisks settings as string ([ad7742a](https://github.com/ydb-platform/ydb-embedded-ui/commit/ad7742a6bf0be59c2b9cbbf947aaa66f79d748be))
|
21
|
+
|
3
22
|
## [3.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.1...v3.1.0) (2022-12-13)
|
4
23
|
|
5
24
|
|
package/README.md
CHANGED
@@ -15,6 +15,8 @@ Local viewer for YDB clusters
|
|
15
15
|
3) Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\
|
16
16
|
You will also see any lint errors in the console.
|
17
17
|
|
18
|
+
For API reference, open Swagger UI on http://localhost:8765/viewer/api/.
|
19
|
+
|
18
20
|
### Making a production bundle.
|
19
21
|
|
20
22
|
Base command `npm run build` builds the app for production to the `build` folder.\
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import cn from 'bem-cn-lite';
|
2
1
|
import {ChangeEventHandler} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
3
|
|
4
4
|
import './DateRange.scss';
|
5
5
|
|
6
|
-
const b = cn('
|
6
|
+
const b = cn('date-range');
|
7
7
|
|
8
8
|
export interface DateRangeValues {
|
9
9
|
/** ms from epoch */
|
@@ -54,21 +54,21 @@ export const DateRange = ({from, to, className, onChange}: DateRangeProps) => {
|
|
54
54
|
const endISO = toTimezonelessISOString(to);
|
55
55
|
|
56
56
|
return (
|
57
|
-
<div className={b(
|
57
|
+
<div className={b(null, className)}>
|
58
58
|
<input
|
59
59
|
type="datetime-local"
|
60
|
-
value={startISO}
|
60
|
+
value={startISO || ''}
|
61
61
|
max={endISO}
|
62
62
|
onChange={handleFromChange}
|
63
|
-
className={b('
|
63
|
+
className={b('input')}
|
64
64
|
/>
|
65
65
|
—
|
66
66
|
<input
|
67
67
|
type="datetime-local"
|
68
68
|
min={startISO}
|
69
|
-
value={endISO}
|
69
|
+
value={endISO || ''}
|
70
70
|
onChange={handleToChange}
|
71
|
-
className={b('
|
71
|
+
className={b('input')}
|
72
72
|
/>
|
73
73
|
</div>
|
74
74
|
);
|
File without changes
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import {useEffect, useState} from 'react';
|
1
|
+
import {ImgHTMLAttributes, useEffect, useState} from 'react';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import {useThemeValue} from '@gravity-ui/uikit';
|
4
4
|
|
5
|
-
export interface IllustrationProps {
|
5
|
+
export interface IllustrationProps extends ImgHTMLAttributes<HTMLImageElement> {
|
6
6
|
name: string;
|
7
7
|
className?: string;
|
8
8
|
}
|
@@ -38,12 +38,5 @@ export const Illustration = ({name, className, ...props}: IllustrationProps) =>
|
|
38
38
|
}
|
39
39
|
}, [srcGetter]);
|
40
40
|
|
41
|
-
return (
|
42
|
-
|
43
|
-
alt={name}
|
44
|
-
src={src}
|
45
|
-
className={b(null, className)}
|
46
|
-
{...props}
|
47
|
-
/>
|
48
|
-
);
|
49
|
-
}
|
41
|
+
return <img alt={name} src={src} className={b(null, className)} {...props} />;
|
42
|
+
};
|
@@ -27,7 +27,7 @@ function DiskStateProgressBar({
|
|
27
27
|
diskAllocatedPercent = -1,
|
28
28
|
severity,
|
29
29
|
}: DiskStateProgressBarProps) {
|
30
|
-
const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
|
30
|
+
const inverted = useSelector((state) => JSON.parse(getSettingValue(state, INVERTED_DISKS_KEY)));
|
31
31
|
|
32
32
|
const renderAllocatedPercent = () => {
|
33
33
|
return (
|
@@ -16,6 +16,8 @@ import './StorageNodes.scss';
|
|
16
16
|
enum TableColumnsIds {
|
17
17
|
NodeId = 'NodeId',
|
18
18
|
FQDN = 'FQDN',
|
19
|
+
DataCenter = 'DataCenter',
|
20
|
+
Rack = 'Rack',
|
19
21
|
uptime = 'uptime',
|
20
22
|
PDisks = 'PDisks',
|
21
23
|
Missing = 'Missing',
|
@@ -36,6 +38,8 @@ interface StorageNodesProps {
|
|
36
38
|
const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
37
39
|
NodeId: 'Node ID',
|
38
40
|
FQDN: 'FQDN',
|
41
|
+
DataCenter: 'DC',
|
42
|
+
Rack: 'Rack',
|
39
43
|
uptime: 'Uptime',
|
40
44
|
PDisks: 'PDisks',
|
41
45
|
Missing: 'Missing',
|
@@ -96,6 +100,18 @@ function StorageNodes({
|
|
96
100
|
},
|
97
101
|
align: DataTable.LEFT,
|
98
102
|
},
|
103
|
+
{
|
104
|
+
name: TableColumnsIds.DataCenter,
|
105
|
+
header: tableColumnsNames[TableColumnsIds.DataCenter],
|
106
|
+
render: ({row}) => row.DataCenter || '—',
|
107
|
+
align: DataTable.LEFT,
|
108
|
+
},
|
109
|
+
{
|
110
|
+
name: TableColumnsIds.Rack,
|
111
|
+
header: tableColumnsNames[TableColumnsIds.Rack],
|
112
|
+
render: ({row}) => row.Rack || '—',
|
113
|
+
align: DataTable.LEFT,
|
114
|
+
},
|
99
115
|
{
|
100
116
|
name: TableColumnsIds.uptime,
|
101
117
|
header: tableColumnsNames[TableColumnsIds.uptime],
|
@@ -9,11 +9,10 @@ import {Switch, Tabs} from '@gravity-ui/uikit';
|
|
9
9
|
|
10
10
|
import {Loader} from '../../../components/Loader';
|
11
11
|
|
12
|
-
|
13
|
-
import TopQueries from './TopQueries/TopQueries';
|
12
|
+
import {TopQueries} from './TopQueries';
|
14
13
|
//@ts-ignore
|
15
14
|
import DetailedOverview from './DetailedOverview/DetailedOverview';
|
16
|
-
import {
|
15
|
+
import {OverloadedShards} from './OverloadedShards';
|
17
16
|
//@ts-ignore
|
18
17
|
import Storage from '../../Storage/Storage';
|
19
18
|
//@ts-ignore
|
@@ -130,8 +129,8 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
130
129
|
/>
|
131
130
|
);
|
132
131
|
}
|
133
|
-
case GeneralPagesIds.
|
134
|
-
return <
|
132
|
+
case GeneralPagesIds.overloadedShards: {
|
133
|
+
return <OverloadedShards tenantPath={tenantNameString} type={type} />;
|
135
134
|
}
|
136
135
|
case GeneralPagesIds.nodes: {
|
137
136
|
return (
|
@@ -3,7 +3,7 @@ import {EPathType} from '../../../types/api/schema';
|
|
3
3
|
export enum GeneralPagesIds {
|
4
4
|
'overview' = 'Overview',
|
5
5
|
'topQueries' = 'topQueries',
|
6
|
-
'
|
6
|
+
'overloadedShards' = 'overloadedShards',
|
7
7
|
'nodes' = 'Nodes',
|
8
8
|
'tablets' = 'Tablets',
|
9
9
|
'storage' = 'Storage',
|
@@ -29,9 +29,9 @@ const topQueries = {
|
|
29
29
|
title: 'Top queries',
|
30
30
|
};
|
31
31
|
|
32
|
-
const
|
33
|
-
id: GeneralPagesIds.
|
34
|
-
title: '
|
32
|
+
const overloadedShards = {
|
33
|
+
id: GeneralPagesIds.overloadedShards,
|
34
|
+
title: 'Overloaded shards',
|
35
35
|
};
|
36
36
|
|
37
37
|
const nodes = {
|
@@ -75,7 +75,7 @@ const consumers = {
|
|
75
75
|
export const DATABASE_PAGES = [
|
76
76
|
overview,
|
77
77
|
topQueries,
|
78
|
-
|
78
|
+
overloadedShards,
|
79
79
|
nodes,
|
80
80
|
tablets,
|
81
81
|
storage,
|
@@ -83,9 +83,9 @@ export const DATABASE_PAGES = [
|
|
83
83
|
describe,
|
84
84
|
];
|
85
85
|
|
86
|
-
export const TABLE_PAGES = [overview,
|
86
|
+
export const TABLE_PAGES = [overview, overloadedShards, graph, tablets, hotKeys, describe];
|
87
87
|
|
88
|
-
export const DIR_PAGES = [overview,
|
88
|
+
export const DIR_PAGES = [overview, overloadedShards, describe];
|
89
89
|
|
90
90
|
export const CDC_STREAM_PAGES = [overview, consumers, describe];
|
91
91
|
export const TOPIC_PAGES = [overview, consumers, describe];
|
@@ -5,6 +5,7 @@ import cn from 'bem-cn-lite';
|
|
5
5
|
import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
|
6
6
|
import {Loader} from '@gravity-ui/uikit';
|
7
7
|
|
8
|
+
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
8
9
|
import InternalLink from '../../../../components/InternalLink/InternalLink';
|
9
10
|
|
10
11
|
import HistoryContext from '../../../../contexts/HistoryContext';
|
@@ -13,8 +14,8 @@ import routes, {createHref} from '../../../../routes';
|
|
13
14
|
|
14
15
|
import {
|
15
16
|
sendShardQuery,
|
16
|
-
|
17
|
-
|
17
|
+
setShardsState,
|
18
|
+
setShardsQueryFilters,
|
18
19
|
} from '../../../../store/reducers/shardsWorkload';
|
19
20
|
import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
|
20
21
|
import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
|
@@ -30,12 +31,10 @@ import {getDefaultNodePath} from '../../../Node/NodePages';
|
|
30
31
|
|
31
32
|
import {isColumnEntityType} from '../../utils/schema';
|
32
33
|
|
33
|
-
import {DateRange, DateRangeValues} from './DateRange';
|
34
|
-
|
35
34
|
import i18n from './i18n';
|
36
|
-
import './
|
35
|
+
import './OverloadedShards.scss';
|
37
36
|
|
38
|
-
const b = cn('
|
37
|
+
const b = cn('overloaded-shards');
|
39
38
|
const bLink = cn('yc-link');
|
40
39
|
|
41
40
|
const TABLE_SETTINGS: Settings = {
|
@@ -83,12 +82,12 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
|
|
83
82
|
return sortOrders.map(({columnId}) => columnId).join(',');
|
84
83
|
}
|
85
84
|
|
86
|
-
interface
|
85
|
+
interface OverloadedShardsProps {
|
87
86
|
tenantPath: string;
|
88
87
|
type?: EPathType;
|
89
88
|
}
|
90
89
|
|
91
|
-
export const
|
90
|
+
export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
92
91
|
const dispatch = useDispatch();
|
93
92
|
|
94
93
|
const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
|
@@ -134,7 +133,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
134
133
|
// don't show loader for requests triggered by table sort, only for path change
|
135
134
|
useEffect(() => {
|
136
135
|
dispatch(
|
137
|
-
|
136
|
+
setShardsState({
|
138
137
|
wasLoaded: false,
|
139
138
|
data: undefined,
|
140
139
|
}),
|
@@ -144,14 +143,14 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
144
143
|
const history = useContext(HistoryContext);
|
145
144
|
|
146
145
|
const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
|
147
|
-
// omit information about sort order to disable ASC order, only DESC makes sense for
|
146
|
+
// omit information about sort order to disable ASC order, only DESC makes sense for overloaded shards
|
148
147
|
// use a string (and not the DataTable default format) to prevent reference change,
|
149
148
|
// which would cause an excess state change, to avoid repeating requests
|
150
149
|
setSortOrder(dataTableToStringSortOrder(newSortOrder));
|
151
150
|
};
|
152
151
|
|
153
152
|
const handleDateRangeChange = (value: DateRangeValues) => {
|
154
|
-
dispatch(
|
153
|
+
dispatch(setShardsQueryFilters(value));
|
155
154
|
setFilters(value);
|
156
155
|
};
|
157
156
|
|
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-diagnostics-overloaded-shards';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './OverloadedShards';
|
@@ -1,43 +1,40 @@
|
|
1
1
|
@import '../../../../styles/mixins.scss';
|
2
2
|
|
3
3
|
.kv-top-queries {
|
4
|
+
display: flex;
|
5
|
+
flex-direction: column;
|
6
|
+
|
4
7
|
height: 100%;
|
5
8
|
|
6
9
|
@include query-data-table;
|
7
|
-
&__message-container {
|
8
|
-
padding: 15px 0;
|
9
|
-
}
|
10
10
|
|
11
11
|
&__loader {
|
12
12
|
display: flex;
|
13
13
|
justify-content: center;
|
14
14
|
}
|
15
15
|
|
16
|
-
&
|
17
|
-
|
16
|
+
&__controls {
|
17
|
+
display: flex;
|
18
|
+
flex-wrap: wrap;
|
19
|
+
gap: 16px;
|
18
20
|
|
19
|
-
|
20
|
-
display: inline-block;
|
21
|
-
}
|
21
|
+
margin-bottom: 10px;
|
22
22
|
}
|
23
23
|
|
24
|
-
&
|
25
|
-
|
26
|
-
line-height: var(--yc-text-body-1-line-height);
|
27
|
-
|
28
|
-
color: var(--yc-color-text-primary);
|
29
|
-
|
30
|
-
&::first-letter {
|
31
|
-
color: var(--yc-color-text-danger);
|
32
|
-
}
|
24
|
+
&__search {
|
25
|
+
@include search();
|
33
26
|
}
|
34
27
|
|
35
|
-
&__result
|
36
|
-
|
28
|
+
&__result {
|
29
|
+
overflow: auto;
|
30
|
+
flex-grow: 1;
|
31
|
+
|
32
|
+
.data-table {
|
37
33
|
&,
|
38
34
|
&__table {
|
39
35
|
width: 100%;
|
40
36
|
}
|
37
|
+
|
41
38
|
&__td {
|
42
39
|
vertical-align: top;
|
43
40
|
white-space: pre;
|
@@ -0,0 +1,202 @@
|
|
1
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
|
5
|
+
import DataTable, {Column, Settings} from '@yandex-cloud/react-data-table';
|
6
|
+
import {Loader} from '@gravity-ui/uikit';
|
7
|
+
|
8
|
+
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
9
|
+
import {Search} from '../../../../components/Search';
|
10
|
+
import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
|
11
|
+
|
12
|
+
import {changeUserInput} from '../../../../store/reducers/executeQuery';
|
13
|
+
import {
|
14
|
+
fetchTopQueries,
|
15
|
+
setTopQueriesFilters,
|
16
|
+
setTopQueriesState,
|
17
|
+
} from '../../../../store/reducers/executeTopQueries';
|
18
|
+
|
19
|
+
import type {KeyValueRow} from '../../../../types/api/query';
|
20
|
+
import type {EPathType} from '../../../../types/api/schema';
|
21
|
+
import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
|
22
|
+
import type {IQueryResult} from '../../../../types/store/query';
|
23
|
+
|
24
|
+
import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
|
25
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
26
|
+
import {prepareQueryError} from '../../../../utils/query';
|
27
|
+
|
28
|
+
import {isColumnEntityType} from '../../utils/schema';
|
29
|
+
import {TenantGeneralTabsIds} from '../../TenantPages';
|
30
|
+
|
31
|
+
import i18n from './i18n';
|
32
|
+
import './TopQueries.scss';
|
33
|
+
|
34
|
+
const b = cn('kv-top-queries');
|
35
|
+
|
36
|
+
const TABLE_SETTINGS: Settings = {
|
37
|
+
...DEFAULT_TABLE_SETTINGS,
|
38
|
+
dynamicRenderType: 'variable',
|
39
|
+
};
|
40
|
+
|
41
|
+
const MAX_QUERY_HEIGHT = 10;
|
42
|
+
const COLUMNS: Column<KeyValueRow>[] = [
|
43
|
+
{
|
44
|
+
name: 'CPUTimeUs',
|
45
|
+
width: 140,
|
46
|
+
sortAccessor: (row) => Number(row['CPUTimeUs']),
|
47
|
+
},
|
48
|
+
{
|
49
|
+
name: 'QueryText',
|
50
|
+
width: 500,
|
51
|
+
sortable: false,
|
52
|
+
render: ({value}) => <TruncatedQuery value={value} maxQueryHeight={MAX_QUERY_HEIGHT} />,
|
53
|
+
},
|
54
|
+
];
|
55
|
+
|
56
|
+
interface TopQueriesProps {
|
57
|
+
path: string;
|
58
|
+
changeSchemaTab: (tab: TenantGeneralTabsIds) => void;
|
59
|
+
type?: EPathType;
|
60
|
+
}
|
61
|
+
|
62
|
+
export const TopQueries = ({path, type, changeSchemaTab}: TopQueriesProps) => {
|
63
|
+
const dispatch = useDispatch();
|
64
|
+
|
65
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
66
|
+
|
67
|
+
const {
|
68
|
+
loading,
|
69
|
+
wasLoaded,
|
70
|
+
error,
|
71
|
+
data: {result: data = undefined} = {},
|
72
|
+
filters: storeFilters,
|
73
|
+
} = useTypedSelector((state) => state.executeTopQueries);
|
74
|
+
|
75
|
+
const preventFetch = useRef(false);
|
76
|
+
|
77
|
+
// some filters sync between redux state and URL
|
78
|
+
// component state is for default values,
|
79
|
+
// default values are determined from the query response, and should not propagate to URL
|
80
|
+
const [filters, setFilters] = useState<ITopQueriesFilters>(storeFilters);
|
81
|
+
|
82
|
+
useEffect(() => {
|
83
|
+
dispatch(setTopQueriesFilters(filters));
|
84
|
+
}, [dispatch, filters]);
|
85
|
+
|
86
|
+
const setDefaultFiltersFromResponse = (responseData?: IQueryResult) => {
|
87
|
+
const intervalEnd = responseData?.result?.[0]?.IntervalEnd;
|
88
|
+
|
89
|
+
if (intervalEnd) {
|
90
|
+
const to = new Date(intervalEnd).getTime();
|
91
|
+
const from = new Date(to - HOUR_IN_SECONDS * 1000).getTime();
|
92
|
+
|
93
|
+
setFilters((currentFilters) => {
|
94
|
+
// request without filters returns the latest interval with data
|
95
|
+
// only in this case should update filters in ui
|
96
|
+
// also don't update if user already interacted with controls
|
97
|
+
const shouldUpdateFilters = !currentFilters.from && !currentFilters.to;
|
98
|
+
|
99
|
+
if (!shouldUpdateFilters) {
|
100
|
+
return currentFilters;
|
101
|
+
}
|
102
|
+
|
103
|
+
preventFetch.current = true;
|
104
|
+
|
105
|
+
return {...currentFilters, from, to};
|
106
|
+
});
|
107
|
+
}
|
108
|
+
};
|
109
|
+
|
110
|
+
useAutofetcher(
|
111
|
+
(isBackground) => {
|
112
|
+
if (preventFetch.current) {
|
113
|
+
preventFetch.current = false;
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
|
117
|
+
if (!isBackground) {
|
118
|
+
dispatch(
|
119
|
+
setTopQueriesState({
|
120
|
+
wasLoaded: false,
|
121
|
+
data: undefined,
|
122
|
+
}),
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
126
|
+
// @ts-expect-error
|
127
|
+
// typed dispatch required, remove error expectation after adding it
|
128
|
+
dispatch(fetchTopQueries({database: path, filters})).then(
|
129
|
+
setDefaultFiltersFromResponse,
|
130
|
+
);
|
131
|
+
},
|
132
|
+
[dispatch, filters, path],
|
133
|
+
autorefresh,
|
134
|
+
);
|
135
|
+
|
136
|
+
const handleRowClick = useCallback(
|
137
|
+
(row) => {
|
138
|
+
const {QueryText: input} = row;
|
139
|
+
|
140
|
+
dispatch(changeUserInput({input}));
|
141
|
+
changeSchemaTab(TenantGeneralTabsIds.query);
|
142
|
+
},
|
143
|
+
[changeSchemaTab, dispatch],
|
144
|
+
);
|
145
|
+
|
146
|
+
const handleTextSearchUpdate = (text: string) => {
|
147
|
+
setFilters((currentFilters) => ({...currentFilters, text}));
|
148
|
+
};
|
149
|
+
|
150
|
+
const handleDateRangeChange = (value: DateRangeValues) => {
|
151
|
+
setFilters((currentFilters) => ({...currentFilters, ...value}));
|
152
|
+
};
|
153
|
+
|
154
|
+
const renderLoader = () => {
|
155
|
+
return (
|
156
|
+
<div className={b('loader')}>
|
157
|
+
<Loader size="m" />
|
158
|
+
</div>
|
159
|
+
);
|
160
|
+
};
|
161
|
+
|
162
|
+
const renderContent = () => {
|
163
|
+
if (loading && !wasLoaded) {
|
164
|
+
return renderLoader();
|
165
|
+
}
|
166
|
+
|
167
|
+
if (error && !error.isCancelled) {
|
168
|
+
return <div className="error">{prepareQueryError(error)}</div>;
|
169
|
+
}
|
170
|
+
|
171
|
+
if (!data || isColumnEntityType(type)) {
|
172
|
+
return i18n('no-data');
|
173
|
+
}
|
174
|
+
|
175
|
+
return (
|
176
|
+
<div className={b('result')}>
|
177
|
+
<DataTable
|
178
|
+
columns={COLUMNS}
|
179
|
+
data={data}
|
180
|
+
settings={TABLE_SETTINGS}
|
181
|
+
onRowClick={handleRowClick}
|
182
|
+
theme="yandex-cloud"
|
183
|
+
/>
|
184
|
+
</div>
|
185
|
+
);
|
186
|
+
};
|
187
|
+
|
188
|
+
return (
|
189
|
+
<div className={b()}>
|
190
|
+
<div className={b('controls')}>
|
191
|
+
<Search
|
192
|
+
value={filters.text}
|
193
|
+
onChange={handleTextSearchUpdate}
|
194
|
+
placeholder={i18n('filter.text.placeholder')}
|
195
|
+
className={b('search')}
|
196
|
+
/>
|
197
|
+
<DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
|
198
|
+
</div>
|
199
|
+
{renderContent()}
|
200
|
+
</div>
|
201
|
+
);
|
202
|
+
};
|
@@ -3,7 +3,7 @@ import {i18n, Lang} from '../../../../../utils/i18n';
|
|
3
3
|
import en from './en.json';
|
4
4
|
import ru from './ru.json';
|
5
5
|
|
6
|
-
const COMPONENT = 'ydb-diagnostics-top-
|
6
|
+
const COMPONENT = 'ydb-diagnostics-top-queries';
|
7
7
|
|
8
8
|
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
9
|
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TopQueries';
|
package/dist/services/api.d.ts
CHANGED
@@ -48,6 +48,13 @@ interface Window {
|
|
48
48
|
getTenantInfo: (params: {
|
49
49
|
path: string;
|
50
50
|
}) => Promise<import('../types/api/tenant').TTenantInfo>;
|
51
|
+
getTabletsInfo: (params: {
|
52
|
+
nodes?: string[];
|
53
|
+
path?: string;
|
54
|
+
}) => Promise<import('../types/api/tablet').TEvTabletStateResponse>;
|
55
|
+
getHeatmapData: (params: {
|
56
|
+
path: string;
|
57
|
+
}) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
|
51
58
|
[method: string]: Function;
|
52
59
|
};
|
53
60
|
}
|