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.
Files changed (71) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/components/Tablet/Tablet.scss +1 -16
  3. package/dist/components/Tablet/Tablet.tsx +5 -5
  4. package/dist/components/TabletIcon/TabletIcon.scss +17 -0
  5. package/dist/components/TabletIcon/TabletIcon.tsx +18 -0
  6. package/dist/containers/App/App.js +1 -1
  7. package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -1
  8. package/dist/containers/Authentication/Authentication.tsx +1 -1
  9. package/dist/containers/Header/Header.scss +2 -0
  10. package/dist/containers/Header/Header.tsx +2 -7
  11. package/dist/containers/Header/{breadcrumbs.ts → breadcrumbs.tsx} +19 -8
  12. package/dist/containers/Nodes/Nodes.tsx +53 -16
  13. package/dist/containers/Nodes/getNodesColumns.tsx +31 -13
  14. package/dist/containers/Storage/Storage.tsx +64 -32
  15. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +56 -73
  16. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +33 -43
  17. package/dist/containers/Storage/utils/index.ts +3 -3
  18. package/dist/containers/Tablet/Tablet.tsx +9 -3
  19. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.scss +8 -0
  20. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +13 -1
  21. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +3 -1
  22. package/dist/containers/Tenant/Query/i18n/en.json +6 -4
  23. package/dist/containers/Tenant/Query/i18n/ru.json +6 -4
  24. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +4 -0
  25. package/dist/containers/Tenant/i18n/en.json +3 -0
  26. package/dist/containers/Tenant/i18n/ru.json +3 -0
  27. package/dist/containers/Tenant/utils/queryTemplates.ts +89 -0
  28. package/dist/containers/Tenant/utils/schema.ts +1 -1
  29. package/dist/containers/Tenant/utils/schemaActions.ts +30 -54
  30. package/dist/containers/Tenant/utils/schemaControls.tsx +69 -0
  31. package/dist/containers/UserSettings/i18n/en.json +3 -0
  32. package/dist/containers/UserSettings/i18n/ru.json +3 -0
  33. package/dist/containers/UserSettings/settings.ts +12 -1
  34. package/dist/{reportWebVitals.js → reportWebVitals.ts} +3 -1
  35. package/dist/services/api.ts +30 -16
  36. package/dist/store/reducers/{authentication.js → authentication/authentication.ts} +14 -7
  37. package/dist/store/reducers/authentication/types.ts +15 -0
  38. package/dist/store/reducers/describe.ts +1 -16
  39. package/dist/store/reducers/header/types.ts +2 -0
  40. package/dist/store/reducers/index.ts +1 -1
  41. package/dist/store/reducers/nodes/nodes.ts +23 -6
  42. package/dist/store/reducers/nodes/selectors.ts +2 -2
  43. package/dist/store/reducers/nodes/types.ts +15 -5
  44. package/dist/store/reducers/settings/settings.ts +5 -0
  45. package/dist/store/reducers/storage/selectors.ts +50 -150
  46. package/dist/store/reducers/storage/storage.ts +73 -25
  47. package/dist/store/reducers/storage/types.ts +49 -17
  48. package/dist/store/reducers/storage/utils.ts +207 -0
  49. package/dist/store/utils.ts +1 -1
  50. package/dist/types/api/compute.ts +0 -12
  51. package/dist/types/api/error.ts +4 -0
  52. package/dist/types/api/nodes.ts +0 -12
  53. package/dist/types/api/storage.ts +32 -4
  54. package/dist/types/window.d.ts +1 -0
  55. package/dist/utils/constants.ts +3 -0
  56. package/dist/utils/filters.ts +23 -0
  57. package/dist/utils/hooks/index.ts +4 -0
  58. package/dist/utils/hooks/useNodesRequestParams.ts +46 -0
  59. package/dist/utils/hooks/useStorageRequestParams.ts +28 -0
  60. package/dist/utils/hooks/useTableSort.ts +37 -0
  61. package/dist/utils/nodes.ts +25 -0
  62. package/dist/utils/storage.ts +31 -3
  63. package/package.json +2 -6
  64. package/dist/HOCS/WithSearch/WithSearch.js +0 -26
  65. package/dist/HOCS/index.js +0 -1
  66. package/dist/components/Hotkey/Hotkey.js +0 -102
  67. package/dist/components/Pagination/Pagination.js +0 -63
  68. package/dist/components/Pagination/Pagination.scss +0 -28
  69. package/dist/types/store/storage.ts +0 -12
  70. /package/dist/{index.js → index.tsx} +0 -0
  71. /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 <span className={b()}>{parsedDuration}</span>;
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
  };
@@ -67,6 +67,8 @@
67
67
  }
68
68
 
69
69
  &__popover {
70
- max-width: 340px;
70
+ max-width: 420px;
71
+
72
+ white-space: pre-wrap;
71
73
  }
72
74
  }
@@ -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. API call: schema.scripting",
21
- "method-description.scan": "Read-only queries, potentially reading a lot of data. API call: table.ExecuteScan",
22
- "method-description.data": "DML queries for changing and fetching data in serialization mode. API call: table.executeDataQuery",
23
- "method-description.query": "Any queries. An experimental API call supposed to replace all existing methods. API Call: query.ExecuteScript"
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-конструкции. API call: schema.scripting",
21
- "method-description.scan": "Только читающие запросы, потенциально читающие много данных. API call: table.ExecuteScan",
22
- "method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable. API call: table.executeDataQuery",
23
- "method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные. API call: query.ExecuteScript"
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]: 'topic',
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
- const createTableTemplate = (path: string) => {
17
- return `CREATE TABLE \`${path}/my_table\`
18
- (
19
- \`id\` Uint64,
20
- \`name\` String,
21
- PRIMARY KEY (\`id\`)
22
- );`;
23
- };
24
- const alterTableTemplate = (path: string) => {
25
- return `ALTER TABLE \`${path}\`
26
- ADD COLUMN is_deleted Bool;`;
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
- [{text: i18n('actions.createTable'), action: actions.createTable}],
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
- [openPreview, copyItem],
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
- [openPreview, copyItem],
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: JUST_COPY,
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: [invertedDisksSetting, useNodesEndpointSetting, enableQueryModesForExplainSetting],
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
- const reportWebVitals = (onPerfEntry) => {
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);
@@ -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
- {visibleEntities, type = 'any', tablets = true, ...params}: NodesApiRequestParams,
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
- with: visibleEntities,
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(params: ComputeApiRequestParams) {
102
- return this.get<TComputeInfo>(this.getPath('/viewer/json/compute?enums=true'), params);
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 {createRequestActionTypes, createApiRequest} from '../utils';
2
- import '../../services/api';
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 = function (state = initialState, action) {
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({