ydb-embedded-ui 1.13.2 → 1.14.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 (60) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/assets/icons/flask.svg +3 -0
  3. package/dist/components/InfoViewer/formatters/common.ts +15 -0
  4. package/dist/components/InfoViewer/formatters/index.ts +2 -0
  5. package/dist/components/InfoViewer/formatters/schema.ts +43 -0
  6. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +44 -0
  7. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +34 -0
  8. package/dist/components/{IndexInfoViewer/IndexInfoViewer.tsx → InfoViewer/schemaInfo/TableIndexInfo.tsx} +7 -18
  9. package/dist/components/InfoViewer/schemaInfo/index.ts +3 -0
  10. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +44 -0
  11. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +35 -0
  12. package/dist/components/InfoViewer/schemaOverview/index.ts +2 -0
  13. package/dist/components/QueryResultTable/Cell/Cell.tsx +33 -0
  14. package/dist/components/QueryResultTable/Cell/index.ts +1 -0
  15. package/dist/components/QueryResultTable/QueryResultTable.scss +11 -0
  16. package/dist/components/QueryResultTable/QueryResultTable.tsx +115 -0
  17. package/dist/components/QueryResultTable/i18n/en.json +3 -0
  18. package/dist/components/QueryResultTable/i18n/index.ts +11 -0
  19. package/dist/components/QueryResultTable/i18n/ru.json +3 -0
  20. package/dist/components/QueryResultTable/index.ts +1 -0
  21. package/dist/containers/App/App.scss +1 -0
  22. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +39 -14
  23. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +18 -7
  24. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +4 -3
  25. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +7 -7
  26. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -2
  27. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +8 -3
  29. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +1 -1
  31. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +36 -10
  32. package/dist/containers/Tenant/Preview/Preview.js +15 -57
  33. package/dist/containers/Tenant/Preview/Preview.scss +4 -8
  34. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -41
  35. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -4
  36. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.scss +1 -2
  37. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  38. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  39. package/dist/containers/Tenant/utils/schema.ts +3 -0
  40. package/dist/containers/Tenant/utils/schemaActions.ts +1 -2
  41. package/dist/containers/Tenants/Tenants.js +12 -2
  42. package/dist/containers/UserSettings/UserSettings.tsx +26 -3
  43. package/dist/services/api.d.ts +19 -2
  44. package/dist/services/api.js +2 -2
  45. package/dist/setupTests.js +4 -0
  46. package/dist/store/reducers/executeQuery.js +4 -9
  47. package/dist/store/reducers/{preview.js → preview.ts} +22 -18
  48. package/dist/store/reducers/settings.js +3 -1
  49. package/dist/store/utils.ts +88 -0
  50. package/dist/types/api/query.ts +147 -0
  51. package/dist/types/api/schema.ts +235 -2
  52. package/dist/types/index.ts +33 -0
  53. package/dist/types/store/query.ts +9 -0
  54. package/dist/utils/{constants.js → constants.ts} +11 -6
  55. package/dist/utils/index.js +0 -24
  56. package/dist/utils/query.test.ts +189 -0
  57. package/dist/utils/query.ts +156 -0
  58. package/dist/utils/tests/providers.tsx +29 -0
  59. package/package.json +2 -2
  60. package/dist/store/utils.js +0 -51
@@ -56,9 +56,12 @@ interface TPathDescription {
56
56
  ColumnTableDescription?: unknown;
57
57
 
58
58
  TableIndex?: TIndexDescription;
59
+
60
+ CdcStreamDescription?: TCdcStreamDescription;
61
+ PersQueueGroup?: TPersQueueGroupDescription;
59
62
  }
60
63
 
61
- interface TDirEntry {
64
+ export interface TDirEntry {
62
65
  Name?: string;
63
66
  /** uint64 */
64
67
  PathId?: string;
@@ -208,12 +211,50 @@ export interface TIndexDescription {
208
211
  DataSize?: string;
209
212
  }
210
213
 
214
+ enum ECdcStreamMode {
215
+ ECdcStreamModeInvalid = 'ECdcStreamModeInvalid',
216
+ ECdcStreamModeKeysOnly = 'ECdcStreamModeKeysOnly',
217
+ ECdcStreamModeUpdate = 'ECdcStreamModeUpdate',
218
+ ECdcStreamModeNewImage = 'ECdcStreamModeNewImage',
219
+ ECdcStreamModeOldImage = 'ECdcStreamModeOldImage',
220
+ ECdcStreamModeNewAndOldImages = 'ECdcStreamModeNewAndOldImages',
221
+ }
222
+
223
+ enum ECdcStreamFormat {
224
+ ECdcStreamFormatInvalid = 'ECdcStreamFormatInvalid',
225
+ ECdcStreamFormatProto = 'ECdcStreamFormatProto',
226
+ ECdcStreamFormatJson = 'ECdcStreamFormatJson',
227
+ }
228
+
229
+ enum ECdcStreamState {
230
+ ECdcStreamStateInvalid = 'ECdcStreamStateInvalid',
231
+ ECdcStreamStateReady = 'ECdcStreamStateReady',
232
+ ECdcStreamStateDisabled = 'ECdcStreamStateDisabled',
233
+ }
234
+
235
+ interface TPathID {
236
+ /** fixed64 */
237
+ OwnerId?: string;
238
+ /** uint64 */
239
+ LocalId?: string;
240
+ }
241
+
242
+ export interface TCdcStreamDescription {
243
+ Name?: string;
244
+ Mode?: ECdcStreamMode;
245
+ Format?: ECdcStreamFormat;
246
+ PathId?: TPathID;
247
+ State?: ECdcStreamState;
248
+ /** uint64 */
249
+ SchemaVersion?: string;
250
+ }
251
+
211
252
  // incomplete
212
253
  export enum EPathType {
213
254
  EPathTypeInvalid = 'EPathTypeInvalid',
214
255
  EPathTypeDir = 'EPathTypeDir',
215
256
  EPathTypeTable = 'EPathTypeTable',
216
-
257
+ EPathTypePersQueueGroup = 'EPathTypePersQueueGroup',
217
258
  EPathTypeSubDomain = 'EPathTypeSubDomain',
218
259
 
219
260
  EPathTypeTableIndex = 'EPathTypeTableIndex',
@@ -264,3 +305,195 @@ interface TPathVersion {
264
305
  /** uint64 */
265
306
  GeneralVersion?: string;
266
307
  }
308
+
309
+ interface TPartitionKeyRange {
310
+ // Inclusive left border. Emptiness means -inf.
311
+ FromBound?: string;
312
+ // Exclusive right border. Emptiness means +inf.
313
+ ToBound?: string;
314
+ }
315
+
316
+ interface TPartition {
317
+ PartitionId?: number;
318
+ /** uint64 */
319
+ TabletId?: string;
320
+ KeyRange?: TPartitionKeyRange;
321
+ }
322
+
323
+ interface TPartitionToAdd {
324
+ PartitionId?: number;
325
+ GroupId?: number;
326
+ }
327
+
328
+ interface TCodecs {
329
+ /** int64 */
330
+ Ids?: string[];
331
+ Codecs?: string[];
332
+ }
333
+
334
+ interface TKeyComponentSchema {
335
+ Name?: string;
336
+ TypeId?: number;
337
+ }
338
+
339
+ enum EMeteringMode {
340
+ METERING_MODE_RESERVED_CAPACITY = 'METERING_MODE_RESERVED_CAPACITY',
341
+ METERING_MODE_REQUEST_UNITS = 'METERING_MODE_REQUEST_UNITS',
342
+ }
343
+
344
+ interface TReadQuota {
345
+ ClientId?: string;
346
+ /** uint64 */
347
+ SpeedInBytesPerSecond?: string;
348
+ /** uint64 */
349
+ BurstSize?: string;
350
+ }
351
+
352
+ interface TChannelProfile {
353
+ PoolKind?: string;
354
+ /** uint64 */
355
+ Size?: string;
356
+ ReadIops?: number;
357
+ ReadBandwidth?: number;
358
+ WriteIops?: number;
359
+ WriteBandwidth?: number;
360
+ }
361
+
362
+ interface IamCredentials {
363
+ Endpoint?: string;
364
+ ServiceAccountKey?: string;
365
+ }
366
+
367
+ interface TCredentials {
368
+ OauthToken?: string;
369
+ JwtParams?: string;
370
+ Iam?: IamCredentials;
371
+ }
372
+
373
+ interface TMirrorPartitionConfig {
374
+ Endpoint?: string;
375
+ EndpointPort?: number;
376
+ Topic?: string;
377
+ Consumer?: string;
378
+ /** uint64 */
379
+ ReadFromTimestampsMs?: string;
380
+ Credentials?: TCredentials;
381
+ Database?: string;
382
+ UseSecureConnection?: boolean;
383
+ SyncWriteTime?: boolean;
384
+ }
385
+
386
+ interface TPQPartitionConfig {
387
+ MaxCountInPartition?: number;
388
+ /** int64 */
389
+ MaxSizeInPartition?: string;
390
+ LifetimeSeconds: number;
391
+ /** uint64 */
392
+ StorageLimitBytes?: string;
393
+
394
+ ImportantClientId?: string[];
395
+ LowWatermark?: number;
396
+ SourceIdLifetimeSeconds?: number;
397
+ SourceIdMaxCounts?: number;
398
+
399
+ /** uint64 */
400
+ WriteSpeedInBytesPerSecond?: string;
401
+ /** uint64 */
402
+ BurstSize?: string;
403
+
404
+ ReadQuota?: TReadQuota[];
405
+ /** uint64 */
406
+ MaxWriteInflightSize?: string;
407
+ /** uint64 */
408
+ BorderWriteInflightSize?: string;
409
+
410
+ NumChannels?: number;
411
+
412
+ TotalPartitions?: number;
413
+
414
+ ExplicitChannelProfiles?: TChannelProfile[];
415
+
416
+ MirrorFrom?: TMirrorPartitionConfig;
417
+ };
418
+
419
+ interface TPQTabletConfig {
420
+ /** uint64 */
421
+ CacheSize?: string;
422
+ PartitionConfig: TPQPartitionConfig;
423
+ /** @deprecated use Partitions */
424
+ PartitionIds?: number[];
425
+ TopicName?: string;
426
+ Version?: number;
427
+ LocalDC?: boolean;
428
+ RequireAuthWrite?: boolean;
429
+ RequireAuthRead?: boolean;
430
+ Producer?: string;
431
+ Ident?: string;
432
+ Topic?: string;
433
+ DC?: string;
434
+
435
+ ReadRules?: string[];
436
+ /** uint64[] */
437
+ ReadFromTimestampsMs?: number[];
438
+ /** uint64[] */
439
+ ConsumerFormatVersions?: number[];
440
+
441
+ ConsumerCodecs?: TCodecs[];
442
+ ReadRuleServiceTypes?: string;
443
+
444
+ /** uint64 */
445
+ FormatVersion?: string;
446
+ Codecs?: TCodecs;
447
+
448
+ /** uint64[] */
449
+ ReadRuleVersions?: string[];
450
+ /** uint64[] */
451
+ ReadRuleGenerations?: string[];
452
+
453
+ TopicPath?: string;
454
+
455
+ YcCloudId?: string;
456
+ YcFolderId?: string;
457
+ YdbDatabaseId?: string;
458
+ YdbDatabasePath?: string;
459
+ FederationAccount?: string;
460
+
461
+ PartitionKeySchema?: TKeyComponentSchema[];
462
+
463
+ Partitions?: TPartition[];
464
+
465
+ MeteringMode?: EMeteringMode;
466
+ }
467
+
468
+ interface TMessageGroup {
469
+ // Id of message group (SourceId)
470
+ Id?: string;
471
+ // Range of the key to which it is allowed to write.
472
+ KeyRange?: TPartitionKeyRange;
473
+ }
474
+
475
+ interface TBootstrapConfig {
476
+ ExplicitMessageGroups?: TMessageGroup[];
477
+ }
478
+
479
+ export interface TPersQueueGroupDescription {
480
+ Name: string;
481
+ /** uint64 */
482
+ PathId?: string;
483
+ TotalGroupCount: number;
484
+
485
+ PartitionsToAdd?: TPartitionToAdd[];
486
+ PartitionsToDelete?: number[];
487
+ NextPartitionId?: number;
488
+ PartitionPerTablet?: number;
489
+ PQTabletConfig: TPQTabletConfig;
490
+ Partitions?: TPartition[];
491
+ /** uint64 */
492
+ AlterVersion?: string;
493
+ /** uint64 */
494
+ BalancerTabletID?: string;
495
+
496
+ PartitionBoundaries?: any;
497
+
498
+ BootstrapConfig?: TBootstrapConfig;
499
+ }
@@ -1 +1,34 @@
1
1
  export type RequiredField<Src, Fields extends keyof Src> = Src & Required<Pick<Src, Fields>>;
2
+
3
+ export enum YQLType {
4
+ // Numeric
5
+ Bool = 'Bool',
6
+ Int8 = 'Int8',
7
+ Int16 = 'Int16',
8
+ Int32 = 'Int32',
9
+ Int64 = 'Int64',
10
+ Uint8 = 'Uint8',
11
+ Uint16 = 'Uint16',
12
+ Uint32 = 'Uint32',
13
+ Uint64 = 'Uint64',
14
+ Float = 'Float',
15
+ Double = 'Double',
16
+ Decimal = 'Decimal',
17
+
18
+ // String
19
+ String = 'String',
20
+ Utf8 = 'Utf8',
21
+ Json = 'Json',
22
+ JsonDocument = 'JsonDocument',
23
+ Yson = 'Yson',
24
+ Uuid = 'Uuid',
25
+
26
+ // Date and time
27
+ Date = 'Date',
28
+ Datetime = 'Datetime',
29
+ Timestamp = 'Timestamp',
30
+ Interval = 'Interval',
31
+ TzDate = 'TzDate',
32
+ TzDateTime = 'TzDateTime',
33
+ TzTimestamp = 'TzTimestamp',
34
+ }
@@ -0,0 +1,9 @@
1
+ import type {KeyValueRow, ColumnType} from '../api/query';
2
+
3
+ export interface IQueryResult {
4
+ result?: KeyValueRow[];
5
+ columns?: ColumnType[];
6
+ stats?: any;
7
+ plan?: any;
8
+ ast?: any;
9
+ }
@@ -14,7 +14,8 @@ export const GIGABYTE = 1_000_000_000;
14
14
  export const TERABYTE = 1_000_000_000_000;
15
15
  export const GROUP = 'group';
16
16
 
17
- export const DAY_IN_SECONDS = 24 * 60 * 60;
17
+ export const HOUR_IN_SECONDS = 60 * 60;
18
+ export const DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS;
18
19
 
19
20
  export const TABLET_STATES = {
20
21
  TABLET_VOLATILE_STATE_UNKNOWN: 'unknown',
@@ -87,13 +88,15 @@ export const TABLET_SYMBOLS = {
87
88
  TenantSlotBroker: 'TB',
88
89
  };
89
90
 
90
- export const getTabletLabel = (type) => {
91
+ const isTabletType = (type: string): type is keyof typeof TABLET_SYMBOLS => type in TABLET_SYMBOLS;
92
+
93
+ export const getTabletLabel = (type?: string) => {
91
94
  if (!type) {
92
- return;
95
+ return undefined;
93
96
  }
94
- const defaultValue = type.match(/[A-Z]/g).join('');
97
+ const defaultValue = type.match(/[A-Z]/g)?.join('');
95
98
 
96
- return TABLET_SYMBOLS[type] || defaultValue;
99
+ return isTabletType(type) ? TABLET_SYMBOLS[type] : defaultValue;
97
100
  };
98
101
 
99
102
  export const LOAD_AVERAGE_TIME_INTERVALS = ['1 min', '5 min', '15 min'];
@@ -116,12 +119,14 @@ export const ALL = 'All';
116
119
  export const PROBLEMS = 'With problems';
117
120
 
118
121
  export const THEME_KEY = 'theme';
122
+ export const INVERTED_DISKS_KEY = 'invertedDisks';
119
123
  export const SAVED_QUERIES_KEY = 'saved_queries';
120
124
  export const QUERIES_HISTORY_KEY = 'queries_history';
121
125
  export const DATA_QA_TUNE_COLUMNS_POPUP = 'tune-columns-popup';
122
126
 
123
127
  export const defaultUserSettings = {
124
128
  [THEME_KEY]: 'light',
129
+ [INVERTED_DISKS_KEY]: false,
125
130
  };
126
131
  export const DEFAULT_SIZE_RESULT_PANE_KEY = 'default-size-result-pane';
127
132
  export const DEFAULT_SIZE_TENANT_SUMMARY_KEY = 'default-size-tenant-summary-pane';
@@ -139,7 +144,7 @@ export const DEFAULT_TABLE_SETTINGS = {
139
144
  syncHeadOnResize: true,
140
145
  dynamicRender: true,
141
146
  highlightRows: true,
142
- };
147
+ } as const;
143
148
 
144
149
  export const TENANT_INITIAL_TAB_KEY = 'saved_tenant_initial_tab';
145
150
  export const QUERY_INITIAL_RUN_ACTION_KEY = 'query_initial_run_action';
@@ -1,6 +1,5 @@
1
1
  import numeral from 'numeral';
2
2
  import locales from 'numeral/locales'; // eslint-disable-line no-unused-vars
3
- import _ from 'lodash';
4
3
 
5
4
  import {i18n} from './i18n';
6
5
  import {MEGABYTE, TERABYTE, DAY_IN_SECONDS, GIGABYTE} from './constants';
@@ -143,26 +142,3 @@ export const getMetaForExplainNode = (node) => {
143
142
  return '';
144
143
  }
145
144
  };
146
-
147
- export const prepareQueryResponse = (data) => {
148
- return _.map(data, (item) => {
149
- const formattedData = {};
150
-
151
- for (const field in item) {
152
- if (Object.prototype.hasOwnProperty.call(item, field)) {
153
- const type = typeof item[field];
154
- if (type === 'object' || type === 'boolean' || Array.isArray(item[field])) {
155
- formattedData[field] = JSON.stringify(item[field]);
156
- } else {
157
- formattedData[field] = item[field];
158
- }
159
- }
160
- }
161
-
162
- return formattedData;
163
- });
164
- };
165
-
166
- export function prepareQueryError(error) {
167
- return error.data?.error?.message || error.data || error
168
- }
@@ -0,0 +1,189 @@
1
+ import {parseQueryAPIExecuteResponse} from './query';
2
+
3
+ describe('API utils', () => {
4
+ describe('json/viewer/query', () => {
5
+ describe('parseQueryAPIExecuteResponse', () => {
6
+ describe('old format', () => {
7
+ describe('plain response', () => {
8
+ it('should handle empty response', () => {
9
+ expect(parseQueryAPIExecuteResponse(null).result).toBeUndefined();
10
+ });
11
+
12
+ it('should parse json string', () => {
13
+ const json = {foo: 'bar'};
14
+ const response = JSON.stringify(json);
15
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(json);
16
+ });
17
+
18
+ // it should not be in the response, but is there because of a bug
19
+ it('should ignore request plan as the response', () => {
20
+ const response = {queries: 'some queries'};
21
+ expect(parseQueryAPIExecuteResponse(response).result).toBeUndefined();
22
+ });
23
+
24
+ it('should accept key-value rows', () => {
25
+ const response = [{foo: 'bar'}];
26
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response);
27
+ });
28
+ });
29
+
30
+ describe('deep response without stats', () => {
31
+ it('should parse json string in the result field', () => {
32
+ const json = {foo: 'bar'};
33
+ const response = {result: JSON.stringify(json)};
34
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(json);
35
+ });
36
+
37
+ // it should not be in the response, but is there because of a bug
38
+ it('should ignore request plan in the result field', () => {
39
+ const response = {result: {queries: 'some queries'}};
40
+ expect(parseQueryAPIExecuteResponse(response).result).toBeUndefined();
41
+ });
42
+
43
+ it('should accept key-value rows in the result field', () => {
44
+ const response = {result: [{foo: 'bar'}]};
45
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response.result);
46
+ });
47
+ });
48
+
49
+ describe('deep response with stats', () => {
50
+ it('should parse json string in the result field', () => {
51
+ const json = {foo: 'bar'};
52
+ const response = {
53
+ result: JSON.stringify(json),
54
+ stats: {metric: 'good'},
55
+ };
56
+ const actual = parseQueryAPIExecuteResponse(response);
57
+ expect(actual.result).toEqual(json);
58
+ expect(actual.stats).toEqual(response.stats);
59
+ });
60
+
61
+ // it should not be in the response, but is there because of a bug
62
+ it('should ignore request plan in the result field', () => {
63
+ const response = {
64
+ result: {queries: 'some queries'},
65
+ stats: {metric: 'good'},
66
+ };
67
+ const actual = parseQueryAPIExecuteResponse(response);
68
+ expect(actual.result).toBeUndefined();
69
+ expect(actual.stats).toEqual(response.stats);
70
+ });
71
+
72
+ it('should accept key-value rows in the result field', () => {
73
+ const response = {
74
+ result: [{foo: 'bar'}],
75
+ stats: {metric: 'good'},
76
+ };
77
+ const actual = parseQueryAPIExecuteResponse(response);
78
+ expect(actual.result).toEqual(response.result);
79
+ expect(actual.stats).toEqual(response.stats);
80
+ });
81
+
82
+ it('should accept stats without a result', () => {
83
+ const response = {
84
+ stats: {metric: 'good'},
85
+ };
86
+ const actual = parseQueryAPIExecuteResponse(response);
87
+ expect(actual.result).toBeUndefined();
88
+ expect(actual.stats).toEqual(response.stats);
89
+ });
90
+ });
91
+ });
92
+
93
+ describe('new format', () => {
94
+ describe('response without stats', () => {
95
+ it('should parse modern schema', () => {
96
+ const response = {
97
+ result: [['42', 'hello world']],
98
+ columns: [{
99
+ name: 'id',
100
+ type: 'Uint64?'
101
+ }, {
102
+ name: 'value',
103
+ type: 'Utf8?'
104
+ }],
105
+ };
106
+ const actual = parseQueryAPIExecuteResponse(response);
107
+ expect(actual.result).toEqual([{
108
+ id: '42',
109
+ value: 'hello world'
110
+ }]);
111
+ expect(actual.columns).toEqual(response.columns);
112
+ });
113
+
114
+ it('should handle empty response for classic schema', () => {
115
+ expect(parseQueryAPIExecuteResponse(null).result).toBeUndefined();
116
+ });
117
+
118
+ it('should parse plain classic schema', () => {
119
+ const response = [{foo: 'bar'}];
120
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response);
121
+ });
122
+
123
+ it('should parse deep classic schema', () => {
124
+ const response = {result: [{foo: 'bar'}]};
125
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response.result);
126
+ });
127
+
128
+ it('should parse ydb schema', () => {
129
+ const response = {result: [{foo: 'bar'}]};
130
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response.result);
131
+ });
132
+ });
133
+
134
+ describe('response with stats', () => {
135
+ it('should parse modern schema', () => {
136
+ const response = {
137
+ result: [['42', 'hello world']],
138
+ columns: [{
139
+ name: 'id',
140
+ type: 'Uint64?'
141
+ }, {
142
+ name: 'value',
143
+ type: 'Utf8?'
144
+ }],
145
+ stats: {metric: 'good'},
146
+ };
147
+ const actual = parseQueryAPIExecuteResponse(response);
148
+ expect(actual.result).toEqual([{
149
+ id: '42',
150
+ value: 'hello world'
151
+ }]);
152
+ expect(actual.columns).toEqual(response.columns);
153
+ expect(actual.stats).toEqual(response.stats);
154
+ });
155
+
156
+ it('should parse classic schema', () => {
157
+ const response = {
158
+ result: [{foo: 'bar'}],
159
+ stats: {metric: 'good'},
160
+ };
161
+ const actual = parseQueryAPIExecuteResponse(response);
162
+ expect(actual.result).toEqual(response.result);
163
+ expect(actual.stats).toEqual(response.stats);
164
+ });
165
+
166
+ it('should parse ydb schema', () => {
167
+ const response = {
168
+ result: [{foo: 'bar'}],
169
+ stats: {metric: 'good'},
170
+ };
171
+ const actual = parseQueryAPIExecuteResponse(response);
172
+ expect(actual.result).toEqual(response.result);
173
+ expect(actual.stats).toEqual(response.stats);
174
+ });
175
+
176
+ it('should accept stats without a result', () => {
177
+ const response = {
178
+ stats: {metric: 'good'},
179
+ };
180
+ const actual = parseQueryAPIExecuteResponse(response);
181
+ expect(actual.result).toBeUndefined();
182
+ expect(actual.columns).toBeUndefined();
183
+ expect(actual.stats).toEqual(response.stats);
184
+ });
185
+ });
186
+ });
187
+ });
188
+ });
189
+ });