ydb-embedded-ui 1.13.2 → 1.14.1

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