ydb-embedded-ui 3.3.2 → 3.3.4
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 +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
|
|