ydb-embedded-ui 3.5.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/components/ClusterInfo/ClusterInfo.tsx +3 -3
  3. package/dist/{containers/Nodes/NodesTable.scss → components/NodeHostWrapper/NodeHostWrapper.scss} +4 -6
  4. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +60 -0
  5. package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -11
  6. package/dist/containers/Header/Header.tsx +1 -1
  7. package/dist/containers/Nodes/getNodesColumns.tsx +7 -46
  8. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +0 -24
  9. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -39
  10. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
  11. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
  12. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
  13. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
  14. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
  15. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
  16. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
  17. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
  18. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
  19. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
  20. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
  21. package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
  22. package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
  23. package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
  24. package/dist/containers/Tenants/Tenants.js +1 -1
  25. package/dist/containers/UserSettings/UserSettings.tsx +30 -1
  26. package/dist/services/api.ts +383 -0
  27. package/dist/store/reducers/{cluster.js → cluster/cluster.ts} +9 -14
  28. package/dist/store/reducers/cluster/types.ts +13 -0
  29. package/dist/store/reducers/executeQuery.ts +12 -37
  30. package/dist/store/reducers/executeTopQueries.ts +2 -2
  31. package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
  32. package/dist/store/reducers/index.ts +5 -4
  33. package/dist/store/reducers/settings.js +19 -17
  34. package/dist/store/reducers/{tenants.js → tenants/tenants.ts} +14 -9
  35. package/dist/store/reducers/tenants/types.ts +17 -0
  36. package/dist/store/utils.ts +3 -2
  37. package/dist/types/api/acl.ts +25 -0
  38. package/dist/types/api/cluster.ts +3 -0
  39. package/dist/types/api/compute.ts +5 -3
  40. package/dist/types/api/error.ts +14 -0
  41. package/dist/types/api/netInfo.ts +48 -0
  42. package/dist/types/api/nodes.ts +5 -3
  43. package/dist/types/api/pdisk.ts +11 -2
  44. package/dist/types/api/query.ts +226 -117
  45. package/dist/types/api/storage.ts +5 -3
  46. package/dist/types/api/tenant.ts +18 -3
  47. package/dist/types/api/vdisk.ts +10 -2
  48. package/dist/types/api/whoami.ts +19 -0
  49. package/dist/types/store/executeQuery.ts +4 -8
  50. package/dist/types/store/explainQuery.ts +38 -0
  51. package/dist/types/store/query.ts +23 -3
  52. package/dist/types/window.d.ts +5 -0
  53. package/dist/utils/constants.ts +2 -1
  54. package/dist/utils/error.ts +25 -0
  55. package/dist/utils/hooks/useTypedSelector.ts +2 -2
  56. package/dist/utils/index.js +0 -49
  57. package/dist/utils/nodes.ts +3 -1
  58. package/dist/utils/prepareQueryExplain.ts +7 -24
  59. package/dist/utils/query.test.ts +153 -231
  60. package/dist/utils/query.ts +44 -78
  61. package/dist/utils/timeParsers/i18n/en.json +9 -9
  62. package/dist/utils/timeParsers/i18n/ru.json +9 -9
  63. package/dist/utils/timeParsers/parsers.ts +9 -0
  64. package/dist/utils/utils.js +1 -2
  65. package/package.json +1 -1
  66. package/dist/services/api.d.ts +0 -86
  67. package/dist/services/api.js +0 -278
@@ -113,52 +113,3 @@ export const renderExplainNode = (node) => {
113
113
  const parts = node.name.split('|');
114
114
  return parts.length > 1 ? parts[1] : node.name;
115
115
  };
116
-
117
- export const getExplainNodeId = (...values) => {
118
- return values.join('|');
119
- };
120
-
121
- const getStringFromProps = (props) => {
122
- return props
123
- .map(([name, value]) => {
124
- return value && `${name}: ${Array.isArray(value) ? value.join(', ') : value}`;
125
- })
126
- .filter(Boolean)
127
- .join('\n');
128
- };
129
-
130
- export const getMetaForExplainNode = (node) => {
131
- switch (node.type) {
132
- case 'MultiLookup':
133
- case 'Lookup': {
134
- return getStringFromProps([
135
- ['lookup by', node.lookup_by],
136
- ['columns', node.columns],
137
- ]);
138
- }
139
- case 'FullScan':
140
- case 'Scan': {
141
- return getStringFromProps([
142
- ['scan by', node.scan_by],
143
- ['limit', node.limit],
144
- ['columns', node.columns],
145
- ]);
146
- }
147
- case 'Upsert':
148
- case 'MultiUpsert': {
149
- return getStringFromProps([
150
- ['key', node.key],
151
- ['columns', node.columns],
152
- ]);
153
- }
154
- case 'Erase':
155
- case 'MultiErase': {
156
- return getStringFromProps([
157
- ['key', node.key],
158
- ['columns', node.columns],
159
- ]);
160
- }
161
- default:
162
- return '';
163
- }
164
- };
@@ -15,6 +15,8 @@ export const NodesUptimeFilterTitles = {
15
15
  export const isUnavailableNode = (node: INodesPreparedEntity | TSystemStateInfo) =>
16
16
  !node.SystemState || node.SystemState === EFlag.Grey;
17
17
 
18
+ export type NodeAddress = Pick<TSystemStateInfo, 'Host' | 'Endpoints'>;
19
+
18
20
  export interface AdditionalNodesInfo extends Record<string, unknown> {
19
- getNodeRef?: Function;
21
+ getNodeRef?: (node?: NodeAddress) => string;
20
22
  }
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  Link,
3
3
  GraphNode,
4
4
  TopologyNodeDataStats,
@@ -7,28 +7,11 @@ import {
7
7
  TopologyNodeDataStatsItem,
8
8
  } from '@gravity-ui/paranoid';
9
9
 
10
- interface PlanOperator {
11
- Name: string;
12
- [key: string]: any;
13
- }
14
-
15
- export interface Plan {
16
- PlanNodeId: number;
17
- 'Node Type': string;
18
- Plans?: Plan[];
19
- Operators?: PlanOperator[];
20
- Tables?: string[];
21
- PlanNodeType?: string;
22
- [key: string]: any;
23
- }
24
-
25
- export interface RootPlan {
26
- Plan: Plan;
27
- }
10
+ import type {PlanNode} from '../types/api/query';
28
11
 
29
12
  const CONNECTION_NODE_META_FIELDS = new Set(['PlanNodeId', 'PlanNodeType', 'Node Type', 'Plans']);
30
13
 
31
- function prepareStats(plan: Plan) {
14
+ function prepareStats(plan: PlanNode) {
32
15
  const stats: TopologyNodeDataStats[] = [];
33
16
 
34
17
  if (plan.Operators) {
@@ -77,7 +60,7 @@ function prepareStats(plan: Plan) {
77
60
  return stats;
78
61
  }
79
62
 
80
- function getNodeType(plan: Plan) {
63
+ function getNodeType(plan: PlanNode) {
81
64
  switch (plan.PlanNodeType) {
82
65
  case 'Connection':
83
66
  return 'connection';
@@ -90,11 +73,11 @@ function getNodeType(plan: Plan) {
90
73
  }
91
74
  }
92
75
 
93
- export function preparePlan(plan: Plan) {
94
- const nodes: GraphNode[] = [];
76
+ export function preparePlan(plan: PlanNode) {
77
+ const nodes: GraphNode<ExplainPlanNodeData>[] = [];
95
78
  const links: Link[] = [];
96
79
 
97
- function parsePlans(plans: Plan[] = [], from: string) {
80
+ function parsePlans(plans: PlanNode[] = [], from: string) {
98
81
  plans.forEach((p) => {
99
82
  const node: GraphNode<ExplainPlanNodeData> = {
100
83
  name: String(p.PlanNodeId),
@@ -1,260 +1,182 @@
1
- import {parseQueryAPIExecuteResponse, parseQueryAPIExplainResponse} from './query';
1
+ import type {PlanMeta, PlanNode, PlanTable, TKqpStatsQuery} from '../types/api/query';
2
+ import {
3
+ parseQueryAPIExecuteResponse,
4
+ parseQueryAPIExplainResponse,
5
+ parseQueryExplainPlan,
6
+ } from './query';
2
7
 
3
8
  describe('API utils', () => {
4
9
  describe('json/viewer/query', () => {
5
10
  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
- });
11
+ describe('should handle responses with incorrect format', () => {
12
+ it('should handle null response', () => {
13
+ expect(parseQueryAPIExecuteResponse(null)).toEqual({});
28
14
  });
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(
46
- response.result,
47
- );
48
- });
15
+ it('should handle undefined response', () => {
16
+ expect(parseQueryAPIExecuteResponse(undefined)).toEqual({});
49
17
  });
50
-
51
- describe('deep response with stats', () => {
52
- it('should parse json string in the result field', () => {
53
- const json = {foo: 'bar'};
54
- const response = {
55
- result: JSON.stringify(json),
56
- stats: {metric: 'good'},
57
- };
58
- const actual = parseQueryAPIExecuteResponse(response);
59
- expect(actual.result).toEqual(json);
60
- expect(actual.stats).toEqual(response.stats);
61
- });
62
-
63
- // it should not be in the response, but is there because of a bug
64
- it('should ignore request plan in the result field', () => {
65
- const response = {
66
- result: {queries: 'some queries'},
67
- stats: {metric: 'good'},
68
- };
69
- const actual = parseQueryAPIExecuteResponse(response);
70
- expect(actual.result).toBeUndefined();
71
- expect(actual.stats).toEqual(response.stats);
72
- });
73
-
74
- it('should accept key-value rows in the result field', () => {
75
- const response = {
76
- result: [{foo: 'bar'}],
77
- stats: {metric: 'good'},
78
- };
79
- const actual = parseQueryAPIExecuteResponse(response);
80
- expect(actual.result).toEqual(response.result);
81
- expect(actual.stats).toEqual(response.stats);
82
- });
83
-
84
- it('should accept stats without a result', () => {
85
- const response = {
86
- stats: {metric: 'good'},
87
- };
88
- const actual = parseQueryAPIExecuteResponse(response);
89
- expect(actual.result).toBeUndefined();
90
- expect(actual.stats).toEqual(response.stats);
91
- });
18
+ it('should handle string response', () => {
19
+ expect(parseQueryAPIExecuteResponse('foo')).toEqual({});
20
+ });
21
+ it('should handle array response', () => {
22
+ expect(parseQueryAPIExecuteResponse([{foo: 'bar'}])).toEqual({});
23
+ });
24
+ it('should handle json string in the result field', () => {
25
+ const json = {foo: 'bar'};
26
+ const response = {result: JSON.stringify(json)};
27
+ expect(parseQueryAPIExecuteResponse(response)).toEqual({});
28
+ });
29
+ it('should handle object with request plan in the result field', () => {
30
+ const response = {result: {queries: 'some queries'}};
31
+ expect(parseQueryAPIExecuteResponse(response)).toEqual({});
92
32
  });
93
33
  });
94
-
95
- describe('new format', () => {
96
- describe('response without stats', () => {
97
- it('should parse modern schema', () => {
98
- const response = {
99
- result: [['42', 'hello world']],
100
- columns: [
101
- {
102
- name: 'id',
103
- type: 'Uint64?',
104
- },
105
- {
106
- name: 'value',
107
- type: 'Utf8?',
108
- },
109
- ],
110
- };
111
- const actual = parseQueryAPIExecuteResponse(response);
112
- expect(actual.result).toEqual([
34
+ describe('should correctly parse data', () => {
35
+ it('should parse modern schema result to KeyValueRow', () => {
36
+ const response = {
37
+ result: [['42', 'hello world']],
38
+ columns: [
113
39
  {
114
- id: '42',
115
- value: 'hello world',
40
+ name: 'id',
41
+ type: 'Uint64?',
116
42
  },
117
- ]);
118
- expect(actual.columns).toEqual(response.columns);
119
- });
120
-
121
- it('should handle empty response for classic schema', () => {
122
- expect(parseQueryAPIExecuteResponse(null).result).toBeUndefined();
123
- });
124
-
125
- it('should parse plain classic schema', () => {
126
- const response = [{foo: 'bar'}];
127
- expect(parseQueryAPIExecuteResponse(response).result).toEqual(response);
128
- });
129
-
130
- it('should parse deep classic schema', () => {
131
- const response = {result: [{foo: 'bar'}]};
132
- expect(parseQueryAPIExecuteResponse(response).result).toEqual(
133
- response.result,
134
- );
135
- });
136
-
137
- it('should parse ydb schema', () => {
138
- const response = {result: [{foo: 'bar'}]};
139
- expect(parseQueryAPIExecuteResponse(response).result).toEqual(
140
- response.result,
141
- );
142
- });
143
- });
144
-
145
- describe('response with stats', () => {
146
- it('should parse modern schema', () => {
147
- const response = {
148
- result: [['42', 'hello world']],
149
- columns: [
150
- {
151
- name: 'id',
152
- type: 'Uint64?',
153
- },
154
- {
155
- name: 'value',
156
- type: 'Utf8?',
157
- },
158
- ],
159
- stats: {metric: 'good'},
160
- };
161
- const actual = parseQueryAPIExecuteResponse(response);
162
- expect(actual.result).toEqual([
163
43
  {
164
- id: '42',
165
- value: 'hello world',
44
+ name: 'value',
45
+ type: 'Utf8?',
166
46
  },
167
- ]);
168
- expect(actual.columns).toEqual(response.columns);
169
- expect(actual.stats).toEqual(response.stats);
170
- });
171
-
172
- it('should parse classic schema', () => {
173
- const response = {
174
- result: [{foo: 'bar'}],
175
- stats: {metric: 'good'},
176
- };
177
- const actual = parseQueryAPIExecuteResponse(response);
178
- expect(actual.result).toEqual(response.result);
179
- expect(actual.stats).toEqual(response.stats);
180
- });
47
+ ],
48
+ };
49
+ const parsedResponse = parseQueryAPIExecuteResponse(response);
50
+
51
+ expect(parsedResponse.result).toEqual([
52
+ {
53
+ id: '42',
54
+ value: 'hello world',
55
+ },
56
+ ]);
57
+ expect(parsedResponse.columns).toEqual(response.columns);
58
+ });
59
+ it('should return KeyValueRow result for ydb and classic schemas unchanged', () => {
60
+ const response = {result: [{foo: 'bar'}]};
61
+ expect(parseQueryAPIExecuteResponse(response).result).toEqual(response.result);
62
+ });
63
+ it('shoudl return stats for modern schema', () => {
64
+ const result = [['42', 'hello world']];
65
+ const columns = [
66
+ {
67
+ name: 'id',
68
+ type: 'Uint64?',
69
+ },
70
+ {
71
+ name: 'value',
72
+ type: 'Utf8?',
73
+ },
74
+ ];
75
+
76
+ const stats = {metric: 'good'} as TKqpStatsQuery;
77
+
78
+ const response = {result, columns, stats};
79
+ const parsedResponse = parseQueryAPIExecuteResponse(response);
80
+
81
+ expect(parsedResponse.result).toEqual([
82
+ {
83
+ id: '42',
84
+ value: 'hello world',
85
+ },
86
+ ]);
87
+ expect(parsedResponse.columns).toEqual(response.columns);
88
+ expect(parsedResponse.stats).toEqual(response.stats);
89
+ });
90
+ it('shoudl return stats for ydb and classic schemas', () => {
91
+ const result = [{foo: 'bar'}];
92
+ const stats = {metric: 'good'} as TKqpStatsQuery;
181
93
 
182
- it('should parse ydb schema', () => {
183
- const response = {
184
- result: [{foo: 'bar'}],
185
- stats: {metric: 'good'},
186
- };
187
- const actual = parseQueryAPIExecuteResponse(response);
188
- expect(actual.result).toEqual(response.result);
189
- expect(actual.stats).toEqual(response.stats);
190
- });
94
+ const response = {result, stats};
95
+ const parsedResponse = parseQueryAPIExecuteResponse(response);
191
96
 
192
- it('should accept stats without a result', () => {
193
- const response = {
194
- stats: {metric: 'good'},
195
- };
196
- const actual = parseQueryAPIExecuteResponse(response);
197
- expect(actual.result).toBeUndefined();
198
- expect(actual.columns).toBeUndefined();
199
- expect(actual.stats).toEqual(response.stats);
200
- });
97
+ expect(parsedResponse.result).toEqual(response.result);
98
+ expect(parsedResponse.stats).toEqual(response.stats);
99
+ });
100
+ it('should accept stats without a result', () => {
101
+ const stats = {metric: 'good'} as TKqpStatsQuery;
102
+ const response = {stats};
103
+ const actual = parseQueryAPIExecuteResponse(response);
104
+ expect(actual.result).toBeUndefined();
105
+ expect(actual.columns).toBeUndefined();
106
+ expect(actual.stats).toEqual(response.stats);
201
107
  });
202
108
  });
203
109
  });
204
110
 
205
111
  describe('parseQueryAPIExplainResponse', () => {
206
- it('should handle empty response', () => {
207
- expect(parseQueryAPIExplainResponse(null)).toEqual({});
208
- });
209
-
210
- it('should accept stats without a plan', () => {
211
- const stats = {metric: 'good'};
212
- expect(parseQueryAPIExplainResponse({stats}).stats).toEqual(stats);
213
- });
214
-
215
- describe('old format', () => {
216
- describe('explain', () => {
217
- it('should parse plan data in the root', () => {
218
- const plan = {foo: 'bar'};
219
- expect(parseQueryAPIExplainResponse(plan).plan).toEqual(plan);
220
- });
221
-
222
- it('should parse plan in the result field with stats', () => {
223
- const plan = {foo: 'bar'};
224
- const stats = {metric: 'good'};
225
- const actual = parseQueryAPIExplainResponse({result: plan, stats});
226
- expect(actual.plan).toEqual(plan);
227
- expect(actual.stats).toEqual(stats);
228
- });
112
+ describe('should handle responses with incorrect format', () => {
113
+ it('should handle null response', () => {
114
+ expect(parseQueryAPIExecuteResponse(null)).toEqual({});
229
115
  });
230
-
231
- describe('explain-ast', () => {
232
- it('should parse ast field in the root', () => {
233
- const ast = 'ast';
234
- expect(parseQueryAPIExplainResponse({ast}).ast).toBe(ast);
235
- });
236
-
237
- it('should parse ast in the result field with stats', () => {
238
- const ast = 'ast';
239
- const stats = {metric: 'good'};
240
- const actual = parseQueryAPIExplainResponse({result: {ast}, stats});
241
- expect(actual.ast).toBe(ast);
242
- expect(actual.stats).toEqual(stats);
243
- });
116
+ it('should handle undefined response', () => {
117
+ expect(parseQueryAPIExecuteResponse(undefined)).toEqual({});
118
+ });
119
+ it('should handle object with plan in the result field', () => {
120
+ const response = {result: {foo: 'bar'}};
121
+ expect(parseQueryAPIExecuteResponse(response)).toEqual({});
244
122
  });
245
123
  });
246
124
 
247
- describe('new format', () => {
248
- it('should parse explain response with stats', () => {
249
- const plan = {foo: 'bar'};
125
+ describe('should correctly parse data', () => {
126
+ it('should parse explain-scan', () => {
127
+ const plan: PlanNode = {};
128
+ const tables: PlanTable[] = [];
129
+ const meta: PlanMeta = {version: '0.2', type: 'script'};
250
130
  const ast = 'ast';
251
- const stats = {metric: 'good'};
252
- const actual = parseQueryAPIExplainResponse({plan, ast, stats});
253
- expect(actual.plan).toEqual(plan);
254
- expect(actual.ast).toBe(ast);
255
- expect(actual.stats).toEqual(stats);
131
+ const response = {plan: {Plan: plan, tables, meta}, ast};
132
+ expect(parseQueryAPIExplainResponse(response)).toBe(response);
133
+ });
134
+ it('should parse explain-script', () => {
135
+ const plan: PlanNode = {};
136
+ const tables: PlanTable[] = [];
137
+ const meta: PlanMeta = {version: '0.2', type: 'script'};
138
+
139
+ const response = {
140
+ plan: {queries: [{Plan: plan, tables}], meta},
141
+ };
142
+ expect(parseQueryAPIExplainResponse(response)).toBe(response);
256
143
  });
257
144
  });
258
145
  });
146
+
147
+ describe('parseQueryExplainPlan', () => {
148
+ it('should parse explain script plan to explain scan', () => {
149
+ const plan: PlanNode = {};
150
+ const tables: PlanTable[] = [];
151
+ const meta: PlanMeta = {version: '0.2', type: 'script'};
152
+
153
+ const rawPlan = {
154
+ queries: [
155
+ {
156
+ Plan: plan,
157
+ tables,
158
+ },
159
+ ],
160
+ meta,
161
+ };
162
+ const parsedPlan = parseQueryExplainPlan(rawPlan);
163
+ expect(parsedPlan.Plan).toEqual(plan);
164
+ expect(parsedPlan.tables).toBe(tables);
165
+ expect(parsedPlan.meta).toEqual(meta);
166
+ });
167
+ it('should left scan plan unchanged', () => {
168
+ const plan: PlanNode = {};
169
+ const tables: PlanTable[] = [];
170
+ const meta: PlanMeta = {version: '0.2', type: 'script'};
171
+
172
+ const rawPlan = {
173
+ Plan: plan,
174
+ tables: tables,
175
+ meta: meta,
176
+ };
177
+ const parsedPlan = parseQueryExplainPlan(rawPlan);
178
+ expect(parsedPlan).toEqual(rawPlan);
179
+ });
180
+ });
259
181
  });
260
182
  });