ydb-embedded-ui 3.5.0 → 4.0.0

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 (37) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
  3. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
  4. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
  5. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
  6. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
  7. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
  8. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
  9. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
  10. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
  11. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
  12. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
  13. package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
  14. package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
  15. package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
  16. package/dist/containers/UserSettings/UserSettings.tsx +30 -1
  17. package/dist/services/api.d.ts +4 -3
  18. package/dist/services/api.js +2 -2
  19. package/dist/store/reducers/executeQuery.ts +12 -37
  20. package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
  21. package/dist/store/reducers/settings.js +18 -3
  22. package/dist/types/api/error.ts +14 -0
  23. package/dist/types/api/query.ts +226 -117
  24. package/dist/types/store/executeQuery.ts +4 -8
  25. package/dist/types/store/explainQuery.ts +38 -0
  26. package/dist/types/store/query.ts +23 -3
  27. package/dist/utils/constants.ts +2 -1
  28. package/dist/utils/error.ts +25 -0
  29. package/dist/utils/index.js +0 -49
  30. package/dist/utils/prepareQueryExplain.ts +7 -24
  31. package/dist/utils/query.test.ts +153 -231
  32. package/dist/utils/query.ts +44 -78
  33. package/dist/utils/timeParsers/i18n/en.json +9 -9
  34. package/dist/utils/timeParsers/i18n/ru.json +9 -9
  35. package/dist/utils/timeParsers/parsers.ts +9 -0
  36. package/dist/utils/utils.js +1 -2
  37. package/package.json +1 -1
@@ -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
  });
@@ -2,13 +2,10 @@ import {YQLType} from '../types';
2
2
  import type {
3
3
  AnyExecuteResponse,
4
4
  AnyExplainResponse,
5
- AnyResponse,
6
- CommonFields,
7
- DeepExecuteResponse,
8
- DeprecatedExecuteResponsePlain,
9
- ExecuteClassicResponsePlain,
10
5
  ExecuteModernResponse,
11
6
  KeyValueRow,
7
+ ScanPlan,
8
+ ScriptPlan,
12
9
  } from '../types/api/query';
13
10
  import type {IQueryResult} from '../types/store/query';
14
11
 
@@ -49,6 +46,7 @@ export const getColumnType = (type: string) => {
49
46
  }
50
47
  };
51
48
 
49
+ /** parse response result field from ArrayRow to KeyValueRow */
52
50
  const parseExecuteModernResponse = (data: ExecuteModernResponse): IQueryResult => {
53
51
  const {result, columns, ...restData} = data;
54
52
 
@@ -57,43 +55,17 @@ const parseExecuteModernResponse = (data: ExecuteModernResponse): IQueryResult =
57
55
  result &&
58
56
  columns &&
59
57
  result.map((row) => {
60
- return row.reduce((newRow: KeyValueRow, cellData, columnIndex) => {
58
+ return row.reduce((newRow, cellData, columnIndex) => {
61
59
  const {name} = columns[columnIndex];
62
60
  newRow[name] = cellData;
63
61
  return newRow;
64
- }, {});
62
+ }, {} as KeyValueRow);
65
63
  }),
66
64
  columns,
67
65
  ...restData,
68
66
  };
69
67
  };
70
68
 
71
- const parseDeprecatedExecuteResponseValue = (
72
- data?: DeprecatedExecuteResponsePlain | ExecuteClassicResponsePlain,
73
- ): KeyValueRow[] | undefined => {
74
- if (!data) {
75
- return undefined;
76
- }
77
-
78
- if (typeof data === 'string') {
79
- try {
80
- return JSON.parse(data);
81
- } catch (e) {
82
- return undefined;
83
- }
84
- }
85
-
86
- if (Array.isArray(data)) {
87
- return data;
88
- }
89
-
90
- // Plan is not a valid response in this case
91
- return undefined;
92
- };
93
-
94
- const hasResult = (data: AnyExecuteResponse): data is DeepExecuteResponse =>
95
- Boolean(data && typeof data === 'object' && 'result' in data);
96
-
97
69
  const isModern = (response: AnyExecuteResponse): response is ExecuteModernResponse =>
98
70
  Boolean(
99
71
  response &&
@@ -102,70 +74,64 @@ const isModern = (response: AnyExecuteResponse): response is ExecuteModernRespon
102
74
  Array.isArray((response as ExecuteModernResponse).columns),
103
75
  );
104
76
 
105
- const hasCommonFields = (data: AnyResponse): data is CommonFields =>
106
- Boolean(
107
- data && typeof data === 'object' && ('ast' in data || 'plan' in data || 'stats' in data),
77
+ type UnsupportedQueryResponseFormat =
78
+ | Array<unknown>
79
+ | string
80
+ | null
81
+ | undefined
82
+ | {result: string | Record<string, unknown>};
83
+
84
+ const isUnsupportedType = (
85
+ data: AnyExecuteResponse | AnyExplainResponse | UnsupportedQueryResponseFormat,
86
+ ): data is UnsupportedQueryResponseFormat => {
87
+ return Boolean(
88
+ !data ||
89
+ typeof data !== 'object' ||
90
+ Array.isArray(data) ||
91
+ ('result' in data && !Array.isArray(data.result)),
108
92
  );
93
+ };
109
94
 
110
- // complex logic because of the variety of possible responses
111
- // after all backends are updated to the latest version, it can be simplified
112
- export const parseQueryAPIExecuteResponse = (data: AnyExecuteResponse): IQueryResult => {
113
- if (!data) {
95
+ export const parseQueryAPIExecuteResponse = (
96
+ data: AnyExecuteResponse | UnsupportedQueryResponseFormat,
97
+ ): IQueryResult => {
98
+ if (isUnsupportedType(data)) {
114
99
  return {};
115
100
  }
116
-
117
- if (hasResult(data)) {
118
- if (isModern(data)) {
119
- return parseExecuteModernResponse(data);
120
- }
121
-
122
- return {
123
- ...data,
124
- result: parseDeprecatedExecuteResponseValue(data.result),
125
- };
126
- }
127
-
128
- if (hasCommonFields(data)) {
129
- return data;
101
+ if (isModern(data)) {
102
+ return parseExecuteModernResponse(data);
130
103
  }
131
104
 
132
- return {
133
- result: parseDeprecatedExecuteResponseValue(data),
134
- };
105
+ return data;
135
106
  };
136
107
 
137
- // complex logic because of the variety of possible responses
138
- // after all backends are updated to the latest version, it can be simplified
139
- export const parseQueryAPIExplainResponse = (data: AnyExplainResponse): IQueryResult => {
140
- if (!data) {
108
+ export const parseQueryAPIExplainResponse = (
109
+ data: AnyExplainResponse | UnsupportedQueryResponseFormat,
110
+ ): IQueryResult => {
111
+ if (isUnsupportedType(data)) {
141
112
  return {};
142
113
  }
143
114
 
144
- if ('ast' in data) {
145
- return data;
146
- }
115
+ return data;
116
+ };
147
117
 
148
- if ('result' in data) {
149
- const {result, ...restData} = data;
118
+ const isExplainScriptPlan = (plan: ScriptPlan | ScanPlan): plan is ScriptPlan =>
119
+ Boolean(plan && 'queries' in plan);
150
120
 
151
- if ('ast' in data.result) {
152
- return {
153
- ast: result.ast,
154
- ...restData,
155
- };
121
+ export const parseQueryExplainPlan = (plan: ScriptPlan | ScanPlan): ScanPlan => {
122
+ if (isExplainScriptPlan(plan)) {
123
+ if (!plan.queries || !plan.queries.length) {
124
+ return {meta: plan.meta};
156
125
  }
157
126
 
158
127
  return {
159
- plan: data.result,
160
- ...restData,
128
+ Plan: plan.queries[0].Plan,
129
+ tables: plan.queries[0].tables,
130
+ meta: plan.meta,
161
131
  };
162
132
  }
163
133
 
164
- if (hasCommonFields(data)) {
165
- return data;
166
- }
167
-
168
- return {plan: data};
134
+ return plan;
169
135
  };
170
136
 
171
137
  export const prepareQueryResponse = (data?: KeyValueRow[]) => {
@@ -1,11 +1,11 @@
1
1
  {
2
- "daysHours": "{{days}}d {{hours}}h",
3
- "hoursMin": "{{hours}}h {{minutes}}m",
4
- "minSec": "{{minutes}}m {{seconds}}s",
5
- "secMs": "{{seconds}}s {{ms}}ms",
6
- "days": "{{days}}d",
7
- "hours": "{{hours}}h",
8
- "min": "{{minutes}}m",
9
- "sec": "{{seconds}}s",
10
- "ms": "{{ms}}ms"
2
+ "daysHours": "{{days}}\u00a0d\u00a0{{hours}}\u00a0h",
3
+ "hoursMin": "{{hours}}\u00a0h\u00a0{{minutes}}\u00a0m",
4
+ "minSec": "{{minutes}}\u00a0m\u00a0{{seconds}}\u00a0s",
5
+ "secMs": "{{seconds}}\u00a0s\u00a0{{ms}}\u00a0ms",
6
+ "days": "{{days}}\u00a0d",
7
+ "hours": "{{hours}}\u00a0h",
8
+ "min": "{{minutes}}\u00a0m",
9
+ "sec": "{{seconds}}\u00a0s",
10
+ "ms": "{{ms}}\u00a0ms"
11
11
  }
@@ -1,11 +1,11 @@
1
1
  {
2
- "daysHours": "{{days}}д {{hours}}ч",
3
- "hoursMin": "{{hours}}ч {{minutes}}м",
4
- "minSec": "{{minutes}}м {{seconds}}с",
5
- "secMs": "{{seconds}}с {{ms}}мс",
6
- "days": "{{days}}д",
7
- "hours": "{{hours}}ч",
8
- "min": "{{minutes}}м",
9
- "sec": "{{seconds}}с",
10
- "ms": "{{ms}}мс"
2
+ "daysHours": "{{days}}\u00a0д\u00a0{{hours}}\u00a0ч",
3
+ "hoursMin": "{{hours}}\u00a0ч\u00a0{{minutes}}\u00a0м",
4
+ "minSec": "{{minutes}}\u00a0м\u00a0{{seconds}}\u00a0с",
5
+ "secMs": "{{seconds}}\u00a0с\u00a0{{ms}}\u00a0мс",
6
+ "days": "{{days}}\u00a0д",
7
+ "hours": "{{hours}}\u00a0ч",
8
+ "min": "{{minutes}}\u00a0м",
9
+ "sec": "{{seconds}}\u00a0с",
10
+ "ms": "{{ms}}\u00a0мс"
11
11
  }
@@ -1,5 +1,6 @@
1
1
  import type {IProtobufTimeObject} from '../../types/api/common';
2
2
 
3
+ import {isNumeric} from '../utils';
3
4
  import {parseProtobufDurationToMs, parseProtobufTimestampToMs} from '.';
4
5
 
5
6
  export const parseLag = (value: string | IProtobufTimeObject | undefined) =>
@@ -16,3 +17,11 @@ export const parseTimestampToIdleTime = (value: string | IProtobufTimeObject | u
16
17
  // Usually it below 100ms, so it could be omitted
17
18
  return duration < 0 ? 0 : duration;
18
19
  };
20
+
21
+ export const parseUsToMs = (value: string | number | undefined) => {
22
+ if (!value || !isNumeric(value)) {
23
+ return 0;
24
+ }
25
+
26
+ return Math.round(Number(value) / 1000);
27
+ };