ydb-embedded-ui 3.5.0 → 4.0.0

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