ydb-embedded-ui 4.13.0 → 4.15.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 +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({
|