ydb-embedded-ui 4.15.1 → 4.16.1

Sign up to get free protection for your applications and to get access to all the features.
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) {