ydb-embedded-ui 4.15.0 → 4.16.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 +21 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.scss +5 -0
- package/dist/components/DiagnosticCard/DiagnosticCard.tsx +17 -0
- package/dist/components/EntityStatus/EntityStatus.js +2 -2
- package/dist/components/EntityStatus/EntityStatus.scss +15 -0
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +3 -11
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +20 -11
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +14 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +26 -37
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +37 -25
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +154 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -0
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +4 -1
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +42 -11
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +1 -1
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +11 -7
- package/dist/containers/Tenant/Query/i18n/en.json +3 -0
- package/dist/containers/Tenant/Query/i18n/ru.json +3 -0
- package/dist/containers/Tenant/utils/queryTemplates.ts +30 -6
- package/dist/containers/Tenants/Tenants.tsx +3 -1
- package/dist/containers/UserSettings/Setting.tsx +9 -2
- package/dist/containers/UserSettings/i18n/en.json +5 -1
- package/dist/containers/UserSettings/i18n/ru.json +5 -1
- package/dist/containers/UserSettings/settings.ts +25 -0
- package/dist/services/api.ts +16 -16
- package/dist/store/reducers/executeQuery.ts +33 -7
- package/dist/store/reducers/explainQuery.ts +12 -4
- package/dist/store/reducers/healthcheckInfo.ts +27 -11
- package/dist/store/reducers/settings/settings.ts +4 -10
- package/dist/store/reducers/tenant/tenant.ts +19 -1
- package/dist/store/reducers/tenant/types.ts +3 -1
- package/dist/store/reducers/tenants/selectors.ts +1 -1
- package/dist/store/reducers/tenants/utils.ts +2 -2
- package/dist/types/api/tenant.ts +19 -20
- package/dist/types/store/executeQuery.ts +11 -1
- package/dist/types/store/query.ts +2 -1
- package/dist/utils/autofetcher.ts +7 -7
- package/dist/utils/constants.ts +2 -1
- package/dist/utils/hooks/i18n/en.json +1 -1
- package/dist/utils/hooks/i18n/ru.json +1 -1
- package/dist/utils/hooks/useQueryModes.ts +4 -2
- package/dist/utils/i18n/i18n.ts +10 -4
- package/dist/utils/query.ts +14 -0
- package/dist/utils/settings.ts +10 -0
- package/package.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/PreviewItem.tsx +0 -33
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +0 -213
@@ -3,10 +3,13 @@ import block from 'bem-cn-lite';
|
|
3
3
|
|
4
4
|
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
5
5
|
|
6
|
+
import type {QueryInHistory} from '../../../../types/store/executeQuery';
|
6
7
|
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
|
7
8
|
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
|
9
|
+
import {selectQueriesHistory} from '../../../../store/reducers/executeQuery';
|
8
10
|
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
|
9
|
-
import {useTypedSelector} from '../../../../utils/hooks';
|
11
|
+
import {useQueryModes, useTypedSelector} from '../../../../utils/hooks';
|
12
|
+
import {QUERY_MODES, QUERY_MODES_TITLES, QUERY_SYNTAX} from '../../../../utils/query';
|
10
13
|
import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants';
|
11
14
|
|
12
15
|
import i18n from '../i18n';
|
@@ -22,24 +25,51 @@ interface QueriesHistoryProps {
|
|
22
25
|
function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
|
23
26
|
const dispatch = useDispatch();
|
24
27
|
|
25
|
-
const
|
28
|
+
const [queryMode, setQueryMode] = useQueryModes();
|
29
|
+
|
30
|
+
const queriesHistory = useTypedSelector(selectQueriesHistory);
|
26
31
|
const reversedHistory = [...queriesHistory].reverse();
|
27
32
|
|
28
|
-
const onQueryClick = (
|
29
|
-
|
30
|
-
|
33
|
+
const onQueryClick = (query: QueryInHistory) => {
|
34
|
+
let isQueryModeSet = true;
|
35
|
+
|
36
|
+
if (query.syntax === QUERY_SYNTAX.pg && queryMode !== QUERY_MODES.pg) {
|
37
|
+
isQueryModeSet = setQueryMode(
|
38
|
+
QUERY_MODES.pg,
|
39
|
+
i18n('history.cannot-set-mode', {mode: QUERY_MODES_TITLES[QUERY_MODES.pg]}),
|
40
|
+
);
|
41
|
+
} else if (query.syntax !== QUERY_SYNTAX.pg && queryMode === QUERY_MODES.pg) {
|
42
|
+
// Set query mode for queries with yql syntax
|
43
|
+
isQueryModeSet = setQueryMode(QUERY_MODES.script);
|
44
|
+
}
|
45
|
+
|
46
|
+
if (isQueryModeSet) {
|
47
|
+
changeUserInput({input: query.queryText});
|
48
|
+
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
|
49
|
+
}
|
31
50
|
};
|
32
51
|
|
33
|
-
const columns: Column<
|
52
|
+
const columns: Column<QueryInHistory>[] = [
|
34
53
|
{
|
35
54
|
name: 'queryText',
|
36
55
|
header: 'Query Text',
|
37
|
-
render: ({row
|
38
|
-
|
39
|
-
<
|
40
|
-
|
41
|
-
|
56
|
+
render: ({row}) => {
|
57
|
+
return (
|
58
|
+
<div className={b('query')}>
|
59
|
+
<TruncatedQuery value={row.queryText} maxQueryHeight={MAX_QUERY_HEIGHT} />
|
60
|
+
</div>
|
61
|
+
);
|
62
|
+
},
|
63
|
+
sortable: false,
|
64
|
+
},
|
65
|
+
{
|
66
|
+
name: 'syntax',
|
67
|
+
header: 'Syntax',
|
68
|
+
render: ({row}) => {
|
69
|
+
return row.syntax === QUERY_SYNTAX.pg ? 'PostgreSQL' : 'YQL';
|
70
|
+
},
|
42
71
|
sortable: false,
|
72
|
+
width: 200,
|
43
73
|
},
|
44
74
|
];
|
45
75
|
|
@@ -52,6 +82,7 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
|
|
52
82
|
settings={QUERY_TABLE_SETTINGS}
|
53
83
|
emptyDataMessage={i18n('history.empty')}
|
54
84
|
onRowClick={(row) => onQueryClick(row)}
|
85
|
+
rowClassName={() => b('table-row')}
|
55
86
|
/>
|
56
87
|
</div>
|
57
88
|
);
|
@@ -264,7 +264,7 @@ function QueryEditor(props) {
|
|
264
264
|
|
265
265
|
const {queries, currentIndex} = history;
|
266
266
|
if (input !== queries[currentIndex]) {
|
267
|
-
saveQueryToHistory(input);
|
267
|
+
saveQueryToHistory(input, mode);
|
268
268
|
}
|
269
269
|
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
|
270
270
|
};
|
@@ -4,7 +4,7 @@ import {Button, ButtonView, DropdownMenu} from '@gravity-ui/uikit';
|
|
4
4
|
import {useMemo} from 'react';
|
5
5
|
|
6
6
|
import type {QueryAction, QueryMode} from '../../../../types/store/query';
|
7
|
-
import {QUERY_MODES} from '../../../../utils/query';
|
7
|
+
import {QUERY_MODES, QUERY_MODES_TITLES} from '../../../../utils/query';
|
8
8
|
import {Icon} from '../../../../components/Icon';
|
9
9
|
import {LabelWithPopover} from '../../../../components/LabelWithPopover';
|
10
10
|
|
@@ -21,32 +21,36 @@ const b = block('ydb-query-editor-controls');
|
|
21
21
|
|
22
22
|
const OldQueryModeSelectorOptions = {
|
23
23
|
[QUERY_MODES.script]: {
|
24
|
-
title:
|
24
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.script],
|
25
25
|
description: i18n('method-description.script'),
|
26
26
|
},
|
27
27
|
[QUERY_MODES.scan]: {
|
28
|
-
title:
|
28
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
|
29
29
|
description: i18n('method-description.scan'),
|
30
30
|
},
|
31
31
|
} as const;
|
32
32
|
|
33
33
|
const QueryModeSelectorOptions = {
|
34
34
|
[QUERY_MODES.script]: {
|
35
|
-
title:
|
35
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.script],
|
36
36
|
description: i18n('method-description.script'),
|
37
37
|
},
|
38
38
|
[QUERY_MODES.scan]: {
|
39
|
-
title:
|
39
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
|
40
40
|
description: i18n('method-description.scan'),
|
41
41
|
},
|
42
42
|
[QUERY_MODES.data]: {
|
43
|
-
title:
|
43
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.data],
|
44
44
|
description: i18n('method-description.data'),
|
45
45
|
},
|
46
46
|
[QUERY_MODES.query]: {
|
47
|
-
title:
|
47
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.query],
|
48
48
|
description: i18n('method-description.query'),
|
49
49
|
},
|
50
|
+
[QUERY_MODES.pg]: {
|
51
|
+
title: QUERY_MODES_TITLES[QUERY_MODES.pg],
|
52
|
+
description: i18n('method-description.pg'),
|
53
|
+
},
|
50
54
|
} as const;
|
51
55
|
|
52
56
|
interface QueryEditorControlsProps {
|
@@ -8,6 +8,8 @@
|
|
8
8
|
"history.empty": "History is empty",
|
9
9
|
"saved.empty": "There are no saved queries",
|
10
10
|
|
11
|
+
"history.cannot-set-mode": "This query is available only with '{{mode}}' query mode. You need to turn in additional query modes in settings to enable it",
|
12
|
+
|
11
13
|
"delete-dialog.header": "Delete query",
|
12
14
|
"delete-dialog.question": "Are you sure you want to delete query",
|
13
15
|
"delete-dialog.delete": "Delete",
|
@@ -21,6 +23,7 @@
|
|
21
23
|
"method-description.scan": "Read-only queries, potentially reading a lot of data.\nAPI call: table.ExecuteScan",
|
22
24
|
"method-description.data": "DML queries for changing and fetching data in serialization mode.\nAPI call: table.executeDataQuery",
|
23
25
|
"method-description.query": "Any query. An experimental API call supposed to replace all existing methods.\nAPI Call: query.ExecuteScript",
|
26
|
+
"method-description.pg": "Queries in postgresql syntax.\nAPI call: query.ExecuteScript",
|
24
27
|
|
25
28
|
"query-duration.description": "Duration of server-side query execution"
|
26
29
|
}
|
@@ -8,6 +8,8 @@
|
|
8
8
|
"history.empty": "История пуста",
|
9
9
|
"saved.empty": "Нет сохраненных запросов",
|
10
10
|
|
11
|
+
"history.cannot-set-mode": "Этот запрос доступен только в режиме '{{mode}}'. Вам необходимо включить дополнительные режимы выполнения запросов в настройках",
|
12
|
+
|
11
13
|
"delete-dialog.header": "Удалить запрос",
|
12
14
|
"delete-dialog.question": "Вы уверены что хотите удалить запрос",
|
13
15
|
"delete-dialog.delete": "Удалить",
|
@@ -21,6 +23,7 @@
|
|
21
23
|
"method-description.scan": "Только читающие запросы, потенциально читающие много данных.\nAPI call: table.ExecuteScan",
|
22
24
|
"method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable.\nAPI call: table.executeDataQuery",
|
23
25
|
"method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные.\nAPI call: query.ExecuteScript",
|
26
|
+
"method-description.pg": "Запросы в синтаксисе postgresql.\nAPI call: query.ExecuteScript",
|
24
27
|
|
25
28
|
"query-duration.description": "Время выполнения запроса на стороне сервера"
|
26
29
|
}
|
@@ -1,10 +1,34 @@
|
|
1
1
|
export const createTableTemplate = (path: string) => {
|
2
|
-
return
|
3
|
-
(
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_table
|
3
|
+
CREATE TABLE \`${path}/ydb_row_table\` (
|
4
|
+
category_id Uint64 NOT NULL,
|
5
|
+
id Uint64,
|
6
|
+
expire_at Datetime,
|
7
|
+
updated_on Datetime,
|
8
|
+
name Text,
|
9
|
+
\`binary-payload\` Bytes,
|
10
|
+
attributes JsonDocument,
|
11
|
+
-- uncomment to add a secondary index
|
12
|
+
-- INDEX idx_row_table_id GLOBAL SYNC ON ( id ) COVER ( name, attributes ), -- Secondary indexes docs https://ydb.tech/en/docs/yql/reference/syntax/create_table#secondary_index
|
13
|
+
PRIMARY KEY (category_id, id)
|
14
|
+
)
|
15
|
+
WITH (
|
16
|
+
AUTO_PARTITIONING_BY_SIZE = ENABLED,
|
17
|
+
AUTO_PARTITIONING_PARTITION_SIZE_MB = 2048,
|
18
|
+
AUTO_PARTITIONING_BY_LOAD = ENABLED,
|
19
|
+
AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = 4,
|
20
|
+
AUTO_PARTITIONING_MAX_PARTITIONS_COUNT = 1024,
|
21
|
+
-- uncomment to create a table with predefined partitions
|
22
|
+
-- UNIFORM_PARTITIONS = 4, -- The number of partitions for uniform initial table partitioning.
|
23
|
+
-- The primary key's first column must have type Uint64 or Uint32.
|
24
|
+
-- A created table is immediately divided into the specified number of partitions
|
25
|
+
-- uncomment to launch read only replicas in every AZ
|
26
|
+
-- READ_REPLICAS_SETTINGS = 'PER_AZ:1', -- Enable read replicas for stale read, launch one replica in every availability zone
|
27
|
+
-- uncomment to enable ttl
|
28
|
+
-- TTL = Interval("PT1H") ON expire_at, -- Enable background deletion of expired rows https://ydb.tech/en/docs/concepts/ttl
|
29
|
+
KEY_BLOOM_FILTER = ENABLED -- With a Bloom filter, you can more efficiently determine
|
30
|
+
-- if some keys are missing in a table when making multiple single queries by the primary key.
|
31
|
+
)`;
|
8
32
|
};
|
9
33
|
export const alterTableTemplate = (path: string) => {
|
10
34
|
return `ALTER TABLE \`${path}\`
|
@@ -106,7 +106,9 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
|
|
106
106
|
backend,
|
107
107
|
})}
|
108
108
|
/>
|
109
|
-
{
|
109
|
+
{row.Name &&
|
110
|
+
row.Type &&
|
111
|
+
additionalTenantsProps?.getMonitoringLink?.(row.Name, row.Type)}
|
110
112
|
</div>
|
111
113
|
);
|
112
114
|
},
|
@@ -18,6 +18,7 @@ export interface SettingProps {
|
|
18
18
|
helpPopoverContent?: ReactNode;
|
19
19
|
options?: {value: string; content: string}[];
|
20
20
|
defaultValue?: unknown;
|
21
|
+
onValueUpdate?: VoidFunction;
|
21
22
|
}
|
22
23
|
|
23
24
|
export const Setting = ({
|
@@ -27,9 +28,15 @@ export const Setting = ({
|
|
27
28
|
helpPopoverContent,
|
28
29
|
options,
|
29
30
|
defaultValue,
|
31
|
+
onValueUpdate,
|
30
32
|
}: SettingProps) => {
|
31
33
|
const [settingValue, setValue] = useSetting(settingKey, defaultValue);
|
32
34
|
|
35
|
+
const onUpdate = (value: unknown) => {
|
36
|
+
setValue(value);
|
37
|
+
onValueUpdate?.();
|
38
|
+
};
|
39
|
+
|
33
40
|
const renderTitleComponent = (value: ReactNode) => {
|
34
41
|
if (helpPopoverContent) {
|
35
42
|
return (
|
@@ -48,7 +55,7 @@ export const Setting = ({
|
|
48
55
|
const getSettingsElement = (elementType: SettingsElementType) => {
|
49
56
|
switch (elementType) {
|
50
57
|
case 'switch': {
|
51
|
-
return <Switch checked={Boolean(settingValue)} onUpdate={
|
58
|
+
return <Switch checked={Boolean(settingValue)} onUpdate={onUpdate} />;
|
52
59
|
}
|
53
60
|
|
54
61
|
case 'radio': {
|
@@ -57,7 +64,7 @@ export const Setting = ({
|
|
57
64
|
}
|
58
65
|
|
59
66
|
return (
|
60
|
-
<RadioButton value={String(settingValue)} onUpdate={
|
67
|
+
<RadioButton value={String(settingValue)} onUpdate={onUpdate}>
|
61
68
|
{options.map(({value, content}) => {
|
62
69
|
return (
|
63
70
|
<RadioButton.Option value={value} key={value}>
|
@@ -10,6 +10,10 @@
|
|
10
10
|
"settings.theme.option-light": "Light",
|
11
11
|
"settings.theme.option-system": "System",
|
12
12
|
|
13
|
+
"settings.language.title": "Interface language",
|
14
|
+
"settings.language.option-russian": "Russian",
|
15
|
+
"settings.language.option-english": "English",
|
16
|
+
|
13
17
|
"settings.invertedDisks.title": "Inverted disks space indicators",
|
14
18
|
|
15
19
|
"settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
|
@@ -19,5 +23,5 @@
|
|
19
23
|
"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
24
|
|
21
25
|
"settings.enableAdditionalQueryModes.title": "Enable additional query modes",
|
22
|
-
"settings.enableAdditionalQueryModes.popover": "Adds 'Data'
|
26
|
+
"settings.enableAdditionalQueryModes.popover": "Adds 'Data', 'YQL - QueryService' and 'PostgreSQL' modes. May not work on some versions"
|
23
27
|
}
|
@@ -10,6 +10,10 @@
|
|
10
10
|
"settings.theme.option-light": "Светлая",
|
11
11
|
"settings.theme.option-system": "Системная",
|
12
12
|
|
13
|
+
"settings.language.title": "Язык интерфейса",
|
14
|
+
"settings.language.option-russian": "Русский",
|
15
|
+
"settings.language.option-english": "English",
|
16
|
+
|
13
17
|
"settings.invertedDisks.title": "Инвертированные индикаторы места на дисках",
|
14
18
|
|
15
19
|
"settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
|
@@ -19,5 +23,5 @@
|
|
19
23
|
"settings.useBackendParamsForTables.popover": "Добавляет фильтрацию и сортировку таблиц Nodes и Storage с использованием параметров запроса. Может улушить производительность, но на старых версиях может привести к дополнительным запросам и большему времени ожидания загрузки",
|
20
24
|
|
21
25
|
"settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
|
22
|
-
"settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data'
|
26
|
+
"settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'Data', 'YQL - QueryService' и 'PostgreSQL'. Может работать некорректно на некоторых версиях"
|
23
27
|
}
|
@@ -6,10 +6,12 @@ import flaskIcon from '../../assets/icons/flask.svg';
|
|
6
6
|
import {
|
7
7
|
ENABLE_ADDITIONAL_QUERY_MODES,
|
8
8
|
INVERTED_DISKS_KEY,
|
9
|
+
LANGUAGE_KEY,
|
9
10
|
THEME_KEY,
|
10
11
|
USE_BACKEND_PARAMS_FOR_TABLES_KEY,
|
11
12
|
USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
|
12
13
|
} from '../../utils/constants';
|
14
|
+
import {Lang, defaultLang} from '../../utils/i18n';
|
13
15
|
|
14
16
|
import type {SettingProps} from './Setting';
|
15
17
|
import i18n from './i18n';
|
@@ -50,6 +52,29 @@ export const themeSetting: SettingProps = {
|
|
50
52
|
type: 'radio',
|
51
53
|
options: themeOptions,
|
52
54
|
};
|
55
|
+
|
56
|
+
const languageOptions = [
|
57
|
+
{
|
58
|
+
value: Lang.Ru,
|
59
|
+
content: i18n('settings.language.option-russian'),
|
60
|
+
},
|
61
|
+
{
|
62
|
+
value: Lang.En,
|
63
|
+
content: i18n('settings.language.option-english'),
|
64
|
+
},
|
65
|
+
];
|
66
|
+
|
67
|
+
export const languageSetting: SettingProps = {
|
68
|
+
settingKey: LANGUAGE_KEY,
|
69
|
+
title: i18n('settings.language.title'),
|
70
|
+
type: 'radio',
|
71
|
+
options: languageOptions,
|
72
|
+
defaultValue: defaultLang,
|
73
|
+
onValueUpdate: () => {
|
74
|
+
window.location.reload();
|
75
|
+
},
|
76
|
+
};
|
77
|
+
|
53
78
|
export const invertedDisksSetting: SettingProps = {
|
54
79
|
settingKey: INVERTED_DISKS_KEY,
|
55
80
|
title: i18n('settings.invertedDisks.title'),
|
package/dist/services/api.ts
CHANGED
@@ -28,6 +28,7 @@ import type {DescribeTopicResult} from '../types/api/topic';
|
|
28
28
|
import type {TEvPDiskStateResponse} from '../types/api/pdisk';
|
29
29
|
import type {TEvVDiskStateResponse} from '../types/api/vdisk';
|
30
30
|
import type {TUserToken} from '../types/api/whoami';
|
31
|
+
import type {QuerySyntax} from '../types/store/query';
|
31
32
|
import type {ComputeApiRequestParams, NodesApiRequestParams} from '../store/reducers/nodes/types';
|
32
33
|
import type {StorageApiRequestParams} from '../store/reducers/storage/types';
|
33
34
|
|
@@ -75,12 +76,16 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
75
76
|
cluster_name: clusterName,
|
76
77
|
});
|
77
78
|
}
|
78
|
-
getTenantInfo({path}: {path: string}) {
|
79
|
-
return this.get<TTenantInfo>(
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
getTenantInfo({path}: {path: string}, {concurrentId}: AxiosOptions = {}) {
|
80
|
+
return this.get<TTenantInfo>(
|
81
|
+
this.getPath('/viewer/json/tenantinfo'),
|
82
|
+
{
|
83
|
+
path,
|
84
|
+
tablets: true,
|
85
|
+
storage: true,
|
86
|
+
},
|
87
|
+
{concurrentId: concurrentId || `getTenantInfo|${path}`},
|
88
|
+
);
|
84
89
|
}
|
85
90
|
getNodes(
|
86
91
|
{
|
@@ -281,17 +286,15 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
281
286
|
}
|
282
287
|
sendQuery<Action extends Actions, Schema extends Schemas = undefined>(
|
283
288
|
{
|
284
|
-
query,
|
285
|
-
database,
|
286
|
-
action,
|
287
|
-
stats,
|
288
289
|
schema,
|
290
|
+
...params
|
289
291
|
}: {
|
290
292
|
query?: string;
|
291
293
|
database?: string;
|
292
294
|
action?: Action;
|
293
295
|
stats?: string;
|
294
296
|
schema?: Schema;
|
297
|
+
syntax?: QuerySyntax;
|
295
298
|
},
|
296
299
|
{concurrentId}: AxiosOptions = {},
|
297
300
|
) {
|
@@ -303,12 +306,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
303
306
|
this.getPath(
|
304
307
|
`/viewer/json/query?timeout=${backendTimeout}${schema ? `&schema=${schema}` : ''}`,
|
305
308
|
),
|
306
|
-
|
307
|
-
query,
|
308
|
-
database,
|
309
|
-
action,
|
310
|
-
stats,
|
311
|
-
},
|
309
|
+
params,
|
312
310
|
{},
|
313
311
|
{
|
314
312
|
concurrentId,
|
@@ -320,6 +318,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
320
318
|
query: string,
|
321
319
|
database: string,
|
322
320
|
action: Action,
|
321
|
+
syntax?: QuerySyntax,
|
323
322
|
) {
|
324
323
|
return this.post<ExplainResponse<Action>>(
|
325
324
|
this.getPath('/viewer/json/query'),
|
@@ -327,6 +326,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
327
326
|
query,
|
328
327
|
database,
|
329
328
|
action: action || 'explain',
|
329
|
+
syntax,
|
330
330
|
timeout: 600000,
|
331
331
|
},
|
332
332
|
{},
|
@@ -4,12 +4,14 @@ import type {ExecuteActions} from '../../types/api/query';
|
|
4
4
|
import type {
|
5
5
|
ExecuteQueryAction,
|
6
6
|
ExecuteQueryState,
|
7
|
+
ExecuteQueryStateSlice,
|
7
8
|
MonacoHotKeyAction,
|
9
|
+
QueryInHistory,
|
8
10
|
} from '../../types/store/executeQuery';
|
9
|
-
import type {QueryRequestParams, QueryMode} from '../../types/store/query';
|
11
|
+
import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query';
|
10
12
|
import {getValueFromLS, parseJson} from '../../utils/utils';
|
11
13
|
import {QUERIES_HISTORY_KEY} from '../../utils/constants';
|
12
|
-
import {parseQueryAPIExecuteResponse} from '../../utils/query';
|
14
|
+
import {QUERY_MODES, QUERY_SYNTAX, parseQueryAPIExecuteResponse} from '../../utils/query';
|
13
15
|
import {parseQueryError} from '../../utils/error';
|
14
16
|
import '../../services/api';
|
15
17
|
|
@@ -87,8 +89,12 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
|
|
87
89
|
}
|
88
90
|
|
89
91
|
case SAVE_QUERY_TO_HISTORY: {
|
90
|
-
const
|
91
|
-
|
92
|
+
const queryText = action.data.queryText;
|
93
|
+
|
94
|
+
// Do not save explicit yql syntax value for easier further support (use yql by default)
|
95
|
+
const syntax = action.data.mode === QUERY_MODES.pg ? QUERY_SYNTAX.pg : undefined;
|
96
|
+
|
97
|
+
const newQueries = [...state.history.queries, {queryText, syntax}].slice(
|
92
98
|
state.history.queries.length >= MAXIMUM_QUERIES_IN_HISTORY ? 1 : 0,
|
93
99
|
);
|
94
100
|
window.localStorage.setItem(QUERIES_HISTORY_KEY, JSON.stringify(newQueries));
|
@@ -151,7 +157,15 @@ interface SendQueryParams extends QueryRequestParams {
|
|
151
157
|
}
|
152
158
|
|
153
159
|
export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
|
154
|
-
|
160
|
+
let action: ExecuteActions = 'execute';
|
161
|
+
let syntax: QuerySyntax = QUERY_SYNTAX.yql;
|
162
|
+
|
163
|
+
if (mode === 'pg') {
|
164
|
+
action = 'execute-query';
|
165
|
+
syntax = QUERY_SYNTAX.pg;
|
166
|
+
} else if (mode) {
|
167
|
+
action = `execute-${mode}`;
|
168
|
+
}
|
155
169
|
|
156
170
|
return createApiRequest({
|
157
171
|
request: window.api.sendQuery({
|
@@ -159,6 +173,7 @@ export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
|
|
159
173
|
query,
|
160
174
|
database,
|
161
175
|
action,
|
176
|
+
syntax,
|
162
177
|
stats: 'profile',
|
163
178
|
}),
|
164
179
|
actions: SEND_QUERY,
|
@@ -166,10 +181,10 @@ export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
|
|
166
181
|
});
|
167
182
|
};
|
168
183
|
|
169
|
-
export const saveQueryToHistory = (
|
184
|
+
export const saveQueryToHistory = (queryText: string, mode: QueryMode) => {
|
170
185
|
return {
|
171
186
|
type: SAVE_QUERY_TO_HISTORY,
|
172
|
-
data:
|
187
|
+
data: {queryText, mode},
|
173
188
|
} as const;
|
174
189
|
};
|
175
190
|
|
@@ -206,4 +221,15 @@ export const setTenantPath = (value: string) => {
|
|
206
221
|
} as const;
|
207
222
|
};
|
208
223
|
|
224
|
+
export const selectQueriesHistory = (state: ExecuteQueryStateSlice): QueryInHistory[] => {
|
225
|
+
return state.executeQuery.history.queries.map((rawQuery) => {
|
226
|
+
if (typeof rawQuery === 'string') {
|
227
|
+
return {
|
228
|
+
queryText: rawQuery,
|
229
|
+
};
|
230
|
+
}
|
231
|
+
return rawQuery;
|
232
|
+
});
|
233
|
+
};
|
234
|
+
|
209
235
|
export default executeQuery;
|
@@ -9,10 +9,10 @@ import type {
|
|
9
9
|
ExplainQueryState,
|
10
10
|
PreparedExplainResponse,
|
11
11
|
} from '../../types/store/explainQuery';
|
12
|
-
import type {QueryRequestParams, QueryMode} from '../../types/store/query';
|
12
|
+
import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query';
|
13
13
|
|
14
14
|
import {preparePlan} from '../../utils/prepareQueryExplain';
|
15
|
-
import {parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../utils/query';
|
15
|
+
import {QUERY_SYNTAX, parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../utils/query';
|
16
16
|
import {parseQueryError} from '../../utils/error';
|
17
17
|
|
18
18
|
import {createRequestActionTypes, createApiRequest} from '../utils';
|
@@ -103,10 +103,18 @@ interface ExplainQueryParams extends QueryRequestParams {
|
|
103
103
|
}
|
104
104
|
|
105
105
|
export const getExplainQuery = ({query, database, mode}: ExplainQueryParams) => {
|
106
|
-
|
106
|
+
let action: ExplainActions = 'explain';
|
107
|
+
let syntax: QuerySyntax = QUERY_SYNTAX.yql;
|
108
|
+
|
109
|
+
if (mode === 'pg') {
|
110
|
+
action = 'explain-query';
|
111
|
+
syntax = QUERY_SYNTAX.pg;
|
112
|
+
} else if (mode) {
|
113
|
+
action = `explain-${mode}`;
|
114
|
+
}
|
107
115
|
|
108
116
|
return createApiRequest({
|
109
|
-
request: window.api.getExplainQuery(query, database, action),
|
117
|
+
request: window.api.getExplainQuery(query, database, action, syntax),
|
110
118
|
actions: GET_EXPLAIN_QUERY,
|
111
119
|
dataHandler: (response): PreparedExplainResponse => {
|
112
120
|
const {plan: rawPlan, ast} = parseQueryAPIExplainResponse(response);
|
@@ -1,17 +1,16 @@
|
|
1
1
|
import _flow from 'lodash/fp/flow';
|
2
2
|
import _sortBy from 'lodash/fp/sortBy';
|
3
3
|
import _uniqBy from 'lodash/fp/uniqBy';
|
4
|
-
import _omit from 'lodash/omit';
|
5
4
|
import {createSelector, Selector} from 'reselect';
|
6
5
|
import {Reducer} from 'redux';
|
7
6
|
|
8
|
-
import {
|
9
|
-
|
7
|
+
import type {
|
8
|
+
IHealthCheckInfoAction,
|
10
9
|
IHealthcheckInfoRootStateSlice,
|
10
|
+
IHealthcheckInfoState,
|
11
11
|
IIssuesTree,
|
12
|
-
IHealthCheckInfoAction,
|
13
12
|
} from '../../types/store/healthcheck';
|
14
|
-
import {IssueLog, StatusFlag} from '../../types/api/healthcheck';
|
13
|
+
import type {IssueLog, StatusFlag} from '../../types/api/healthcheck';
|
15
14
|
|
16
15
|
import '../../services/api';
|
17
16
|
import {createRequestActionTypes, createApiRequest} from '../utils';
|
@@ -49,6 +48,8 @@ const healthcheckInfo: Reducer<IHealthcheckInfoState, IHealthCheckInfoAction> =
|
|
49
48
|
...state,
|
50
49
|
error: action.error,
|
51
50
|
loading: false,
|
51
|
+
wasLoaded: true,
|
52
|
+
data: undefined,
|
52
53
|
};
|
53
54
|
}
|
54
55
|
|
@@ -110,6 +111,24 @@ const getInvertedConsequencesTree = ({
|
|
110
111
|
: [];
|
111
112
|
};
|
112
113
|
|
114
|
+
const getIssuesStatistics = (data: IssueLog[]): [StatusFlag, number][] => {
|
115
|
+
const issuesMap = {} as Record<StatusFlag, number>;
|
116
|
+
|
117
|
+
for (const issue of data) {
|
118
|
+
if (!issuesMap[issue.status]) {
|
119
|
+
issuesMap[issue.status] = 0;
|
120
|
+
}
|
121
|
+
issuesMap[issue.status]++;
|
122
|
+
}
|
123
|
+
|
124
|
+
return (Object.entries(issuesMap) as [StatusFlag, number][]).sort(([aStatus], [bStatus]) => {
|
125
|
+
const bPriority = mapStatusToPriority[bStatus] || 0;
|
126
|
+
const aPriority = mapStatusToPriority[aStatus] || 0;
|
127
|
+
|
128
|
+
return aPriority - bPriority;
|
129
|
+
});
|
130
|
+
};
|
131
|
+
|
113
132
|
const getIssuesLog = (state: IHealthcheckInfoRootStateSlice) =>
|
114
133
|
state.healthcheckInfo.data?.issue_log;
|
115
134
|
|
@@ -121,13 +140,10 @@ export const selectIssuesTrees: Selector<IHealthcheckInfoRootStateSlice, IIssues
|
|
121
140
|
return getInvertedConsequencesTree({data, roots});
|
122
141
|
});
|
123
142
|
|
124
|
-
export const
|
143
|
+
export const selectIssuesStatistics: Selector<
|
125
144
|
IHealthcheckInfoRootStateSlice,
|
126
|
-
|
127
|
-
|
128
|
-
> = createSelector([selectIssuesTrees, (_, id: string | undefined) => id], (issuesTrees = [], id) =>
|
129
|
-
issuesTrees.find((issuesTree) => issuesTree.id === id),
|
130
|
-
);
|
145
|
+
[StatusFlag, number][]
|
146
|
+
> = createSelector(getIssuesLog, (issues = []) => getIssuesStatistics(issues));
|
131
147
|
|
132
148
|
export function getHealthcheckInfo(database: string) {
|
133
149
|
return createApiRequest({
|