ydb-embedded-ui 4.13.0 → 4.15.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +25 -0
- package/dist/components/Tablet/Tablet.scss +1 -16
- package/dist/components/Tablet/Tablet.tsx +5 -5
- package/dist/components/TabletIcon/TabletIcon.scss +17 -0
- package/dist/components/TabletIcon/TabletIcon.tsx +18 -0
- package/dist/containers/App/App.js +1 -1
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -1
- package/dist/containers/Authentication/Authentication.tsx +1 -1
- package/dist/containers/Header/Header.scss +2 -0
- package/dist/containers/Header/Header.tsx +2 -7
- package/dist/containers/Header/{breadcrumbs.ts → breadcrumbs.tsx} +19 -8
- package/dist/containers/Nodes/Nodes.tsx +53 -16
- package/dist/containers/Nodes/getNodesColumns.tsx +31 -13
- package/dist/containers/Storage/Storage.tsx +64 -32
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +56 -73
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +33 -43
- package/dist/containers/Storage/utils/index.ts +3 -3
- package/dist/containers/Tablet/Tablet.tsx +9 -3
- package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.scss +8 -0
- package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +13 -1
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +3 -1
- package/dist/containers/Tenant/Query/i18n/en.json +6 -4
- package/dist/containers/Tenant/Query/i18n/ru.json +6 -4
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +4 -0
- package/dist/containers/Tenant/i18n/en.json +3 -0
- package/dist/containers/Tenant/i18n/ru.json +3 -0
- package/dist/containers/Tenant/utils/queryTemplates.ts +89 -0
- package/dist/containers/Tenant/utils/schema.ts +1 -1
- package/dist/containers/Tenant/utils/schemaActions.ts +30 -54
- package/dist/containers/Tenant/utils/schemaControls.tsx +69 -0
- package/dist/containers/UserSettings/i18n/en.json +3 -0
- package/dist/containers/UserSettings/i18n/ru.json +3 -0
- package/dist/containers/UserSettings/settings.ts +12 -1
- package/dist/{reportWebVitals.js → reportWebVitals.ts} +3 -1
- package/dist/services/api.ts +30 -16
- package/dist/store/reducers/{authentication.js → authentication/authentication.ts} +14 -7
- package/dist/store/reducers/authentication/types.ts +15 -0
- package/dist/store/reducers/describe.ts +1 -16
- package/dist/store/reducers/header/types.ts +2 -0
- package/dist/store/reducers/index.ts +1 -1
- package/dist/store/reducers/nodes/nodes.ts +23 -6
- package/dist/store/reducers/nodes/selectors.ts +2 -2
- package/dist/store/reducers/nodes/types.ts +15 -5
- package/dist/store/reducers/settings/settings.ts +5 -0
- package/dist/store/reducers/storage/selectors.ts +50 -150
- package/dist/store/reducers/storage/storage.ts +73 -25
- package/dist/store/reducers/storage/types.ts +49 -17
- package/dist/store/reducers/storage/utils.ts +207 -0
- package/dist/store/utils.ts +1 -1
- package/dist/types/api/compute.ts +0 -12
- package/dist/types/api/error.ts +4 -0
- package/dist/types/api/nodes.ts +0 -12
- package/dist/types/api/storage.ts +32 -4
- package/dist/types/window.d.ts +1 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/filters.ts +23 -0
- package/dist/utils/hooks/index.ts +4 -0
- package/dist/utils/hooks/useNodesRequestParams.ts +46 -0
- package/dist/utils/hooks/useStorageRequestParams.ts +28 -0
- package/dist/utils/hooks/useTableSort.ts +37 -0
- package/dist/utils/nodes.ts +25 -0
- package/dist/utils/storage.ts +31 -3
- package/package.json +2 -6
- package/dist/HOCS/WithSearch/WithSearch.js +0 -26
- package/dist/HOCS/index.js +0 -1
- package/dist/components/Hotkey/Hotkey.js +0 -102
- package/dist/components/Pagination/Pagination.js +0 -63
- package/dist/components/Pagination/Pagination.scss +0 -28
- package/dist/types/store/storage.ts +0 -12
- /package/dist/{index.js → index.tsx} +0 -0
- /package/dist/utils/{monaco.js → monaco.ts} +0 -0
@@ -1,6 +1,9 @@
|
|
1
1
|
import block from 'bem-cn-lite';
|
2
2
|
|
3
3
|
import {formatDurationToShortTimeFormat, parseUsToMs} from '../../../../utils/timeParsers';
|
4
|
+
import {LabelWithPopover} from '../../../../components/LabelWithPopover';
|
5
|
+
|
6
|
+
import i18n from '../i18n';
|
4
7
|
|
5
8
|
import './QueryDuration.scss';
|
6
9
|
|
@@ -17,5 +20,14 @@ export const QueryDuration = ({duration}: QueryDurationProps) => {
|
|
17
20
|
|
18
21
|
const parsedDuration = formatDurationToShortTimeFormat(parseUsToMs(duration), 1);
|
19
22
|
|
20
|
-
return
|
23
|
+
return (
|
24
|
+
<span className={b()}>
|
25
|
+
<LabelWithPopover
|
26
|
+
className={b('item-with-popover')}
|
27
|
+
contentClassName={b('popover')}
|
28
|
+
text={parsedDuration}
|
29
|
+
popoverContent={i18n('query-duration.description')}
|
30
|
+
/>
|
31
|
+
</span>
|
32
|
+
);
|
21
33
|
};
|
@@ -17,8 +17,10 @@
|
|
17
17
|
"preview.not-available": "Preview is not available",
|
18
18
|
"preview.close": "Close preview",
|
19
19
|
|
20
|
-
"method-description.script": "For YQL-scripts combining DDL and DML
|
21
|
-
"method-description.scan": "Read-only queries, potentially reading a lot of data
|
22
|
-
"method-description.data": "DML queries for changing and fetching data in serialization mode
|
23
|
-
"method-description.query": "Any
|
20
|
+
"method-description.script": "For YQL-scripts combining DDL and DML.\nAPI call: schema.scripting",
|
21
|
+
"method-description.scan": "Read-only queries, potentially reading a lot of data.\nAPI call: table.ExecuteScan",
|
22
|
+
"method-description.data": "DML queries for changing and fetching data in serialization mode.\nAPI call: table.executeDataQuery",
|
23
|
+
"method-description.query": "Any query. An experimental API call supposed to replace all existing methods.\nAPI Call: query.ExecuteScript",
|
24
|
+
|
25
|
+
"query-duration.description": "Duration of server-side query execution"
|
24
26
|
}
|
@@ -17,8 +17,10 @@
|
|
17
17
|
"preview.not-available": "Предпросмотр недоступен",
|
18
18
|
"preview.close": "Закрыть предпросмотр",
|
19
19
|
|
20
|
-
"method-description.script": "Для скриптов, совмещающих DDL и DML
|
21
|
-
"method-description.scan": "Только читающие запросы, потенциально читающие много
|
22
|
-
"method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable
|
23
|
-
"method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все
|
20
|
+
"method-description.script": "Для скриптов, совмещающих DDL и DML-конструкции.\nAPI call: schema.scripting",
|
21
|
+
"method-description.scan": "Только читающие запросы, потенциально читающие много данных.\nAPI call: table.ExecuteScan",
|
22
|
+
"method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable.\nAPI call: table.executeDataQuery",
|
23
|
+
"method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные.\nAPI call: query.ExecuteScript",
|
24
|
+
|
25
|
+
"query-duration.description": "Время выполнения запроса на стороне сервера"
|
24
26
|
}
|
@@ -9,6 +9,7 @@ import {useQueryModes} from '../../../../utils/hooks';
|
|
9
9
|
|
10
10
|
import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
|
11
11
|
import {getActions} from '../../utils/schemaActions';
|
12
|
+
import {getControls} from '../../utils/schemaControls';
|
12
13
|
|
13
14
|
interface SchemaTreeProps {
|
14
15
|
rootPath: string;
|
@@ -78,6 +79,9 @@ export function SchemaTree(props: SchemaTreeProps) {
|
|
78
79
|
setActivePath: handleActivePathUpdate,
|
79
80
|
setQueryMode,
|
80
81
|
})}
|
82
|
+
renderAdditionalNodeElements={getControls(dispatch, {
|
83
|
+
setActivePath: handleActivePathUpdate,
|
84
|
+
})}
|
81
85
|
activePath={currentPath}
|
82
86
|
onActivePathUpdate={handleActivePathUpdate}
|
83
87
|
cache={false}
|
@@ -14,8 +14,11 @@
|
|
14
14
|
"actions.openPreview": "Open preview",
|
15
15
|
"actions.createTable": "Create table...",
|
16
16
|
"actions.createExternalTable": "Create external table...",
|
17
|
+
"actions.createTopic": "Create topic...",
|
17
18
|
"actions.dropTable": "Drop table...",
|
19
|
+
"actions.dropTopic": "Drop topic...",
|
18
20
|
"actions.alterTable": "Alter table...",
|
21
|
+
"actions.alterTopic": "Alter topic...",
|
19
22
|
"actions.selectQuery": "Select query...",
|
20
23
|
"actions.upsertQuery": "Upsert query..."
|
21
24
|
}
|
@@ -14,8 +14,11 @@
|
|
14
14
|
"actions.openPreview": "Открыть превью",
|
15
15
|
"actions.createTable": "Создать таблицу...",
|
16
16
|
"actions.createExternalTable": "Создать внешнюю таблицу...",
|
17
|
+
"actions.createTopic": "Создать топик...",
|
17
18
|
"actions.dropTable": "Удалить таблицу...",
|
19
|
+
"actions.dropTopic": "Удалить топик...",
|
18
20
|
"actions.alterTable": "Изменить таблицу...",
|
21
|
+
"actions.alterTopic": "Изменить топик...",
|
19
22
|
"actions.selectQuery": "Select запрос...",
|
20
23
|
"actions.upsertQuery": "Upsert запрос..."
|
21
24
|
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
export const createTableTemplate = (path: string) => {
|
2
|
+
return `CREATE TABLE \`${path}/my_table\`
|
3
|
+
(
|
4
|
+
\`id\` Uint64,
|
5
|
+
\`name\` String,
|
6
|
+
PRIMARY KEY (\`id\`)
|
7
|
+
);`;
|
8
|
+
};
|
9
|
+
export const alterTableTemplate = (path: string) => {
|
10
|
+
return `ALTER TABLE \`${path}\`
|
11
|
+
ADD COLUMN is_deleted Bool;`;
|
12
|
+
};
|
13
|
+
export const selectQueryTemplate = (path: string) => {
|
14
|
+
return `SELECT *
|
15
|
+
FROM \`${path}\`
|
16
|
+
LIMIT 10;`;
|
17
|
+
};
|
18
|
+
export const upsertQueryTemplate = (path: string) => {
|
19
|
+
return `UPSERT INTO \`${path}\`
|
20
|
+
( \`id\`, \`name\` )
|
21
|
+
VALUES ( );`;
|
22
|
+
};
|
23
|
+
|
24
|
+
export const dropExternalTableTemplate = (path: string) => {
|
25
|
+
return `DROP EXTERNAL TABLE \`${path}\`;`;
|
26
|
+
};
|
27
|
+
|
28
|
+
export const createExternalTableTemplate = (path: string) => {
|
29
|
+
// Remove data source name from path
|
30
|
+
// to create table in the same folder with data source
|
31
|
+
const targetPath = path.split('/').slice(0, -1).join('/');
|
32
|
+
|
33
|
+
return `CREATE EXTERNAL TABLE \`${targetPath}/my_external_table\` (
|
34
|
+
column1 Int,
|
35
|
+
column2 Int
|
36
|
+
) WITH (
|
37
|
+
DATA_SOURCE="${path}",
|
38
|
+
LOCATION="",
|
39
|
+
FORMAT="json_as_string",
|
40
|
+
\`file_pattern\`=""
|
41
|
+
);`;
|
42
|
+
};
|
43
|
+
|
44
|
+
export const createTopicTemplate = (path: string) => {
|
45
|
+
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_topic
|
46
|
+
CREATE TOPIC \`${path}/my_topic\` (
|
47
|
+
CONSUMER consumer1,
|
48
|
+
CONSUMER consumer2 WITH (read_from = Datetime('2022-12-01T12:13:22Z')) -- Sets up the message write time starting from which the consumer will receive data.
|
49
|
+
-- Value type: Datetime OR Timestamp OR integer (unix-timestamp in the numeric format).
|
50
|
+
-- Default value: now
|
51
|
+
) WITH (
|
52
|
+
min_active_partitions = 5, -- Minimum number of topic partitions.
|
53
|
+
partition_count_limit = 10, -- Maximum number of active partitions in the topic. 0 is interpreted as unlimited.
|
54
|
+
retention_period = Interval('PT12H'), -- Data retention period in the topic. Value type: Interval, default value: 18h.
|
55
|
+
retention_storage_mb = 1, -- Limit on the maximum disk space occupied by the topic data.
|
56
|
+
-- When this value is exceeded, the older data is cleared, like under a retention policy.
|
57
|
+
-- 0 is interpreted as unlimited.
|
58
|
+
partition_write_speed_bytes_per_second = 2097152, -- Maximum allowed write speed per partition.
|
59
|
+
partition_write_burst_bytes = 2097152 -- Write quota allocated for write bursts.
|
60
|
+
-- When set to zero, the actual write_burst value is equalled to
|
61
|
+
-- the quota value (this allows write bursts of up to one second).
|
62
|
+
);`;
|
63
|
+
};
|
64
|
+
|
65
|
+
export const alterTopicTemplate = (path: string) => {
|
66
|
+
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/alter_topic
|
67
|
+
ALTER TOPIC \`${path}\`
|
68
|
+
ADD CONSUMER new_consumer WITH (read_from = 0), -- Sets up the message write time starting from which the consumer will receive data.
|
69
|
+
-- Value type: Datetime OR Timestamp OR integer (unix-timestamp in the numeric format).
|
70
|
+
-- Default value: now
|
71
|
+
ALTER CONSUMER consumer1 SET (read_from = Datetime('2023-12-01T12:13:22Z')),
|
72
|
+
DROP CONSUMER consumer2,
|
73
|
+
SET (
|
74
|
+
min_active_partitions = 10, -- Minimum number of topic partitions.
|
75
|
+
partition_count_limit = 15, -- Maximum number of active partitions in the topic. 0 is interpreted as unlimited.
|
76
|
+
retention_period = Interval('PT36H'), -- Data retention period in the topic. Value type: Interval, default value: 18h.
|
77
|
+
retention_storage_mb = 10, -- Limit on the maximum disk space occupied by the topic data.
|
78
|
+
-- When this value is exceeded, the older data is cleared, like under a retention policy.
|
79
|
+
-- 0 is interpreted as unlimited.
|
80
|
+
partition_write_speed_bytes_per_second = 3145728, -- Maximum allowed write speed per partition.
|
81
|
+
partition_write_burst_bytes = 1048576 -- Write quota allocated for write bursts.
|
82
|
+
-- When set to zero, the actual write_burst value is equalled to
|
83
|
+
-- the quota value (this allows write bursts of up to one second).
|
84
|
+
);`;
|
85
|
+
};
|
86
|
+
|
87
|
+
export const dropTopicTemplate = (path: string) => {
|
88
|
+
return `DROP TOPIC \`${path}\`;`;
|
89
|
+
};
|
@@ -30,7 +30,7 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
|
|
30
30
|
|
31
31
|
[EPathType.EPathTypeColumnTable]: 'column_table',
|
32
32
|
|
33
|
-
[EPathType.EPathTypeCdcStream]: '
|
33
|
+
[EPathType.EPathTypeCdcStream]: 'stream',
|
34
34
|
[EPathType.EPathTypePersQueueGroup]: 'topic',
|
35
35
|
|
36
36
|
[EPathType.EPathTypeExternalDataSource]: 'external_data_source',
|
@@ -6,55 +6,23 @@ import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-component
|
|
6
6
|
import type {QueryMode} from '../../../types/store/query';
|
7
7
|
import type {SetQueryModeIfAvailable} from '../../../utils/hooks';
|
8
8
|
import {changeUserInput} from '../../../store/reducers/executeQuery';
|
9
|
-
import {setShowPreview} from '../../../store/reducers/schema/schema';
|
10
9
|
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
|
11
10
|
import {TENANT_QUERY_TABS_ID, TENANT_PAGES_IDS} from '../../../store/reducers/tenant/constants';
|
12
11
|
import createToast from '../../../utils/createToast';
|
13
12
|
|
14
13
|
import i18n from '../i18n';
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
};
|
28
|
-
const selectQueryTemplate = (path: string) => {
|
29
|
-
return `SELECT *
|
30
|
-
FROM \`${path}\`
|
31
|
-
LIMIT 10;`;
|
32
|
-
};
|
33
|
-
const upsertQueryTemplate = (path: string) => {
|
34
|
-
return `UPSERT INTO \`${path}\`
|
35
|
-
( \`id\`, \`name\` )
|
36
|
-
VALUES ( );`;
|
37
|
-
};
|
38
|
-
|
39
|
-
const dropExternalTableTemplate = (path: string) => {
|
40
|
-
return `DROP EXTERNAL TABLE \`${path}\`;`;
|
41
|
-
};
|
42
|
-
|
43
|
-
const createExternalTableTemplate = (path: string) => {
|
44
|
-
// Remove data source name from path
|
45
|
-
// to create table in the same folder with data source
|
46
|
-
const targetPath = path.split('/').slice(0, -1).join('/');
|
47
|
-
|
48
|
-
return `CREATE EXTERNAL TABLE \`${targetPath}/my_external_table\` (
|
49
|
-
column1 Int,
|
50
|
-
column2 Int
|
51
|
-
) WITH (
|
52
|
-
DATA_SOURCE="${path}",
|
53
|
-
LOCATION="",
|
54
|
-
FORMAT="json_as_string",
|
55
|
-
\`file_pattern\`=""
|
56
|
-
);`;
|
57
|
-
};
|
15
|
+
import {
|
16
|
+
alterTableTemplate,
|
17
|
+
alterTopicTemplate,
|
18
|
+
createExternalTableTemplate,
|
19
|
+
createTableTemplate,
|
20
|
+
createTopicTemplate,
|
21
|
+
dropExternalTableTemplate,
|
22
|
+
dropTopicTemplate,
|
23
|
+
selectQueryTemplate,
|
24
|
+
upsertQueryTemplate,
|
25
|
+
} from './queryTemplates';
|
58
26
|
|
59
27
|
interface ActionsAdditionalEffects {
|
60
28
|
setQueryMode: SetQueryModeIfAvailable;
|
@@ -93,6 +61,9 @@ const bindActions = (
|
|
93
61
|
'query',
|
94
62
|
i18n('actions.externalTableSelectUnavailable'),
|
95
63
|
),
|
64
|
+
createTopic: inputQuery(createTopicTemplate, 'script'),
|
65
|
+
alterTopic: inputQuery(alterTopicTemplate, 'script'),
|
66
|
+
dropTopic: inputQuery(dropTopicTemplate, 'script'),
|
96
67
|
copyPath: () => {
|
97
68
|
try {
|
98
69
|
copy(path);
|
@@ -109,12 +80,6 @@ const bindActions = (
|
|
109
80
|
});
|
110
81
|
}
|
111
82
|
},
|
112
|
-
openPreview: () => {
|
113
|
-
dispatch(setShowPreview(true));
|
114
|
-
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
|
115
|
-
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
|
116
|
-
setActivePath(path);
|
117
|
-
},
|
118
83
|
};
|
119
84
|
};
|
120
85
|
|
@@ -125,14 +90,16 @@ export const getActions =
|
|
125
90
|
(path: string, type: NavigationTreeNodeType) => {
|
126
91
|
const actions = bindActions(path, dispatch, additionalEffects);
|
127
92
|
const copyItem = {text: i18n('actions.copyPath'), action: actions.copyPath};
|
128
|
-
const openPreview = {text: i18n('actions.openPreview'), action: actions.openPreview};
|
129
93
|
|
130
94
|
const DIR_SET: ActionsSet = [
|
131
95
|
[copyItem],
|
132
|
-
[
|
96
|
+
[
|
97
|
+
{text: i18n('actions.createTable'), action: actions.createTable},
|
98
|
+
{text: i18n('actions.createTopic'), action: actions.createTopic},
|
99
|
+
],
|
133
100
|
];
|
134
101
|
const TABLE_SET: ActionsSet = [
|
135
|
-
[
|
102
|
+
[copyItem],
|
136
103
|
[
|
137
104
|
{text: i18n('actions.alterTable'), action: actions.alterTable},
|
138
105
|
{text: i18n('actions.selectQuery'), action: actions.selectQuery},
|
@@ -140,8 +107,16 @@ export const getActions =
|
|
140
107
|
],
|
141
108
|
];
|
142
109
|
|
110
|
+
const TOPIC_SET: ActionsSet = [
|
111
|
+
[copyItem],
|
112
|
+
[
|
113
|
+
{text: i18n('actions.alterTopic'), action: actions.alterTopic},
|
114
|
+
{text: i18n('actions.dropTopic'), action: actions.dropTopic},
|
115
|
+
],
|
116
|
+
];
|
117
|
+
|
143
118
|
const EXTERNAL_TABLE_SET = [
|
144
|
-
[
|
119
|
+
[copyItem],
|
145
120
|
[
|
146
121
|
{
|
147
122
|
text: i18n('actions.selectQuery'),
|
@@ -168,7 +143,8 @@ export const getActions =
|
|
168
143
|
column_table: TABLE_SET,
|
169
144
|
|
170
145
|
index_table: JUST_COPY,
|
171
|
-
topic:
|
146
|
+
topic: TOPIC_SET,
|
147
|
+
stream: JUST_COPY,
|
172
148
|
|
173
149
|
index: JUST_COPY,
|
174
150
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import {Dispatch} from 'react';
|
2
|
+
|
3
|
+
import {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
|
4
|
+
import {Button} from '@gravity-ui/uikit';
|
5
|
+
|
6
|
+
import {setShowPreview} from '../../../store/reducers/schema/schema';
|
7
|
+
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
|
8
|
+
import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
|
9
|
+
import {IconWrapper} from '../../../components/Icon';
|
10
|
+
|
11
|
+
import i18n from '../i18n';
|
12
|
+
|
13
|
+
interface ControlsAdditionalEffects {
|
14
|
+
setActivePath: (path: string) => void;
|
15
|
+
}
|
16
|
+
|
17
|
+
const bindActions = (
|
18
|
+
path: string,
|
19
|
+
dispatch: Dispatch<any>,
|
20
|
+
additionalEffects: ControlsAdditionalEffects,
|
21
|
+
) => {
|
22
|
+
const {setActivePath} = additionalEffects;
|
23
|
+
|
24
|
+
return {
|
25
|
+
openPreview: () => {
|
26
|
+
dispatch(setShowPreview(true));
|
27
|
+
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
|
28
|
+
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
|
29
|
+
setActivePath(path);
|
30
|
+
},
|
31
|
+
};
|
32
|
+
};
|
33
|
+
|
34
|
+
type Controls = ReturnType<Required<NavigationTreeProps>['renderAdditionalNodeElements']>;
|
35
|
+
|
36
|
+
export const getControls =
|
37
|
+
(dispatch: Dispatch<any>, additionalEffects: ControlsAdditionalEffects) =>
|
38
|
+
(path: string, type: NavigationTreeNodeType) => {
|
39
|
+
const options = bindActions(path, dispatch, additionalEffects);
|
40
|
+
const openPreview = (
|
41
|
+
<Button
|
42
|
+
view="flat-secondary"
|
43
|
+
onClick={options.openPreview}
|
44
|
+
title={i18n('actions.openPreview')}
|
45
|
+
size="s"
|
46
|
+
>
|
47
|
+
<IconWrapper name="tablePreview" />
|
48
|
+
</Button>
|
49
|
+
);
|
50
|
+
|
51
|
+
const nodeTypeToControls: Record<NavigationTreeNodeType, Controls> = {
|
52
|
+
database: undefined,
|
53
|
+
directory: undefined,
|
54
|
+
|
55
|
+
table: openPreview,
|
56
|
+
column_table: openPreview,
|
57
|
+
|
58
|
+
index_table: undefined,
|
59
|
+
topic: undefined,
|
60
|
+
stream: undefined,
|
61
|
+
|
62
|
+
index: undefined,
|
63
|
+
|
64
|
+
external_table: openPreview,
|
65
|
+
external_data_source: undefined,
|
66
|
+
};
|
67
|
+
|
68
|
+
return nodeTypeToControls[type];
|
69
|
+
};
|
@@ -15,6 +15,9 @@
|
|
15
15
|
"settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
|
16
16
|
"settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on versions before 23-1",
|
17
17
|
|
18
|
+
"settings.useBackendParamsForTables.title": "Offload tables filters and sorting to backend",
|
19
|
+
"settings.useBackendParamsForTables.popover": "Filter and sort Nodes and Storage tables with request params. May increase performance, but could causes additional fetches and longer loading time on older versions",
|
20
|
+
|
18
21
|
"settings.enableAdditionalQueryModes.title": "Enable additional query modes",
|
19
22
|
"settings.enableAdditionalQueryModes.popover": "Adds 'Data' and 'YQL - QueryService' modes. May not work on some versions"
|
20
23
|
}
|
@@ -15,6 +15,9 @@
|
|
15
15
|
"settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
|
16
16
|
"settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на версиях до 23-1",
|
17
17
|
|
18
|
+
"settings.useBackendParamsForTables.title": "Перенести фильтры и сортировку таблиц на бэкенд",
|
19
|
+
"settings.useBackendParamsForTables.popover": "Добавляет фильтрацию и сортировку таблиц Nodes и Storage с использованием параметров запроса. Может улушить производительность, но на старых версиях может привести к дополнительным запросам и большему времени ожидания загрузки",
|
20
|
+
|
18
21
|
"settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
|
19
22
|
"settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data' и 'YQL - QueryService'. Может работать некорректно на некоторых версиях"
|
20
23
|
}
|
@@ -7,6 +7,7 @@ import {
|
|
7
7
|
ENABLE_ADDITIONAL_QUERY_MODES,
|
8
8
|
INVERTED_DISKS_KEY,
|
9
9
|
THEME_KEY,
|
10
|
+
USE_BACKEND_PARAMS_FOR_TABLES_KEY,
|
10
11
|
USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
|
11
12
|
} from '../../utils/constants';
|
12
13
|
|
@@ -58,6 +59,11 @@ export const useNodesEndpointSetting: SettingProps = {
|
|
58
59
|
title: i18n('settings.useNodesEndpoint.title'),
|
59
60
|
helpPopoverContent: i18n('settings.useNodesEndpoint.popover'),
|
60
61
|
};
|
62
|
+
export const useBackendParamsForTables: SettingProps = {
|
63
|
+
settingKey: USE_BACKEND_PARAMS_FOR_TABLES_KEY,
|
64
|
+
title: i18n('settings.useBackendParamsForTables.title'),
|
65
|
+
helpPopoverContent: i18n('settings.useBackendParamsForTables.popover'),
|
66
|
+
};
|
61
67
|
export const enableQueryModesForExplainSetting: SettingProps = {
|
62
68
|
settingKey: ENABLE_ADDITIONAL_QUERY_MODES,
|
63
69
|
title: i18n('settings.enableAdditionalQueryModes.title'),
|
@@ -72,7 +78,12 @@ export const generalSection: SettingsSection = {
|
|
72
78
|
export const experimentsSection: SettingsSection = {
|
73
79
|
id: 'experimentsSection',
|
74
80
|
title: i18n('section.experiments'),
|
75
|
-
settings: [
|
81
|
+
settings: [
|
82
|
+
invertedDisksSetting,
|
83
|
+
useNodesEndpointSetting,
|
84
|
+
useBackendParamsForTables,
|
85
|
+
enableQueryModesForExplainSetting,
|
86
|
+
],
|
76
87
|
};
|
77
88
|
|
78
89
|
export const generalPage: SettingsPage = {
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
import type {ReportHandler} from 'web-vitals';
|
2
|
+
|
3
|
+
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
2
4
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
3
5
|
import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => {
|
4
6
|
getCLS(onPerfEntry);
|
package/dist/services/api.ts
CHANGED
@@ -32,6 +32,7 @@ import type {ComputeApiRequestParams, NodesApiRequestParams} from '../store/redu
|
|
32
32
|
import type {StorageApiRequestParams} from '../store/reducers/storage/types';
|
33
33
|
|
34
34
|
import {backend as BACKEND} from '../store';
|
35
|
+
import {prepareSortValue} from '../utils/filters';
|
35
36
|
|
36
37
|
const config = {withCredentials: !window.custom_backend};
|
37
38
|
|
@@ -82,39 +83,52 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
82
83
|
});
|
83
84
|
}
|
84
85
|
getNodes(
|
85
|
-
{
|
86
|
+
{
|
87
|
+
visibleEntities,
|
88
|
+
type = 'any',
|
89
|
+
tablets = true,
|
90
|
+
sortOrder,
|
91
|
+
sortValue,
|
92
|
+
...params
|
93
|
+
}: NodesApiRequestParams,
|
86
94
|
{concurrentId}: AxiosOptions = {},
|
87
95
|
) {
|
96
|
+
const sort = prepareSortValue(sortValue, sortOrder);
|
97
|
+
|
88
98
|
return this.get<TNodesInfo>(
|
89
99
|
this.getPath('/viewer/json/nodes?enums=true'),
|
90
|
-
{
|
91
|
-
|
92
|
-
type,
|
93
|
-
tablets,
|
94
|
-
...params,
|
95
|
-
},
|
96
|
-
{
|
97
|
-
concurrentId,
|
98
|
-
},
|
100
|
+
{with: visibleEntities, type, tablets, sort, ...params},
|
101
|
+
{concurrentId},
|
99
102
|
);
|
100
103
|
}
|
101
|
-
getCompute(
|
102
|
-
|
104
|
+
getCompute(
|
105
|
+
{sortOrder, sortValue, ...params}: ComputeApiRequestParams,
|
106
|
+
{concurrentId}: AxiosOptions = {},
|
107
|
+
) {
|
108
|
+
const sort = prepareSortValue(sortValue, sortOrder);
|
109
|
+
|
110
|
+
return this.get<TComputeInfo>(
|
111
|
+
this.getPath('/viewer/json/compute?enums=true'),
|
112
|
+
{sort, ...params},
|
113
|
+
{concurrentId},
|
114
|
+
);
|
103
115
|
}
|
104
116
|
getStorageInfo(
|
105
|
-
{tenant, visibleEntities, nodeId}: StorageApiRequestParams,
|
117
|
+
{tenant, visibleEntities, nodeId, sortOrder, sortValue, ...params}: StorageApiRequestParams,
|
106
118
|
{concurrentId}: AxiosOptions = {},
|
107
119
|
) {
|
120
|
+
const sort = prepareSortValue(sortValue, sortOrder);
|
121
|
+
|
108
122
|
return this.get<TStorageInfo>(
|
109
123
|
this.getPath(`/viewer/json/storage?enums=true`),
|
110
124
|
{
|
111
125
|
tenant,
|
112
126
|
node_id: nodeId,
|
113
127
|
with: visibleEntities,
|
128
|
+
sort,
|
129
|
+
...params,
|
114
130
|
},
|
115
|
-
{
|
116
|
-
concurrentId,
|
117
|
-
},
|
131
|
+
{concurrentId},
|
118
132
|
);
|
119
133
|
}
|
120
134
|
getPdiskInfo(nodeId: string | number, pdiskId: string | number) {
|
@@ -1,5 +1,9 @@
|
|
1
|
-
import {
|
2
|
-
|
1
|
+
import type {Reducer} from 'redux';
|
2
|
+
|
3
|
+
import '../../../services/api';
|
4
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
5
|
+
|
6
|
+
import type {AuthenticationAction, AuthenticationState} from './types';
|
3
7
|
|
4
8
|
export const SET_UNAUTHENTICATED = createRequestActionTypes(
|
5
9
|
'authentication',
|
@@ -11,16 +15,19 @@ export const FETCH_USER = createRequestActionTypes('authentication', 'FETCH_USER
|
|
11
15
|
const initialState = {
|
12
16
|
isAuthenticated: true,
|
13
17
|
user: '',
|
14
|
-
error:
|
18
|
+
error: undefined,
|
15
19
|
};
|
16
20
|
|
17
|
-
const authentication
|
21
|
+
const authentication: Reducer<AuthenticationState, AuthenticationAction> = (
|
22
|
+
state = initialState,
|
23
|
+
action,
|
24
|
+
) => {
|
18
25
|
switch (action.type) {
|
19
26
|
case SET_UNAUTHENTICATED.SUCCESS: {
|
20
|
-
return {...state, isAuthenticated: false, user: '', error:
|
27
|
+
return {...state, isAuthenticated: false, user: '', error: undefined};
|
21
28
|
}
|
22
29
|
case SET_AUTHENTICATED.SUCCESS: {
|
23
|
-
return {...state, isAuthenticated: true, error:
|
30
|
+
return {...state, isAuthenticated: true, error: undefined};
|
24
31
|
}
|
25
32
|
case SET_AUTHENTICATED.FAILURE: {
|
26
33
|
return {...state, error: action.error};
|
@@ -34,7 +41,7 @@ const authentication = function (state = initialState, action) {
|
|
34
41
|
}
|
35
42
|
};
|
36
43
|
|
37
|
-
export const authenticate = (user, password) => {
|
44
|
+
export const authenticate = (user: string, password: string) => {
|
38
45
|
return createApiRequest({
|
39
46
|
request: window.api.authenticate(user, password),
|
40
47
|
actions: SET_AUTHENTICATED,
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import type {AuthErrorResponse} from '../../../types/api/error';
|
2
|
+
import type {ApiRequestAction} from '../../utils';
|
3
|
+
|
4
|
+
import {FETCH_USER, SET_AUTHENTICATED, SET_UNAUTHENTICATED} from './authentication';
|
5
|
+
|
6
|
+
export interface AuthenticationState {
|
7
|
+
isAuthenticated: boolean;
|
8
|
+
user: string | undefined;
|
9
|
+
error: AuthErrorResponse | undefined;
|
10
|
+
}
|
11
|
+
|
12
|
+
export type AuthenticationAction =
|
13
|
+
| ApiRequestAction<typeof SET_UNAUTHENTICATED, unknown, unknown>
|
14
|
+
| ApiRequestAction<typeof SET_AUTHENTICATED, unknown, AuthErrorResponse>
|
15
|
+
| ApiRequestAction<typeof FETCH_USER, string | undefined, unknown>;
|
@@ -1,9 +1,7 @@
|
|
1
|
-
import {createSelector, Selector} from 'reselect';
|
2
1
|
import {Reducer} from 'redux';
|
3
2
|
|
4
3
|
import '../../services/api';
|
5
|
-
import {
|
6
|
-
IDescribeRootStateSlice,
|
4
|
+
import type {
|
7
5
|
IDescribeState,
|
8
6
|
IDescribeAction,
|
9
7
|
IDescribeHandledResponse,
|
@@ -93,19 +91,6 @@ export const setDataWasNotLoaded = () => {
|
|
93
91
|
} as const;
|
94
92
|
};
|
95
93
|
|
96
|
-
// Consumers selectors
|
97
|
-
const selectConsumersNames = (state: IDescribeRootStateSlice, path?: string) =>
|
98
|
-
path
|
99
|
-
? state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules
|
100
|
-
: undefined;
|
101
|
-
|
102
|
-
interface IConsumer {
|
103
|
-
name: string;
|
104
|
-
}
|
105
|
-
|
106
|
-
export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[], [string | undefined]> =
|
107
|
-
createSelector(selectConsumersNames, (names = []) => names.map((name) => ({name})));
|
108
|
-
|
109
94
|
export function getDescribe({path}: {path: string}) {
|
110
95
|
const request = window.api.getDescribe({path});
|
111
96
|
return createApiRequest({
|