ydb-embedded-ui 3.3.2 → 3.3.4
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +27 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
- package/dist/components/Errors/ResponseError/index.ts +1 -0
- package/dist/components/Errors/i18n/en.json +2 -1
- package/dist/components/Errors/i18n/ru.json +2 -1
- package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
- package/dist/components/InfoViewer/InfoViewer.scss +1 -1
- package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
- package/dist/components/InfoViewer/formatters/index.ts +1 -0
- package/dist/components/InfoViewer/formatters/table.ts +40 -0
- package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
- package/dist/components/QueryExecutionStatus/index.ts +1 -0
- package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
- package/dist/containers/App/Content.js +12 -5
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
- package/dist/containers/Authentication/Authentication.scss +6 -0
- package/dist/containers/Authentication/Authentication.tsx +34 -15
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
- package/dist/containers/Nodes/Nodes.tsx +1 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
- package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
- package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
- package/dist/containers/Storage/Storage.js +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +12 -3
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
- package/dist/containers/Storage/utils/index.ts +26 -10
- package/dist/containers/Tablet/Tablet.js +1 -1
- package/dist/containers/Tenant/Acl/Acl.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
- package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss +8 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +64 -29
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards → TopShards}/i18n/index.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
- package/dist/containers/Tenants/Tenants.js +4 -3
- package/dist/routes.ts +1 -0
- package/dist/services/api.js +4 -1
- package/dist/store/reducers/authentication.js +0 -15
- package/dist/store/reducers/shardsWorkload.ts +30 -3
- package/dist/store/reducers/storage.js +1 -1
- package/dist/store/state-url-mapping.js +3 -0
- package/dist/types/store/shardsWorkload.ts +6 -0
- package/dist/utils/constants.ts +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/prepareQueryExplain.ts +1 -1
- package/dist/utils/typecheckers.ts +5 -0
- package/package.json +5 -3
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
@@ -0,0 +1,56 @@
|
|
1
|
+
import {RadioButton} from '@gravity-ui/uikit';
|
2
|
+
|
3
|
+
import {DateRange, DateRangeValues} from '../../../../../components/DateRange';
|
4
|
+
|
5
|
+
import {
|
6
|
+
EShardsWorkloadMode,
|
7
|
+
IShardsWorkloadFilters,
|
8
|
+
} from '../../../../../types/store/shardsWorkload';
|
9
|
+
|
10
|
+
import {isEnumMember} from '../../../../../utils/typecheckers';
|
11
|
+
|
12
|
+
import i18n from '../i18n';
|
13
|
+
import {b} from '../TopShards';
|
14
|
+
|
15
|
+
import './Filters.scss';
|
16
|
+
|
17
|
+
interface FiltersProps {
|
18
|
+
value: IShardsWorkloadFilters;
|
19
|
+
onChange: (value: Partial<IShardsWorkloadFilters>) => void;
|
20
|
+
className?: string;
|
21
|
+
}
|
22
|
+
|
23
|
+
export const Filters = ({value, onChange, className}: FiltersProps) => {
|
24
|
+
const handleModeChange = (mode: string) => {
|
25
|
+
if (!isEnumMember(EShardsWorkloadMode, mode)) {
|
26
|
+
const values = Object.values(EShardsWorkloadMode).join(', ');
|
27
|
+
throw new Error(`Unexpected TopShards mode "${mode}". Should be one of: ${values}`);
|
28
|
+
}
|
29
|
+
|
30
|
+
onChange({mode});
|
31
|
+
};
|
32
|
+
|
33
|
+
const handleDateRangeChange = (dateRange: DateRangeValues) => {
|
34
|
+
onChange({
|
35
|
+
mode: EShardsWorkloadMode.History,
|
36
|
+
...dateRange,
|
37
|
+
});
|
38
|
+
};
|
39
|
+
|
40
|
+
const from = value.mode === EShardsWorkloadMode.Immediate ? undefined : value.from;
|
41
|
+
const to = value.mode === EShardsWorkloadMode.Immediate ? undefined : value.to;
|
42
|
+
|
43
|
+
return (
|
44
|
+
<div className={b('filters', className)}>
|
45
|
+
<RadioButton value={value.mode} onUpdate={handleModeChange}>
|
46
|
+
<RadioButton.Option value={EShardsWorkloadMode.Immediate}>
|
47
|
+
{i18n('filters.mode.immediate')}
|
48
|
+
</RadioButton.Option>
|
49
|
+
<RadioButton.Option value={EShardsWorkloadMode.History}>
|
50
|
+
{i18n('filters.mode.history')}
|
51
|
+
</RadioButton.Option>
|
52
|
+
</RadioButton>
|
53
|
+
<DateRange from={from} to={to} onChange={handleDateRangeChange} />
|
54
|
+
</div>
|
55
|
+
);
|
56
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Filters';
|
@@ -1,6 +1,7 @@
|
|
1
|
-
.
|
1
|
+
.top-shards {
|
2
2
|
display: flex;
|
3
3
|
flex-direction: column;
|
4
|
+
gap: 10px;
|
4
5
|
|
5
6
|
height: 100%;
|
6
7
|
|
@@ -11,15 +12,6 @@
|
|
11
12
|
justify-content: center;
|
12
13
|
}
|
13
14
|
|
14
|
-
&__controls {
|
15
|
-
display: flex;
|
16
|
-
flex-wrap: wrap;
|
17
|
-
align-items: baseline;
|
18
|
-
gap: 16px;
|
19
|
-
|
20
|
-
margin-bottom: 10px;
|
21
|
-
}
|
22
|
-
|
23
15
|
&__table {
|
24
16
|
overflow: auto;
|
25
17
|
flex-grow: 1;
|
@@ -2,10 +2,9 @@ import {useState, useContext, useEffect, useMemo} from 'react';
|
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
|
5
|
-
import DataTable, {Column, Settings, SortOrder} from '@
|
5
|
+
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
6
6
|
import {Loader} from '@gravity-ui/uikit';
|
7
7
|
|
8
|
-
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
9
8
|
import {InternalLink} from '../../../../components/InternalLink';
|
10
9
|
|
11
10
|
import HistoryContext from '../../../../contexts/HistoryContext';
|
@@ -18,7 +17,7 @@ import {
|
|
18
17
|
setShardsQueryFilters,
|
19
18
|
} from '../../../../store/reducers/shardsWorkload';
|
20
19
|
import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
|
21
|
-
import
|
20
|
+
import {EShardsWorkloadMode, IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
|
22
21
|
|
23
22
|
import type {EPathType} from '../../../../types/api/schema';
|
24
23
|
|
@@ -31,10 +30,12 @@ import {getDefaultNodePath} from '../../../Node/NodePages';
|
|
31
30
|
|
32
31
|
import {isColumnEntityType} from '../../utils/schema';
|
33
32
|
|
33
|
+
import {Filters} from './Filters';
|
34
|
+
|
34
35
|
import i18n from './i18n';
|
35
|
-
import './
|
36
|
+
import './TopShards.scss';
|
36
37
|
|
37
|
-
const b = cn('
|
38
|
+
export const b = cn('top-shards');
|
38
39
|
const bLink = cn('yc-link');
|
39
40
|
|
40
41
|
const TABLE_SETTINGS: Settings = {
|
@@ -53,6 +54,7 @@ const tableColumnsNames = {
|
|
53
54
|
NodeId: 'NodeId',
|
54
55
|
PeakTime: 'PeakTime',
|
55
56
|
InFlightTxCount: 'InFlightTxCount',
|
57
|
+
IntervalEnd: 'IntervalEnd',
|
56
58
|
};
|
57
59
|
|
58
60
|
function prepareCPUWorkloadValue(value: string) {
|
@@ -82,12 +84,18 @@ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
|
|
82
84
|
return sortOrders.map(({columnId}) => columnId).join(',');
|
83
85
|
}
|
84
86
|
|
85
|
-
|
87
|
+
function fillDateRangeFor(value: IShardsWorkloadFilters) {
|
88
|
+
value.to = Date.now();
|
89
|
+
value.from = value.to - HOUR_IN_SECONDS * 1000;
|
90
|
+
return value;
|
91
|
+
}
|
92
|
+
|
93
|
+
interface TopShardsProps {
|
86
94
|
tenantPath: string;
|
87
95
|
type?: EPathType;
|
88
96
|
}
|
89
97
|
|
90
|
-
export const
|
98
|
+
export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
91
99
|
const dispatch = useDispatch();
|
92
100
|
|
93
101
|
const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
|
@@ -100,17 +108,20 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
100
108
|
wasLoaded,
|
101
109
|
} = useTypedSelector((state) => state.shardsWorkload);
|
102
110
|
|
103
|
-
// default
|
111
|
+
// default filters shouldn't propagate into URL until user interacts with the control
|
104
112
|
// redux initial value can't be used, as it synchronizes with URL
|
105
113
|
const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
};
|
114
|
+
const defaultValue = {...storeFilters};
|
115
|
+
|
116
|
+
if (!defaultValue.mode) {
|
117
|
+
defaultValue.mode = EShardsWorkloadMode.Immediate;
|
111
118
|
}
|
112
119
|
|
113
|
-
|
120
|
+
if (!defaultValue.from && !defaultValue.to) {
|
121
|
+
fillDateRangeFor(defaultValue);
|
122
|
+
}
|
123
|
+
|
124
|
+
return defaultValue;
|
114
125
|
});
|
115
126
|
|
116
127
|
const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
|
@@ -143,18 +154,34 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
143
154
|
const history = useContext(HistoryContext);
|
144
155
|
|
145
156
|
const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
|
146
|
-
// omit information about sort order to disable ASC order, only DESC makes sense for
|
157
|
+
// omit information about sort order to disable ASC order, only DESC makes sense for top shards
|
147
158
|
// use a string (and not the DataTable default format) to prevent reference change,
|
148
159
|
// which would cause an excess state change, to avoid repeating requests
|
149
160
|
setSortOrder(dataTableToStringSortOrder(newSortOrder));
|
150
161
|
};
|
151
162
|
|
152
|
-
const
|
163
|
+
const handleFiltersChange = (value: Partial<IShardsWorkloadFilters>) => {
|
164
|
+
const newStateValue = {...value};
|
165
|
+
const isDateRangePristine =
|
166
|
+
!storeFilters.from && !storeFilters.to && !value.from && !value.to;
|
167
|
+
|
168
|
+
if (isDateRangePristine) {
|
169
|
+
switch (value.mode) {
|
170
|
+
case EShardsWorkloadMode.Immediate:
|
171
|
+
newStateValue.from = newStateValue.to = undefined;
|
172
|
+
break;
|
173
|
+
case EShardsWorkloadMode.History:
|
174
|
+
// should default to the current datetime every time history mode activates
|
175
|
+
fillDateRangeFor(newStateValue);
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
153
180
|
dispatch(setShardsQueryFilters(value));
|
154
|
-
setFilters(
|
181
|
+
setFilters((state) => ({...state, ...newStateValue}));
|
155
182
|
};
|
156
183
|
|
157
|
-
const tableColumns
|
184
|
+
const tableColumns = useMemo(() => {
|
158
185
|
const onSchemaClick = (schemaPath: string) => {
|
159
186
|
return () => {
|
160
187
|
dispatch(setCurrentSchemaPath(schemaPath));
|
@@ -163,7 +190,7 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
163
190
|
};
|
164
191
|
};
|
165
192
|
|
166
|
-
|
193
|
+
const columns: Column<any>[] = [
|
167
194
|
{
|
168
195
|
name: tableColumnsNames.Path,
|
169
196
|
render: ({value: relativeNodePath}) => {
|
@@ -216,11 +243,6 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
216
243
|
align: DataTable.RIGHT,
|
217
244
|
sortable: false,
|
218
245
|
},
|
219
|
-
{
|
220
|
-
name: tableColumnsNames.PeakTime,
|
221
|
-
render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
|
222
|
-
sortable: false,
|
223
|
-
},
|
224
246
|
{
|
225
247
|
name: tableColumnsNames.InFlightTxCount,
|
226
248
|
render: ({value}) => formatNumber(value as number),
|
@@ -228,7 +250,22 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
228
250
|
sortable: false,
|
229
251
|
},
|
230
252
|
];
|
231
|
-
|
253
|
+
|
254
|
+
if (filters.mode === EShardsWorkloadMode.History) {
|
255
|
+
// after NodeId
|
256
|
+
columns.splice(5, 0, {
|
257
|
+
name: tableColumnsNames.PeakTime,
|
258
|
+
render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
|
259
|
+
sortable: false,
|
260
|
+
});
|
261
|
+
columns.push({
|
262
|
+
name: tableColumnsNames.IntervalEnd,
|
263
|
+
render: ({value}) => formatDateTime(new Date(value as string).getTime()),
|
264
|
+
});
|
265
|
+
}
|
266
|
+
|
267
|
+
return columns;
|
268
|
+
}, [dispatch, filters.mode, history, tenantPath]);
|
232
269
|
|
233
270
|
const renderLoader = () => {
|
234
271
|
return (
|
@@ -267,10 +304,8 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
267
304
|
|
268
305
|
return (
|
269
306
|
<div className={b()}>
|
270
|
-
<
|
271
|
-
|
272
|
-
<DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
|
273
|
-
</div>
|
307
|
+
<Filters value={filters} onChange={handleFiltersChange} />
|
308
|
+
{filters.mode === EShardsWorkloadMode.History && <div>{i18n('description')}</div>}
|
274
309
|
{renderContent()}
|
275
310
|
</div>
|
276
311
|
);
|
@@ -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-
|
6
|
+
const COMPONENT = 'ydb-diagnostics-top-shards';
|
7
7
|
|
8
8
|
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
9
|
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TopShards';
|
@@ -26,6 +26,8 @@ import {
|
|
26
26
|
TColumnTableDescription,
|
27
27
|
TDirEntry,
|
28
28
|
} from '../../../types/api/schema';
|
29
|
+
|
30
|
+
import {formatDateTime} from '../../../utils';
|
29
31
|
import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
|
30
32
|
|
31
33
|
import {
|
@@ -201,7 +203,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
|
|
201
203
|
const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
|
202
204
|
let createTime = '';
|
203
205
|
if (startTimeInMilliseconds) {
|
204
|
-
createTime =
|
206
|
+
createTime = formatDateTime(startTimeInMilliseconds);
|
205
207
|
}
|
206
208
|
|
207
209
|
component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
|
@@ -1,28 +1,33 @@
|
|
1
1
|
import React, {useEffect, useRef, useState} from 'react';
|
2
|
+
import {useDispatch, useSelector} from 'react-redux';
|
2
3
|
import cn from 'bem-cn-lite';
|
3
4
|
import MonacoEditor from 'react-monaco-editor';
|
4
|
-
import {Loader, RadioButton} from '@gravity-ui/uikit';
|
5
5
|
import JSONTree from 'react-json-inspector';
|
6
|
-
import
|
6
|
+
import 'react-json-inspector/json-inspector.css';
|
7
|
+
|
7
8
|
import {
|
8
9
|
TextOverflow,
|
9
10
|
getYdbPlanNodeShape,
|
10
11
|
getCompactTopology,
|
11
12
|
getTopology,
|
12
|
-
} from '@
|
13
|
-
import {
|
14
|
-
|
15
|
-
import QueryExecutionStatus from '../../../../components/QueryExecutionStatus/QueryExecutionStatus';
|
13
|
+
} from '@gravity-ui/paranoid';
|
14
|
+
import {Loader, RadioButton} from '@gravity-ui/uikit';
|
15
|
+
|
16
16
|
import Divider from '../../../../components/Divider/Divider';
|
17
17
|
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
18
|
-
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
19
18
|
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
19
|
+
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
|
20
20
|
|
21
|
-
import '
|
22
|
-
import './QueryExplain.scss';
|
23
|
-
import {useDispatch, useSelector} from 'react-redux';
|
21
|
+
import {explainVersions} from '../../../../store/reducers/explainQuery';
|
24
22
|
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
25
23
|
|
24
|
+
import {renderExplainNode} from '../../../../utils';
|
25
|
+
import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
|
26
|
+
|
27
|
+
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
28
|
+
|
29
|
+
import './QueryExplain.scss';
|
30
|
+
|
26
31
|
const b = cn('kv-query-explain');
|
27
32
|
|
28
33
|
const EDITOR_OPTIONS = {
|
@@ -263,7 +268,7 @@ function QueryExplain(props) {
|
|
263
268
|
{!props.loading && (
|
264
269
|
<React.Fragment>
|
265
270
|
<div className={b('controls-right')}>
|
266
|
-
<QueryExecutionStatus
|
271
|
+
<QueryExecutionStatus error={props.error} />
|
267
272
|
{!props.error && (
|
268
273
|
<React.Fragment>
|
269
274
|
<Divider />
|
@@ -1,20 +1,26 @@
|
|
1
1
|
import React, {useEffect, useState} from 'react';
|
2
2
|
import {useDispatch, useSelector} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
|
-
import {RadioButton} from '@gravity-ui/uikit';
|
5
4
|
import JSONTree from 'react-json-inspector';
|
6
5
|
|
6
|
+
import {RadioButton} from '@gravity-ui/uikit';
|
7
|
+
|
7
8
|
import CopyToClipboard from '../../../../components/CopyToClipboard/CopyToClipboard';
|
8
9
|
import Divider from '../../../../components/Divider/Divider';
|
10
|
+
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
9
11
|
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
12
|
+
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
|
13
|
+
|
10
14
|
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
11
15
|
|
12
|
-
import '
|
16
|
+
import {prepareQueryError} from '../../../../utils/query';
|
17
|
+
|
13
18
|
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
14
|
-
|
15
|
-
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
19
|
+
|
16
20
|
import ResultIssues from '../Issues/Issues';
|
17
21
|
|
22
|
+
import './QueryResult.scss';
|
23
|
+
|
18
24
|
const b = cn('kv-query-result');
|
19
25
|
|
20
26
|
const resultOptionsIds = {
|
@@ -94,31 +100,39 @@ function QueryResult(props) {
|
|
94
100
|
};
|
95
101
|
|
96
102
|
const renderIssues = () => {
|
97
|
-
const error = props.error
|
98
|
-
|
99
|
-
const hasIssues = error?.issues && Array.isArray(error.issues);
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
<
|
104
|
-
|
105
|
-
|
106
|
-
<
|
107
|
-
<
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
103
|
+
const error = props.error;
|
104
|
+
|
105
|
+
const hasIssues = error?.data?.issues && Array.isArray(error.data.issues);
|
106
|
+
|
107
|
+
if (hasIssues) {
|
108
|
+
return (
|
109
|
+
<React.Fragment>
|
110
|
+
<ResultIssues data={error.data} />
|
111
|
+
{isFullscreen && (
|
112
|
+
<Fullscreen>
|
113
|
+
<div className={b('result', {fullscreen: true})}>
|
114
|
+
<ResultIssues data={error.data} />
|
115
|
+
</div>
|
116
|
+
</Fullscreen>
|
117
|
+
)}
|
118
|
+
</React.Fragment>
|
119
|
+
)
|
120
|
+
}
|
121
|
+
|
122
|
+
if (error) {
|
123
|
+
return (
|
124
|
+
<div className={b('error')}>
|
125
|
+
{prepareQueryError(error)}
|
126
|
+
</div>
|
127
|
+
);
|
128
|
+
}
|
115
129
|
};
|
116
130
|
|
117
131
|
return (
|
118
132
|
<React.Fragment>
|
119
133
|
<div className={b('controls')}>
|
120
134
|
<div className={b('controls-right')}>
|
121
|
-
<QueryExecutionStatus
|
135
|
+
<QueryExecutionStatus error={props.error} />
|
122
136
|
|
123
137
|
{props.stats && !props.error && (
|
124
138
|
<React.Fragment>
|
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
|
|
5
5
|
import find from 'lodash/find';
|
6
6
|
|
7
7
|
import Icon from '../../../../components/Icon/Icon';
|
8
|
-
import DataTable from '@
|
8
|
+
import DataTable from '@gravity-ui/react-data-table';
|
9
9
|
import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
|
10
10
|
import './SchemaViewer.scss';
|
11
11
|
|
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
|
|
5
5
|
import _ from 'lodash';
|
6
6
|
import {escapeRegExp} from 'lodash/fp';
|
7
7
|
|
8
|
-
import DataTable from '@
|
8
|
+
import DataTable from '@gravity-ui/react-data-table';
|
9
9
|
import {Loader, TextInput, Button} from '@gravity-ui/uikit';
|
10
10
|
|
11
11
|
import EntityStatus from '../../components/EntityStatus/EntityStatus';
|
@@ -218,8 +218,9 @@ class Tenants extends React.Component {
|
|
218
218
|
},
|
219
219
|
accessor: ({Metrics = {}, CoresUsed}) => {
|
220
220
|
if (!isNaN(Number(CoresUsed))) {
|
221
|
-
|
222
|
-
|
221
|
+
return Number(CoresUsed) * 100 > 1
|
222
|
+
? formatCPU(Number(CoresUsed) * 1_000_000)
|
223
|
+
: '—';
|
223
224
|
} else {
|
224
225
|
return Number(Metrics.CPU) ? formatCPU(Number(Metrics.CPU)) : '—';
|
225
226
|
}
|
package/dist/routes.ts
CHANGED
package/dist/services/api.js
CHANGED
@@ -34,21 +34,6 @@ const authentication = function (state = initialState, action) {
|
|
34
34
|
}
|
35
35
|
};
|
36
36
|
|
37
|
-
export const setIsNotAuthenticated = () => {
|
38
|
-
return (dispatch) => {
|
39
|
-
dispatch({
|
40
|
-
type: SET_UNAUTHENTICATED.SUCCESS,
|
41
|
-
});
|
42
|
-
};
|
43
|
-
};
|
44
|
-
export const setIsAuthenticated = () => {
|
45
|
-
return (dispatch) => {
|
46
|
-
dispatch({
|
47
|
-
type: SET_AUTHENTICATED.SUCCESS,
|
48
|
-
});
|
49
|
-
};
|
50
|
-
};
|
51
|
-
|
52
37
|
export const authenticate = (user, password) => {
|
53
38
|
return createApiRequest({
|
54
39
|
request: window.api.authenticate(user, password),
|
@@ -6,6 +6,7 @@ import type {
|
|
6
6
|
IShardsWorkloadFilters,
|
7
7
|
IShardsWorkloadState,
|
8
8
|
} from '../../types/store/shardsWorkload';
|
9
|
+
import {EShardsWorkloadMode} from '../../types/store/shardsWorkload';
|
9
10
|
|
10
11
|
import {parseQueryAPIExecuteResponse} from '../../utils/query';
|
11
12
|
|
@@ -51,7 +52,7 @@ function getFiltersConditions(filters?: IShardsWorkloadFilters) {
|
|
51
52
|
return conditions.join(' AND ');
|
52
53
|
}
|
53
54
|
|
54
|
-
function
|
55
|
+
function createShardQueryHistorical(
|
55
56
|
path: string,
|
56
57
|
filters?: IShardsWorkloadFilters,
|
57
58
|
sortOrder?: SortOrder[],
|
@@ -77,13 +78,36 @@ function createShardQuery(
|
|
77
78
|
DataSize,
|
78
79
|
NodeId,
|
79
80
|
PeakTime,
|
80
|
-
InFlightTxCount
|
81
|
+
InFlightTxCount,
|
82
|
+
IntervalEnd
|
81
83
|
FROM \`.sys/top_partitions_one_hour\`
|
82
84
|
WHERE ${where}
|
83
85
|
${orderBy}
|
84
86
|
LIMIT 20`;
|
85
87
|
}
|
86
88
|
|
89
|
+
function createShardQueryImmediate(path: string, sortOrder?: SortOrder[], tenantName?: string) {
|
90
|
+
const pathSelect = tenantName
|
91
|
+
? `CAST(SUBSTRING(CAST(Path AS String), ${tenantName.length}) AS Utf8) AS Path`
|
92
|
+
: 'Path';
|
93
|
+
|
94
|
+
const orderBy = sortOrder ? `ORDER BY ${sortOrder.map(formatSortOrder).join(', ')}` : '';
|
95
|
+
|
96
|
+
return `SELECT
|
97
|
+
${pathSelect},
|
98
|
+
TabletId,
|
99
|
+
CPUCores,
|
100
|
+
DataSize,
|
101
|
+
NodeId,
|
102
|
+
InFlightTxCount
|
103
|
+
FROM \`.sys/partition_stats\`
|
104
|
+
WHERE
|
105
|
+
Path='${path}'
|
106
|
+
OR Path LIKE '${path}/%'
|
107
|
+
${orderBy}
|
108
|
+
LIMIT 20`;
|
109
|
+
}
|
110
|
+
|
87
111
|
const queryAction = 'execute-scan';
|
88
112
|
|
89
113
|
const shardsWorkload: Reducer<IShardsWorkloadState, IShardsWorkloadAction> = (
|
@@ -146,7 +170,10 @@ export const sendShardQuery = ({database, path = '', sortOrder, filters}: SendSh
|
|
146
170
|
request: window.api.sendQuery(
|
147
171
|
{
|
148
172
|
schema: 'modern',
|
149
|
-
query:
|
173
|
+
query:
|
174
|
+
filters?.mode === EShardsWorkloadMode.Immediate
|
175
|
+
? createShardQueryImmediate(path, sortOrder, database)
|
176
|
+
: createShardQueryHistorical(path, filters, sortOrder, database),
|
150
177
|
database,
|
151
178
|
action: queryAction,
|
152
179
|
},
|
@@ -420,7 +420,7 @@ export const getUsageFilterOptions = createSelector(getVisibleEntitiesList, (ent
|
|
420
420
|
entities.forEach((entity) => {
|
421
421
|
const usage = getUsage(entity, 5);
|
422
422
|
|
423
|
-
if (!Object.
|
423
|
+
if (!Object.prototype.hasOwnProperty.call(items, usage)) {
|
424
424
|
items[usage] = 0;
|
425
425
|
}
|
426
426
|
|