ydb-embedded-ui 4.15.1 → 4.16.1

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +2 -2
  3. package/dist/components/DiagnosticCard/DiagnosticCard.scss +5 -0
  4. package/dist/components/DiagnosticCard/DiagnosticCard.tsx +17 -0
  5. package/dist/components/EntityStatus/EntityStatus.js +2 -2
  6. package/dist/components/EntityStatus/EntityStatus.scss +15 -0
  7. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +2 -1
  8. package/dist/containers/Cluster/Cluster.tsx +2 -2
  9. package/dist/containers/Node/Node.tsx +3 -1
  10. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +3 -1
  11. package/dist/containers/Node/NodeStructure/Pdisk.tsx +2 -2
  12. package/dist/containers/Nodes/Nodes.tsx +3 -2
  13. package/dist/containers/Nodes/getNodesColumns.tsx +3 -1
  14. package/dist/containers/Storage/Storage.tsx +3 -2
  15. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -2
  16. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +5 -12
  17. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -2
  18. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +20 -11
  19. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +14 -1
  20. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +26 -37
  21. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +37 -25
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +155 -0
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +7 -0
  24. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/index.ts +11 -0
  25. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +7 -0
  26. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +3 -2
  27. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +4 -1
  28. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +42 -11
  29. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +1 -1
  30. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +11 -7
  31. package/dist/containers/Tenant/Query/i18n/en.json +3 -0
  32. package/dist/containers/Tenant/Query/i18n/ru.json +3 -0
  33. package/dist/containers/Tenant/Tenant.tsx +3 -2
  34. package/dist/containers/UserSettings/Setting.tsx +9 -2
  35. package/dist/containers/UserSettings/i18n/en.json +5 -1
  36. package/dist/containers/UserSettings/i18n/ru.json +5 -1
  37. package/dist/containers/UserSettings/settings.ts +25 -0
  38. package/dist/services/api.ts +16 -16
  39. package/dist/store/reducers/executeQuery.ts +33 -7
  40. package/dist/store/reducers/explainQuery.ts +12 -4
  41. package/dist/store/reducers/healthcheckInfo.ts +27 -11
  42. package/dist/store/reducers/settings/settings.ts +4 -10
  43. package/dist/store/reducers/tenant/tenant.ts +19 -1
  44. package/dist/store/reducers/tenant/types.ts +3 -1
  45. package/dist/store/reducers/tenants/selectors.ts +1 -1
  46. package/dist/store/reducers/tenants/utils.ts +2 -2
  47. package/dist/types/additionalProps.ts +8 -1
  48. package/dist/types/api/tenant.ts +19 -20
  49. package/dist/types/store/executeQuery.ts +11 -1
  50. package/dist/types/store/query.ts +2 -1
  51. package/dist/utils/autofetcher.ts +7 -7
  52. package/dist/utils/constants.ts +2 -1
  53. package/dist/utils/hooks/i18n/en.json +1 -1
  54. package/dist/utils/hooks/i18n/ru.json +1 -1
  55. package/dist/utils/hooks/useQueryModes.ts +4 -2
  56. package/dist/utils/i18n/i18n.ts +10 -4
  57. package/dist/utils/nodes.ts +0 -6
  58. package/dist/utils/query.ts +14 -0
  59. package/dist/utils/settings.ts +10 -0
  60. package/package.json +1 -1
  61. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/PreviewItem.tsx +0 -33
  62. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +0 -1
  63. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +0 -213
@@ -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>(this.getPath('/viewer/json/tenantinfo'), {
80
- path,
81
- tablets: true,
82
- storage: true,
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 query = action.data;
91
- const newQueries = [...state.history.queries, query].slice(
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
- const action: ExecuteActions = mode ? `execute-${mode}` : 'execute';
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 = (query: string) => {
184
+ export const saveQueryToHistory = (queryText: string, mode: QueryMode) => {
170
185
  return {
171
186
  type: SAVE_QUERY_TO_HISTORY,
172
- data: query,
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
- const action: ExplainActions = mode ? `explain-${mode}` : 'explain';
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
- IHealthcheckInfoState,
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 selectIssuesTreeById: Selector<
143
+ export const selectIssuesStatistics: Selector<
125
144
  IHealthcheckInfoRootStateSlice,
126
- IIssuesTree | undefined,
127
- [string | undefined]
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({
@@ -14,10 +14,12 @@ import {
14
14
  CLUSTER_INFO_HIDDEN_KEY,
15
15
  LAST_USED_QUERY_ACTION_KEY,
16
16
  USE_BACKEND_PARAMS_FOR_TABLES_KEY,
17
+ LANGUAGE_KEY,
17
18
  } from '../../../utils/constants';
18
19
  import '../../../services/api';
19
- import {getValueFromLS, parseJson} from '../../../utils/utils';
20
+ import {parseJson} from '../../../utils/utils';
20
21
  import {QUERY_ACTIONS, QUERY_MODES} from '../../../utils/query';
22
+ import {readSavedSettingsValue, systemSettings, userSettings} from '../../../utils/settings';
21
23
 
22
24
  import {TENANT_PAGES_IDS} from '../tenant/constants';
23
25
 
@@ -38,20 +40,12 @@ export const ProblemFilterValues = {
38
40
  PROBLEMS: 'With problems',
39
41
  } as const;
40
42
 
41
- const userSettings = window.userSettings || {};
42
- const systemSettings = window.systemSettings || {};
43
-
44
- export function readSavedSettingsValue(key: string, defaultValue?: string) {
45
- const savedValue = window.web_version ? userSettings[key] : getValueFromLS(key);
46
-
47
- return savedValue ?? defaultValue;
48
- }
49
-
50
43
  export const initialState = {
51
44
  problemFilter: ProblemFilterValues.ALL,
52
45
  userSettings: {
53
46
  ...userSettings,
54
47
  [THEME_KEY]: readSavedSettingsValue(THEME_KEY, 'system'),
48
+ [LANGUAGE_KEY]: readSavedSettingsValue(LANGUAGE_KEY),
55
49
  [INVERTED_DISKS_KEY]: readSavedSettingsValue(INVERTED_DISKS_KEY, 'false'),
56
50
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: readSavedSettingsValue(
57
51
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
@@ -20,6 +20,7 @@ const SET_QUERY_TAB = 'tenant/SET_QUERY_TAB';
20
20
  const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
21
21
  const SET_SUMMARY_TAB = 'tenant/SET_SUMMARY_TAB';
22
22
  const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
23
+ const SET_DATA_WAS_NOT_LOADED = 'tenant/SET_DATA_WAS_NOT_LOADED';
23
24
 
24
25
  const initialState = {loading: false, wasLoaded: false};
25
26
 
@@ -43,6 +44,10 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
43
44
  }
44
45
 
45
46
  case FETCH_TENANT.FAILURE: {
47
+ if (action.error?.isCancelled) {
48
+ return state;
49
+ }
50
+
46
51
  return {
47
52
  ...state,
48
53
  error: action.error,
@@ -84,6 +89,13 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
84
89
  };
85
90
  }
86
91
 
92
+ case SET_DATA_WAS_NOT_LOADED: {
93
+ return {
94
+ ...state,
95
+ wasLoaded: false,
96
+ };
97
+ }
98
+
87
99
  default:
88
100
  return state;
89
101
  }
@@ -91,7 +103,7 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
91
103
 
92
104
  export const getTenantInfo = ({path}: {path: string}) => {
93
105
  return createApiRequest({
94
- request: window.api.getTenantInfo({path}),
106
+ request: window.api.getTenantInfo({path}, {concurrentId: 'getTenantInfo'}),
95
107
  actions: FETCH_TENANT,
96
108
  dataHandler: (tenantData): TTenant | undefined => {
97
109
  return tenantData.TenantInfo?.[0];
@@ -131,4 +143,10 @@ export function setSummaryTab(tab: TenantSummaryTab) {
131
143
  } as const;
132
144
  }
133
145
 
146
+ export const setDataWasNotLoaded = () => {
147
+ return {
148
+ type: SET_DATA_WAS_NOT_LOADED,
149
+ } as const;
150
+ };
151
+
134
152
  export default tenantReducer;
@@ -16,6 +16,7 @@ import {
16
16
  setQueryTab,
17
17
  setSummaryTab,
18
18
  setTenantPage,
19
+ setDataWasNotLoaded,
19
20
  } from './tenant';
20
21
 
21
22
  export type TenantPage = ValueOf<typeof TENANT_PAGES_IDS>;
@@ -41,4 +42,5 @@ export type TenantAction =
41
42
  | ReturnType<typeof setTenantPage>
42
43
  | ReturnType<typeof setQueryTab>
43
44
  | ReturnType<typeof setDiagnosticsTab>
44
- | ReturnType<typeof setSummaryTab>;
45
+ | ReturnType<typeof setSummaryTab>
46
+ | ReturnType<typeof setDataWasNotLoaded>;
@@ -24,7 +24,7 @@ const filterTenantsByProblems = (tenants: PreparedTenant[], problemFilter: Probl
24
24
  const filteredTenantsBySearch = (tenants: PreparedTenant[], searchQuery: string) => {
25
25
  return tenants.filter((item) => {
26
26
  const re = new RegExp(escapeRegExp(searchQuery), 'i');
27
- return re.test(item.Name) || re.test(item.controlPlaneName);
27
+ return re.test(item.Name || '') || re.test(item.controlPlaneName);
28
28
  });
29
29
  };
30
30
 
@@ -2,8 +2,8 @@ import type {TTenant} from '../../../types/api/tenant';
2
2
  import {isNumeric} from '../../../utils/utils';
3
3
 
4
4
  const getControlPlaneValue = (tenant: TTenant) => {
5
- const parts = tenant.Name.split('/');
6
- const defaultValue = parts.length ? parts[parts.length - 1] : '—';
5
+ const parts = tenant.Name?.split('/');
6
+ const defaultValue = parts?.length ? parts[parts.length - 1] : '—';
7
7
  const controlPlaneName = tenant.ControlPlane?.name;
8
8
 
9
9
  return controlPlaneName ?? defaultValue;
@@ -2,6 +2,7 @@ import type {ReactNode} from 'react';
2
2
 
3
3
  import type {InfoViewerItem} from '../components/InfoViewer';
4
4
  import type {ETenantType} from './api/tenant';
5
+ import type {TSystemStateInfo} from './api/nodes';
5
6
  import type {VersionToColorMap} from './versions';
6
7
 
7
8
  export interface AdditionalVersionsProps {
@@ -20,5 +21,11 @@ export interface AdditionalClusterProps {
20
21
 
21
22
  export interface AdditionalTenantsProps {
22
23
  prepareTenantBackend?: (backend: string | undefined) => string | undefined;
23
- getMonitoringLink?: (name: string, type: ETenantType) => ReactNode;
24
+ getMonitoringLink?: (name?: string, type?: ETenantType) => ReactNode;
25
+ }
26
+
27
+ export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints'>;
28
+
29
+ export interface AdditionalNodesProps extends Record<string, unknown> {
30
+ getNodeRef?: (node?: NodeAddress) => string | null;
24
31
  }
@@ -22,38 +22,38 @@ export interface TTenantInfo {
22
22
  }
23
23
 
24
24
  export interface TTenant {
25
- Name: string;
26
- Id: string;
27
- Type: ETenantType;
28
- State: State;
25
+ Name?: string;
26
+ Id?: string;
27
+ Type?: ETenantType;
28
+ State?: State;
29
29
  StateStats?: THiveDomainStatsStateCount[];
30
- Metrics: TMetrics;
30
+ Metrics?: TMetrics;
31
31
  NodeIds?: number[];
32
- AliveNodes: number;
33
- Resources: TTenantResources;
32
+ AliveNodes?: number;
33
+ Resources?: TTenantResources;
34
34
  /** uint64 */
35
- CreateTime: string;
36
- Owner: string;
35
+ CreateTime?: string;
36
+ Owner?: string;
37
37
  Users?: string[];
38
38
  PoolStats?: TPoolStats[];
39
- UserAttributes: Record<string, string>;
40
- Overall: EFlag;
39
+ UserAttributes?: Record<string, string>;
40
+ Overall?: EFlag;
41
41
  SystemTablets?: TTabletStateInfo[];
42
- ResourceId: string;
42
+ ResourceId?: string;
43
43
  Tablets?: TTabletStateInfo[];
44
44
  /** uint64 */
45
- StorageAllocatedSize: string;
45
+ StorageAllocatedSize?: string;
46
46
  /** uint64 */
47
- StorageMinAvailableSize: string;
47
+ StorageMinAvailableSize?: string;
48
48
  Nodes?: TSystemStateInfo[];
49
49
  /** uint64 */
50
- MemoryUsed: string;
50
+ MemoryUsed?: string;
51
51
  /** uint64 */
52
- MemoryLimit: string;
52
+ MemoryLimit?: string;
53
53
  /** double */
54
- CoresUsed: number;
54
+ CoresUsed?: number;
55
55
  /** uint64 */
56
- StorageGroups: string;
56
+ StorageGroups?: string;
57
57
 
58
58
  MonitoringEndpoint?: string; // additional
59
59
  ControlPlane?: ControlPlane; // additional
@@ -139,11 +139,10 @@ enum State {
139
139
  'CONFIGURING' = 'CONFIGURING',
140
140
  }
141
141
 
142
- enum ETabletVolatileState {
142
+ export enum ETabletVolatileState {
143
143
  'TABLET_VOLATILE_STATE_UNKNOWN' = 'TABLET_VOLATILE_STATE_UNKNOWN',
144
144
  'TABLET_VOLATILE_STATE_STOPPED' = 'TABLET_VOLATILE_STATE_STOPPED',
145
145
  'TABLET_VOLATILE_STATE_BOOTING' = 'TABLET_VOLATILE_STATE_BOOTING',
146
146
  'TABLET_VOLATILE_STATE_STARTING' = 'TABLET_VOLATILE_STATE_STARTING',
147
147
  'TABLET_VOLATILE_STATE_RUNNING' = 'TABLET_VOLATILE_STATE_RUNNING',
148
- '_TABLET_VOLATILE_STATE_BLOCKED' = '_TABLET_VOLATILE_STATE_BLOCKED',
149
148
  }
@@ -14,11 +14,17 @@ import type {IQueryResult, QueryError, QueryErrorResponse} from './query';
14
14
 
15
15
  export type MonacoHotKeyAction = ValueOf<typeof MONACO_HOT_KEY_ACTIONS>;
16
16
 
17
+ export interface QueryInHistory {
18
+ queryText: string;
19
+ syntax?: string;
20
+ }
21
+
17
22
  export interface ExecuteQueryState {
18
23
  loading: boolean;
19
24
  input: string;
20
25
  history: {
21
- queries: string[];
26
+ // String type for backward compatibility
27
+ queries: (QueryInHistory | string)[];
22
28
  currentIndex: number;
23
29
  };
24
30
  monacoHotKey: null | MonacoHotKeyAction;
@@ -38,3 +44,7 @@ export type ExecuteQueryAction =
38
44
  | ReturnType<typeof saveQueryToHistory>
39
45
  | ReturnType<typeof setMonacoHotKey>
40
46
  | ReturnType<typeof setTenantPath>;
47
+
48
+ export interface ExecuteQueryStateSlice {
49
+ executeQuery: ExecuteQueryState;
50
+ }
@@ -1,4 +1,4 @@
1
- import {QUERY_ACTIONS, QUERY_MODES} from '../../utils/query';
1
+ import {QUERY_ACTIONS, QUERY_MODES, QUERY_SYNTAX} from '../../utils/query';
2
2
 
3
3
  import type {IResponseError, NetworkError} from '../api/error';
4
4
  import type {
@@ -29,6 +29,7 @@ export type QueryError = NetworkError | QueryErrorResponse;
29
29
 
30
30
  export type QueryAction = ValueOf<typeof QUERY_ACTIONS>;
31
31
  export type QueryMode = ValueOf<typeof QUERY_MODES>;
32
+ export type QuerySyntax = ValueOf<typeof QUERY_SYNTAX>;
32
33
 
33
34
  export interface SavedQuery {
34
35
  name: string;
@@ -1,4 +1,11 @@
1
1
  export class AutoFetcher {
2
+ static DEFAULT_TIMEOUT = 30000;
3
+ static MIN_TIMEOUT = 30000;
4
+ timeout: number;
5
+ active: boolean;
6
+ timer: undefined | ReturnType<typeof setTimeout>;
7
+ launchCounter: number;
8
+
2
9
  constructor() {
3
10
  this.timeout = AutoFetcher.DEFAULT_TIMEOUT;
4
11
  this.active = true;
@@ -52,11 +59,4 @@ export class AutoFetcher {
52
59
  this.launchCounter++;
53
60
  this.active = true;
54
61
  }
55
-
56
- static DEFAULT_TIMEOUT = 30000;
57
- static MIN_TIMEOUT = 30000;
58
- timeout: number;
59
- active: boolean;
60
- timer: undefined | ReturnType<typeof setTimeout>;
61
- launchCounter: number;
62
62
  }
@@ -25,7 +25,6 @@ export const TABLET_STATES = {
25
25
  TABLET_VOLATILE_STATE_BOOTING: 'booting',
26
26
  TABLET_VOLATILE_STATE_STARTING: 'starting',
27
27
  TABLET_VOLATILE_STATE_RUNNING: 'running',
28
- TABLET_VOLATILE_STATE_BLOCKED: 'blocked',
29
28
  };
30
29
 
31
30
  export const TABLET_COLORS = {
@@ -81,9 +80,11 @@ export const COLORS_PRIORITY = {
81
80
  // ==== Titles ====
82
81
  export const DEVELOPER_UI_TITLE = 'Developer UI';
83
82
  export const CLUSTER_DEFAULT_TITLE = 'Cluster';
83
+ export const TENANT_DEFAULT_TITLE = 'Database';
84
84
 
85
85
  // ==== Settings ====
86
86
  export const THEME_KEY = 'theme';
87
+ export const LANGUAGE_KEY = 'language';
87
88
  export const INVERTED_DISKS_KEY = 'invertedDisks';
88
89
  export const USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY = 'useNodesEndpointInDiagnostics';
89
90
  export const ENABLE_ADDITIONAL_QUERY_MODES = 'enableAdditionalQueryModes';
@@ -1,3 +1,3 @@
1
1
  {
2
- "useQueryModes.queryModeCannotBeSet": "Query mode \"{{mode}}\" cannot be set. You need to turn in additional query modes in settings to enable it"
2
+ "useQueryModes.queryModeCannotBeSet": "Query mode '{{mode}}' cannot be set. You need to turn in additional query modes in settings to enable it"
3
3
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "useQueryModes.queryModeCannotBeSet": "Режим выполнения запроса \"{{mode}}\" недоступен. Вам необходимо включить дополнительные режимы выполнения запросов в настройках"
2
+ "useQueryModes.queryModeCannotBeSet": "Режим выполнения запроса '{{mode}}' недоступен. Вам необходимо включить дополнительные режимы выполнения запросов в настройках"
3
3
  }
@@ -1,6 +1,6 @@
1
1
  import type {QueryMode} from '../../types/store/query';
2
2
  import {ENABLE_ADDITIONAL_QUERY_MODES, QUERY_INITIAL_MODE_KEY} from '../constants';
3
- import {isNewQueryMode} from '../query';
3
+ import {QUERY_MODES_TITLES, isNewQueryMode} from '../query';
4
4
  import createToast from '../createToast';
5
5
  import {useSetting} from './useSetting';
6
6
  import i18n from './i18n';
@@ -18,7 +18,9 @@ export const useQueryModes = (): [QueryMode, SetQueryModeIfAvailable] => {
18
18
  if (isNewQueryMode(value) && !enableAdditionalQueryModes) {
19
19
  createToast({
20
20
  name: 'QueryModeCannotBeSet',
21
- title: errorMessage ?? i18n('useQueryModes.queryModeCannotBeSet', {mode: value}),
21
+ title:
22
+ errorMessage ??
23
+ i18n('useQueryModes.queryModeCannotBeSet', {mode: QUERY_MODES_TITLES[value]}),
22
24
  type: 'error',
23
25
  });
24
26
 
@@ -2,15 +2,21 @@ import {I18N} from '@gravity-ui/i18n';
2
2
  import {configure as configureUiKit} from '@gravity-ui/uikit';
3
3
  import {configure as configureYdbUiComponents} from 'ydb-ui-components';
4
4
 
5
+ import {LANGUAGE_KEY} from '../constants';
6
+ import {readSavedSettingsValue} from '../settings';
7
+
5
8
  enum Lang {
6
9
  En = 'en',
7
10
  Ru = 'ru',
8
11
  }
9
12
 
13
+ const defaultLang = Lang.En;
14
+ const currentLang = readSavedSettingsValue(LANGUAGE_KEY, defaultLang);
15
+
10
16
  const i18n = new I18N();
11
17
 
12
- i18n.setLang(Lang.En);
13
- configureYdbUiComponents({lang: Lang.En});
14
- configureUiKit({lang: Lang.En});
18
+ i18n.setLang(currentLang);
19
+ configureYdbUiComponents({lang: currentLang});
20
+ configureUiKit({lang: currentLang});
15
21
 
16
- export {i18n, Lang};
22
+ export {i18n, Lang, currentLang, defaultLang};
@@ -18,12 +18,6 @@ export const NodesUptimeFilterTitles = {
18
18
  export const isUnavailableNode = (node: NodesPreparedEntity | TSystemStateInfo) =>
19
19
  !node.SystemState || node.SystemState === EFlag.Grey;
20
20
 
21
- export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints'>;
22
-
23
- export interface AdditionalNodesInfo extends Record<string, unknown> {
24
- getNodeRef?: (node?: NodeAddress) => string | null;
25
- }
26
-
27
21
  export const prepareNodesMap = (nodesList?: TNodeInfo[]) => {
28
22
  return nodesList?.reduce<NodesMap>((nodesMap, node) => {
29
23
  if (node.Id && node.Host) {