ydb-embedded-ui 4.21.0 → 4.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/NodeHostWrapper/NodeHostWrapper.scss +2 -1
  3. package/dist/components/QueryResultTable/QueryResultTable.tsx +7 -3
  4. package/dist/components/VirtualTable/TableChunk.tsx +2 -1
  5. package/dist/components/VirtualTable/VirtualTable.tsx +1 -1
  6. package/dist/containers/Node/Node.tsx +9 -17
  7. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +8 -30
  8. package/dist/containers/Node/NodeStructure/Pdisk.tsx +32 -18
  9. package/dist/containers/Node/i18n/en.json +4 -0
  10. package/dist/containers/Node/i18n/index.ts +11 -0
  11. package/dist/containers/Node/i18n/ru.json +4 -0
  12. package/dist/containers/Nodes/Nodes.tsx +5 -10
  13. package/dist/containers/Nodes/getNodesColumns.tsx +57 -13
  14. package/dist/containers/Tablets/Tablets.tsx +3 -8
  15. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +0 -1
  16. package/dist/containers/Tenant/Diagnostics/Network/Network.js +5 -10
  17. package/dist/containers/Tenant/Diagnostics/Network/utils.ts +6 -0
  18. package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss +13 -5
  19. package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +72 -18
  20. package/dist/containers/Tenant/Query/ExplainResult/ExplainResult.js +2 -1
  21. package/dist/containers/Tenant/Query/ExplainResult/utils.ts +6 -0
  22. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +11 -24
  23. package/dist/containers/Tenant/Query/utils/getPreparedResult.ts +4 -5
  24. package/dist/containers/UserSettings/i18n/en.json +5 -2
  25. package/dist/containers/UserSettings/i18n/ru.json +5 -2
  26. package/dist/containers/UserSettings/settings.ts +12 -6
  27. package/dist/store/reducers/executeQuery.ts +4 -3
  28. package/dist/store/reducers/nodes/nodes.ts +2 -2
  29. package/dist/store/reducers/nodes/types.ts +12 -1
  30. package/dist/store/reducers/nodes/utils.ts +6 -0
  31. package/dist/store/reducers/settings/settings.ts +5 -3
  32. package/dist/types/api/netInfo.ts +1 -1
  33. package/dist/types/api/nodes.ts +24 -0
  34. package/dist/types/api/query.ts +23 -8
  35. package/dist/types/store/query.ts +6 -0
  36. package/dist/utils/constants.ts +3 -0
  37. package/dist/utils/developerUI/__test__/developerUI.test.ts +50 -0
  38. package/dist/utils/developerUI/developerUI.ts +42 -0
  39. package/dist/utils/diagnostics.ts +1 -0
  40. package/dist/utils/query.ts +68 -13
  41. package/package.json +1 -1
  42. package/dist/utils/index.js +0 -9
  43. /package/dist/{components/VirtualTable/utils.ts → utils/index.ts} +0 -0
@@ -180,7 +180,7 @@ export interface ColumnType {
180
180
  }
181
181
 
182
182
  /** undefined = 'classic' */
183
- export type Schemas = 'classic' | 'modern' | 'ydb' | undefined;
183
+ export type Schemas = 'classic' | 'modern' | 'ydb' | 'multi' | undefined;
184
184
 
185
185
  /** undefined = 'execute' */
186
186
  export type ExecuteActions =
@@ -231,16 +231,29 @@ export type ExplainResponse<Action extends ExplainActions> = Action extends 'exp
231
231
  ? ExplainScriptResponse
232
232
  : ExplainQueryResponse;
233
233
 
234
+ // ==== Execute Results ====
235
+
236
+ interface ModernSchemaResult {
237
+ result?: ArrayRow[];
238
+ columns?: ColumnType[];
239
+ }
240
+ interface MultiSchemaResult {
241
+ result?: {
242
+ rows?: ArrayRow[] | null;
243
+ columns?: ColumnType[];
244
+ }[];
245
+ }
246
+ interface DefaultSchemaResult {
247
+ result?: KeyValueRow[];
248
+ }
249
+
234
250
  // ==== Execute Responses ====
235
251
 
236
252
  type ResultFields<Schema extends Schemas> = Schema extends 'modern'
237
- ? {
238
- result?: ArrayRow[];
239
- columns?: ColumnType[];
240
- }
241
- : {
242
- result?: KeyValueRow[];
243
- };
253
+ ? ModernSchemaResult
254
+ : Schema extends 'multi'
255
+ ? MultiSchemaResult
256
+ : DefaultSchemaResult;
244
257
 
245
258
  /**
246
259
  * meta.type = 'query'
@@ -287,6 +300,7 @@ export type AnyExplainResponse = ExplainQueryResponse | ExplainScriptResponse;
287
300
  export type ExecuteModernResponse =
288
301
  | ExecuteQueryResponse<'modern'>
289
302
  | ExecuteScriptResponse<'modern'>;
303
+ export type ExecuteMultiResponse = ExecuteQueryResponse<'multi'> | ExecuteScriptResponse<'multi'>;
290
304
  export type ExecuteClassicResponse =
291
305
  | ExecuteQueryResponse<'classic'>
292
306
  | ExecuteScriptResponse<'classic'>;
@@ -294,5 +308,6 @@ export type ExecuteYdbResponse = ExecuteQueryResponse<'ydb'> | ExecuteScriptResp
294
308
 
295
309
  export type AnyExecuteResponse =
296
310
  | ExecuteModernResponse
311
+ | ExecuteMultiResponse
297
312
  | ExecuteClassicResponse
298
313
  | ExecuteYdbResponse;
@@ -11,7 +11,13 @@ import type {
11
11
  } from '../api/query';
12
12
  import type {ValueOf} from '../common';
13
13
 
14
+ export interface ParsedResultSet {
15
+ columns?: ColumnType[];
16
+ result?: KeyValueRow[];
17
+ }
18
+
14
19
  export interface IQueryResult {
20
+ resultSets?: ParsedResultSet[];
15
21
  result?: KeyValueRow[];
16
22
  columns?: ColumnType[];
17
23
  stats?: TKqpStatsQuery;
@@ -133,3 +133,6 @@ export const TENANT_INITIAL_PAGE_KEY = 'saved_tenant_initial_tab';
133
133
 
134
134
  // Send filters and sort params to backend for Nodes and Storage tables
135
135
  export const USE_BACKEND_PARAMS_FOR_TABLES_KEY = 'useBackendParamsForTables';
136
+
137
+ // Enable schema that supports multiple resultsets
138
+ export const QUERY_USE_MULTI_SCHEMA_KEY = 'queryUseMultiSchema';
@@ -0,0 +1,50 @@
1
+ import {
2
+ createDeveloperUILinkWithNodeId,
3
+ createPDiskDeveloperUILink,
4
+ createVDiskDeveloperUILink,
5
+ } from '../developerUI';
6
+
7
+ describe('Developer UI links generators', () => {
8
+ describe('createDeveloperUILinkWithNodeId', () => {
9
+ it('should create relative link with no host', () => {
10
+ expect(createDeveloperUILinkWithNodeId(1)).toBe('/node/1/');
11
+ });
12
+ it('should create relative link with existing relative path with nodeId', () => {
13
+ expect(createDeveloperUILinkWithNodeId(1, '/node/3/')).toBe('/node/1/');
14
+ });
15
+ it('should create full link with host', () => {
16
+ expect(
17
+ createDeveloperUILinkWithNodeId(
18
+ 1,
19
+ 'http://ydb-vla-dev02-001.search.yandex.net:8765',
20
+ ),
21
+ ).toBe('http://ydb-vla-dev02-001.search.yandex.net:8765/node/1/');
22
+ });
23
+ it('should create full link with host with existing node path with nodeId', () => {
24
+ expect(
25
+ createDeveloperUILinkWithNodeId(
26
+ 1,
27
+ 'http://ydb-vla-dev02-001.search.yandex.net:8765/node/3',
28
+ ),
29
+ ).toBe('http://ydb-vla-dev02-001.search.yandex.net:8765/node/1/');
30
+ });
31
+ });
32
+ describe('createPDiskDeveloperUILink', () => {
33
+ it('should create link with pDiskId and nodeId', () => {
34
+ expect(createPDiskDeveloperUILink({nodeId: 1, pDiskId: 1})).toBe(
35
+ '/node/1/actors/pdisks/pdisk000000001',
36
+ );
37
+ });
38
+ });
39
+ describe('createVDiskDeveloperUILink', () => {
40
+ it('should create link with pDiskId, vDiskSlotId nodeId', () => {
41
+ expect(
42
+ createVDiskDeveloperUILink({
43
+ nodeId: 1,
44
+ pDiskId: 1,
45
+ vDiskSlotId: 1,
46
+ }),
47
+ ).toBe('/node/1/actors/vdisks/vdisk000000001_000000001');
48
+ });
49
+ });
50
+ });
@@ -0,0 +1,42 @@
1
+ import {backend} from '../../store';
2
+ import {pad9} from '../utils';
3
+
4
+ // Current node connects with target node by itself using nodeId
5
+ export const createDeveloperUILinkWithNodeId = (nodeId: number | string, host = backend) => {
6
+ const nodePathRegexp = /\/node\/\d+\/?$/g;
7
+
8
+ // In case current backend is already relative node path ({host}/node/{nodeId})
9
+ // We replace existing nodeId path with new nodeId path
10
+ if (nodePathRegexp.test(String(host))) {
11
+ return String(host).replace(nodePathRegexp, `/node/${nodeId}/`);
12
+ }
13
+
14
+ return `${host ?? ''}/node/${nodeId}/`;
15
+ };
16
+
17
+ interface PDiskDeveloperUILinkParams {
18
+ nodeId: number | string;
19
+ pDiskId: number | string;
20
+ host?: string;
21
+ }
22
+
23
+ export const createPDiskDeveloperUILink = ({nodeId, pDiskId, host}: PDiskDeveloperUILinkParams) => {
24
+ const pdiskPath = 'actors/pdisks/pdisk' + pad9(pDiskId);
25
+
26
+ return createDeveloperUILinkWithNodeId(nodeId, host) + pdiskPath;
27
+ };
28
+
29
+ interface VDiskDeveloperUILinkParams extends PDiskDeveloperUILinkParams {
30
+ vDiskSlotId: number | string;
31
+ }
32
+
33
+ export const createVDiskDeveloperUILink = ({
34
+ nodeId,
35
+ pDiskId,
36
+ vDiskSlotId,
37
+ host,
38
+ }: VDiskDeveloperUILinkParams) => {
39
+ const vdiskPath = 'actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(vDiskSlotId);
40
+
41
+ return createDeveloperUILinkWithNodeId(nodeId, host) + vdiskPath;
42
+ };
@@ -3,6 +3,7 @@ import {ValueOf} from '../types/common';
3
3
  const TOP_SHARDS_SORT_VALUES = {
4
4
  CPUCores: 'CPUCores',
5
5
  DataSize: 'DataSize',
6
+ InFlightTxCount: 'InFlightTxCount',
6
7
  } as const;
7
8
 
8
9
  const TOP_QUERIES_SORT_VALUES = {
@@ -2,7 +2,10 @@ import {YQLType} from '../types';
2
2
  import type {
3
3
  AnyExecuteResponse,
4
4
  AnyExplainResponse,
5
+ ArrayRow,
6
+ ColumnType,
5
7
  ExecuteModernResponse,
8
+ ExecuteMultiResponse,
6
9
  KeyValueRow,
7
10
  QueryPlan,
8
11
  ScriptPlan,
@@ -76,34 +79,74 @@ export const getColumnType = (type: string) => {
76
79
  }
77
80
  };
78
81
 
79
- /** parse response result field from ArrayRow to KeyValueRow */
82
+ /** parse response result from ArrayRow to KeyValueRow */
83
+ const parseModernResult = (rows: ArrayRow[], columns: ColumnType[]) => {
84
+ return rows.map((row) => {
85
+ return row.reduce<KeyValueRow>((newRow, cellData, columnIndex) => {
86
+ const {name} = columns[columnIndex];
87
+ newRow[name] = cellData;
88
+ return newRow;
89
+ }, {});
90
+ });
91
+ };
92
+
80
93
  const parseExecuteModernResponse = (data: ExecuteModernResponse): IQueryResult => {
81
94
  const {result, columns, ...restData} = data;
82
95
 
83
96
  return {
84
- result:
85
- result &&
86
- columns &&
87
- result.map((row) => {
88
- return row.reduce((newRow, cellData, columnIndex) => {
89
- const {name} = columns[columnIndex];
90
- newRow[name] = cellData;
91
- return newRow;
92
- }, {} as KeyValueRow);
93
- }),
97
+ result: result && columns && parseModernResult(result, columns),
94
98
  columns,
95
99
  ...restData,
96
100
  };
97
101
  };
98
102
 
103
+ const parseExecuteMultiResponse = (data: ExecuteMultiResponse): IQueryResult => {
104
+ const {result, ...restData} = data;
105
+
106
+ const parsedResult = result?.map((resultSet) => {
107
+ const {rows, columns} = resultSet;
108
+
109
+ let parsedRows: KeyValueRow[] | undefined;
110
+
111
+ if (columns) {
112
+ // Result shouldn't be null if there are columns
113
+ parsedRows = [];
114
+ }
115
+
116
+ if (rows && columns) {
117
+ parsedRows = parseModernResult(rows, columns);
118
+ }
119
+
120
+ return {
121
+ columns: columns,
122
+ result: parsedRows,
123
+ };
124
+ });
125
+
126
+ return {
127
+ resultSets: parsedResult, // use a separate field to make result compatible
128
+ ...restData,
129
+ };
130
+ };
131
+
99
132
  const isModern = (response: AnyExecuteResponse): response is ExecuteModernResponse =>
100
133
  Boolean(
101
134
  response &&
102
135
  !Array.isArray(response) &&
103
- Array.isArray((response as ExecuteModernResponse).result) &&
136
+ Array.isArray(response.result) &&
104
137
  Array.isArray((response as ExecuteModernResponse).columns),
105
138
  );
106
139
 
140
+ const isMulti = (response: AnyExecuteResponse): response is ExecuteMultiResponse =>
141
+ Boolean(
142
+ response &&
143
+ !Array.isArray(response) &&
144
+ Array.isArray(response.result) &&
145
+ typeof response.result[0] === 'object' &&
146
+ 'rows' in response.result[0] &&
147
+ 'columns' in response.result[0],
148
+ );
149
+
107
150
  type UnsupportedQueryResponseFormat =
108
151
  | Array<unknown>
109
152
  | string
@@ -122,12 +165,17 @@ const isUnsupportedType = (
122
165
  );
123
166
  };
124
167
 
168
+ // Although schema is set in request, if schema is not supported default schema for the version will be used
169
+ // So we should additionally parse response
125
170
  export const parseQueryAPIExecuteResponse = (
126
171
  data: AnyExecuteResponse | UnsupportedQueryResponseFormat,
127
172
  ): IQueryResult => {
128
173
  if (isUnsupportedType(data)) {
129
174
  return {};
130
175
  }
176
+ if (isMulti(data)) {
177
+ return parseExecuteMultiResponse(data);
178
+ }
131
179
  if (isModern(data)) {
132
180
  return parseExecuteModernResponse(data);
133
181
  }
@@ -175,7 +223,14 @@ export const prepareQueryResponse = (data?: KeyValueRow[]) => {
175
223
  for (const field in row) {
176
224
  if (Object.prototype.hasOwnProperty.call(row, field)) {
177
225
  const type = typeof row[field];
178
- if (type === 'object' || type === 'boolean' || Array.isArray(row[field])) {
226
+
227
+ // Although typeof null == 'object'
228
+ // null result should be preserved
229
+ if (
230
+ (row[field] !== null && type === 'object') ||
231
+ type === 'boolean' ||
232
+ Array.isArray(row[field])
233
+ ) {
179
234
  formattedData[field] = JSON.stringify(row[field]);
180
235
  } else {
181
236
  formattedData[field] = row[field];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.21.0",
3
+ "version": "4.22.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,9 +0,0 @@
1
- // determine how many nodes have status Connected "true"
2
- export const getConnectedNodesCount = (nodeStateInfo) => {
3
- return nodeStateInfo?.reduce((acc, item) => (item.Connected ? acc + 1 : acc), 0);
4
- };
5
-
6
- export const renderExplainNode = (node) => {
7
- const parts = node.name.split('|');
8
- return parts.length > 1 ? parts[1] : node.name;
9
- };