ydb-embedded-ui 3.0.1 → 3.1.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.
- package/CHANGELOG.md +7 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.tsx +75 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.scss +20 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +70 -14
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +4 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +4 -0
- package/dist/store/reducers/shardsWorkload.ts +87 -21
- package/dist/store/state-url-mapping.js +8 -0
- package/dist/types/store/shardsWorkload.ts +11 -2
- package/dist/utils/query.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.1.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.1...v3.1.0) (2022-12-13)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **TopShards:** date range filter ([aab4396](https://github.com/ydb-platform/ydb-embedded-ui/commit/aab439600ec28d30799c4a7ef7a9c68fcacc148c))
|
9
|
+
|
3
10
|
## [3.0.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.0.0...v3.0.1) (2022-12-12)
|
4
11
|
|
5
12
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
.top-shards {
|
2
|
+
&__date-range {
|
3
|
+
&-input {
|
4
|
+
min-width: 190px;
|
5
|
+
padding: 5px 8px;
|
6
|
+
|
7
|
+
color: var(--yc-color-text-primary);
|
8
|
+
border: 1px solid var(--yc-color-line-generic);
|
9
|
+
border-radius: var(--yc-border-radius-m);
|
10
|
+
background: transparent;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
import {ChangeEventHandler} from 'react';
|
3
|
+
|
4
|
+
import './DateRange.scss';
|
5
|
+
|
6
|
+
const b = cn('top-shards');
|
7
|
+
|
8
|
+
export interface DateRangeValues {
|
9
|
+
/** ms from epoch */
|
10
|
+
from?: number;
|
11
|
+
/** ms from epoch */
|
12
|
+
to?: number;
|
13
|
+
}
|
14
|
+
|
15
|
+
interface DateRangeProps extends DateRangeValues {
|
16
|
+
className?: string;
|
17
|
+
onChange?: (value: DateRangeValues) => void;
|
18
|
+
}
|
19
|
+
|
20
|
+
const toTimezonelessISOString = (timestamp?: number) => {
|
21
|
+
if (!timestamp || isNaN(timestamp)) {
|
22
|
+
return undefined;
|
23
|
+
}
|
24
|
+
|
25
|
+
// shift by local offset to treat toISOString output as local time
|
26
|
+
const shiftedTimestamp = timestamp - new Date().getTimezoneOffset() * 60 * 1000;
|
27
|
+
return new Date(shiftedTimestamp).toISOString().substring(0, 'yyyy-MM-DDThh:mm'.length);
|
28
|
+
};
|
29
|
+
|
30
|
+
export const DateRange = ({from, to, className, onChange}: DateRangeProps) => {
|
31
|
+
const handleFromChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
|
32
|
+
let newFrom = value ? new Date(value).getTime() : undefined;
|
33
|
+
|
34
|
+
// some browsers allow selecting time after the boundary specified in `max`
|
35
|
+
if (newFrom && to && newFrom > to) {
|
36
|
+
newFrom = to;
|
37
|
+
}
|
38
|
+
|
39
|
+
onChange?.({from: newFrom, to});
|
40
|
+
};
|
41
|
+
|
42
|
+
const handleToChange: ChangeEventHandler<HTMLInputElement> = ({target: {value}}) => {
|
43
|
+
let newTo = value ? new Date(value).getTime() : undefined;
|
44
|
+
|
45
|
+
// some browsers allow selecting time before the boundary specified in `min`
|
46
|
+
if (from && newTo && from > newTo) {
|
47
|
+
newTo = from;
|
48
|
+
}
|
49
|
+
|
50
|
+
onChange?.({from, to: newTo});
|
51
|
+
};
|
52
|
+
|
53
|
+
const startISO = toTimezonelessISOString(from);
|
54
|
+
const endISO = toTimezonelessISOString(to);
|
55
|
+
|
56
|
+
return (
|
57
|
+
<div className={b('date-range', className)}>
|
58
|
+
<input
|
59
|
+
type="datetime-local"
|
60
|
+
value={startISO}
|
61
|
+
max={endISO}
|
62
|
+
onChange={handleFromChange}
|
63
|
+
className={b('date-range-input')}
|
64
|
+
/>
|
65
|
+
—
|
66
|
+
<input
|
67
|
+
type="datetime-local"
|
68
|
+
min={startISO}
|
69
|
+
value={endISO}
|
70
|
+
onChange={handleToChange}
|
71
|
+
className={b('date-range-input')}
|
72
|
+
/>
|
73
|
+
</div>
|
74
|
+
);
|
75
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './DateRange';
|
@@ -1,7 +1,27 @@
|
|
1
1
|
.top-shards {
|
2
|
+
display: flex;
|
3
|
+
flex-direction: column;
|
4
|
+
|
5
|
+
height: 100%;
|
6
|
+
|
2
7
|
background-color: var(--yc-color-base-background);
|
8
|
+
|
3
9
|
&__loader {
|
4
10
|
display: flex;
|
5
11
|
justify-content: center;
|
6
12
|
}
|
13
|
+
|
14
|
+
&__controls {
|
15
|
+
display: flex;
|
16
|
+
flex-wrap: wrap;
|
17
|
+
align-items: baseline;
|
18
|
+
gap: 16px;
|
19
|
+
|
20
|
+
margin-bottom: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
&__table {
|
24
|
+
overflow: auto;
|
25
|
+
flex-grow: 1;
|
26
|
+
}
|
7
27
|
}
|
@@ -11,18 +11,28 @@ import HistoryContext from '../../../../contexts/HistoryContext';
|
|
11
11
|
|
12
12
|
import routes, {createHref} from '../../../../routes';
|
13
13
|
|
14
|
-
import {
|
14
|
+
import {
|
15
|
+
sendShardQuery,
|
16
|
+
setShardQueryOptions,
|
17
|
+
setTopShardFilters,
|
18
|
+
} from '../../../../store/reducers/shardsWorkload';
|
15
19
|
import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
|
20
|
+
import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
|
16
21
|
|
17
22
|
import type {EPathType} from '../../../../types/api/schema';
|
18
23
|
|
19
|
-
import {
|
24
|
+
import {formatDateTime, formatNumber} from '../../../../utils';
|
25
|
+
import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
|
20
26
|
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
21
|
-
import {i18n} from '../../../../utils/i18n';
|
22
27
|
import {prepareQueryError} from '../../../../utils/query';
|
23
28
|
|
29
|
+
import {getDefaultNodePath} from '../../../Node/NodePages';
|
30
|
+
|
24
31
|
import {isColumnEntityType} from '../../utils/schema';
|
25
32
|
|
33
|
+
import {DateRange, DateRangeValues} from './DateRange';
|
34
|
+
|
35
|
+
import i18n from './i18n';
|
26
36
|
import './TopShards.scss';
|
27
37
|
|
28
38
|
const b = cn('top-shards');
|
@@ -41,16 +51,15 @@ const tableColumnsNames = {
|
|
41
51
|
CPUCores: 'CPUCores',
|
42
52
|
DataSize: 'DataSize',
|
43
53
|
Path: 'Path',
|
54
|
+
NodeId: 'NodeId',
|
55
|
+
PeakTime: 'PeakTime',
|
56
|
+
InFlightTxCount: 'InFlightTxCount',
|
44
57
|
};
|
45
58
|
|
46
59
|
function prepareCPUWorkloadValue(value: string) {
|
47
60
|
return `${(Number(value) * 100).toFixed(2)}%`;
|
48
61
|
}
|
49
62
|
|
50
|
-
function prepareDateSizeValue(value: number) {
|
51
|
-
return new Intl.NumberFormat(i18n.lang).format(value);
|
52
|
-
}
|
53
|
-
|
54
63
|
function stringToDataTableSortOrder(value: string): SortOrder[] | undefined {
|
55
64
|
return value
|
56
65
|
? value.split(',').map((columnId) => ({
|
@@ -87,10 +96,24 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
87
96
|
const {
|
88
97
|
loading,
|
89
98
|
data: {result: data = undefined} = {},
|
99
|
+
filters: storeFilters,
|
90
100
|
error,
|
91
101
|
wasLoaded,
|
92
102
|
} = useTypedSelector((state) => state.shardsWorkload);
|
93
103
|
|
104
|
+
// default date range should be the last hour, but shouldn't propagate into URL until user interacts with the control
|
105
|
+
// redux initial value can't be used, as it synchronizes with URL
|
106
|
+
const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
|
107
|
+
if (!storeFilters?.from && !storeFilters?.to) {
|
108
|
+
return {
|
109
|
+
from: Date.now() - HOUR_IN_SECONDS * 1000,
|
110
|
+
to: Date.now(),
|
111
|
+
};
|
112
|
+
}
|
113
|
+
|
114
|
+
return storeFilters;
|
115
|
+
});
|
116
|
+
|
94
117
|
const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
|
95
118
|
|
96
119
|
useAutofetcher(
|
@@ -100,10 +123,11 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
100
123
|
database: tenantPath,
|
101
124
|
path: currentSchemaPath,
|
102
125
|
sortOrder: stringToQuerySortOrder(sortOrder),
|
126
|
+
filters,
|
103
127
|
}),
|
104
128
|
);
|
105
129
|
},
|
106
|
-
[dispatch, currentSchemaPath,
|
130
|
+
[dispatch, tenantPath, currentSchemaPath, sortOrder, filters],
|
107
131
|
autorefresh,
|
108
132
|
);
|
109
133
|
|
@@ -115,7 +139,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
115
139
|
data: undefined,
|
116
140
|
}),
|
117
141
|
);
|
118
|
-
}, [dispatch, currentSchemaPath, tenantPath]);
|
142
|
+
}, [dispatch, currentSchemaPath, tenantPath, filters]);
|
119
143
|
|
120
144
|
const history = useContext(HistoryContext);
|
121
145
|
|
@@ -126,6 +150,11 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
126
150
|
setSortOrder(dataTableToStringSortOrder(newSortOrder));
|
127
151
|
};
|
128
152
|
|
153
|
+
const handleDateRangeChange = (value: DateRangeValues) => {
|
154
|
+
dispatch(setTopShardFilters(value));
|
155
|
+
setFilters(value);
|
156
|
+
};
|
157
|
+
|
129
158
|
const tableColumns: Column<any>[] = useMemo(() => {
|
130
159
|
const onSchemaClick = (schemaPath: string) => {
|
131
160
|
return () => {
|
@@ -161,7 +190,7 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
161
190
|
name: tableColumnsNames.DataSize,
|
162
191
|
header: 'DataSize (B)',
|
163
192
|
render: ({value}) => {
|
164
|
-
return
|
193
|
+
return formatNumber(value as number);
|
165
194
|
},
|
166
195
|
align: DataTable.RIGHT,
|
167
196
|
},
|
@@ -176,6 +205,29 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
176
205
|
},
|
177
206
|
sortable: false,
|
178
207
|
},
|
208
|
+
{
|
209
|
+
name: tableColumnsNames.NodeId,
|
210
|
+
render: ({value: nodeId}) => {
|
211
|
+
return (
|
212
|
+
<InternalLink to={getDefaultNodePath(nodeId as string)}>
|
213
|
+
{nodeId as string}
|
214
|
+
</InternalLink>
|
215
|
+
);
|
216
|
+
},
|
217
|
+
align: DataTable.RIGHT,
|
218
|
+
sortable: false,
|
219
|
+
},
|
220
|
+
{
|
221
|
+
name: tableColumnsNames.PeakTime,
|
222
|
+
render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
|
223
|
+
sortable: false,
|
224
|
+
},
|
225
|
+
{
|
226
|
+
name: tableColumnsNames.InFlightTxCount,
|
227
|
+
render: ({value}) => formatNumber(value as number),
|
228
|
+
align: DataTable.RIGHT,
|
229
|
+
sortable: false,
|
230
|
+
},
|
179
231
|
];
|
180
232
|
}, [dispatch, history, tenantPath]);
|
181
233
|
|
@@ -192,12 +244,12 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
192
244
|
return renderLoader();
|
193
245
|
}
|
194
246
|
|
195
|
-
if (
|
196
|
-
return
|
247
|
+
if (error && !error.isCancelled) {
|
248
|
+
return <div className="error">{prepareQueryError(error)}</div>;
|
197
249
|
}
|
198
250
|
|
199
|
-
if (
|
200
|
-
return
|
251
|
+
if (!data || isColumnEntityType(type)) {
|
252
|
+
return i18n('no-data');
|
201
253
|
}
|
202
254
|
|
203
255
|
return (
|
@@ -216,6 +268,10 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
|
|
216
268
|
|
217
269
|
return (
|
218
270
|
<div className={b()}>
|
271
|
+
<div className={b('controls')}>
|
272
|
+
{i18n('description')}
|
273
|
+
<DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
|
274
|
+
</div>
|
219
275
|
{renderContent()}
|
220
276
|
</div>
|
221
277
|
);
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-diagnostics-top-shards';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -1,7 +1,11 @@
|
|
1
1
|
import type {Reducer} from 'redux';
|
2
2
|
|
3
3
|
import '../../services/api';
|
4
|
-
import type {
|
4
|
+
import type {
|
5
|
+
IShardsWorkloadAction,
|
6
|
+
IShardsWorkloadFilters,
|
7
|
+
IShardsWorkloadState,
|
8
|
+
} from '../../types/store/shardsWorkload';
|
5
9
|
|
6
10
|
import {parseQueryAPIExecuteResponse} from '../../utils/query';
|
7
11
|
|
@@ -9,10 +13,12 @@ import {createRequestActionTypes, createApiRequest} from '../utils';
|
|
9
13
|
|
10
14
|
export const SEND_SHARD_QUERY = createRequestActionTypes('query', 'SEND_SHARD_QUERY');
|
11
15
|
const SET_SHARD_QUERY_OPTIONS = 'query/SET_SHARD_QUERY_OPTIONS';
|
16
|
+
const SET_TOP_SHARDS_FILTERS = 'shardsWorkload/SET_TOP_SHARDS_FILTERS';
|
12
17
|
|
13
18
|
const initialState = {
|
14
19
|
loading: false,
|
15
20
|
wasLoaded: false,
|
21
|
+
filters: {},
|
16
22
|
};
|
17
23
|
|
18
24
|
export interface SortOrder {
|
@@ -24,22 +30,56 @@ function formatSortOrder({columnId, order}: SortOrder) {
|
|
24
30
|
return `${columnId} ${order}`;
|
25
31
|
}
|
26
32
|
|
27
|
-
function
|
28
|
-
const
|
33
|
+
function getFiltersConditions(filters?: IShardsWorkloadFilters) {
|
34
|
+
const conditions: string[] = [];
|
35
|
+
|
36
|
+
if (filters?.from && filters?.to && filters.from > filters.to) {
|
37
|
+
throw new Error('Invalid date range');
|
38
|
+
}
|
29
39
|
|
40
|
+
if (filters?.from) {
|
41
|
+
// matching `from` & `to` is an edge case
|
42
|
+
// other cases should not include the starting point, since intervals are stored using the ending time
|
43
|
+
const gt = filters.to === filters.from ? '>=' : '>';
|
44
|
+
conditions.push(`IntervalEnd ${gt} Timestamp('${new Date(filters.from).toISOString()}')`);
|
45
|
+
}
|
46
|
+
|
47
|
+
if (filters?.to) {
|
48
|
+
conditions.push(`IntervalEnd <= Timestamp('${new Date(filters.to).toISOString()}')`);
|
49
|
+
}
|
50
|
+
|
51
|
+
return conditions.join(' AND ');
|
52
|
+
}
|
53
|
+
|
54
|
+
function createShardQuery(
|
55
|
+
path: string,
|
56
|
+
filters?: IShardsWorkloadFilters,
|
57
|
+
sortOrder?: SortOrder[],
|
58
|
+
tenantName?: string,
|
59
|
+
) {
|
30
60
|
const pathSelect = tenantName
|
31
61
|
? `CAST(SUBSTRING(CAST(Path AS String), ${tenantName.length}) AS Utf8) AS Path`
|
32
62
|
: 'Path';
|
33
63
|
|
64
|
+
let where = `Path='${path}' OR Path LIKE '${path}/%'`;
|
65
|
+
|
66
|
+
const filterConditions = getFiltersConditions(filters);
|
67
|
+
if (filterConditions.length) {
|
68
|
+
where = `(${where}) AND ${filterConditions}`;
|
69
|
+
}
|
70
|
+
|
71
|
+
const orderBy = sortOrder ? `ORDER BY ${sortOrder.map(formatSortOrder).join(', ')}` : '';
|
72
|
+
|
34
73
|
return `SELECT
|
35
74
|
${pathSelect},
|
36
75
|
TabletId,
|
37
76
|
CPUCores,
|
38
|
-
DataSize
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
77
|
+
DataSize,
|
78
|
+
NodeId,
|
79
|
+
PeakTime,
|
80
|
+
InFlightTxCount
|
81
|
+
FROM \`.sys/top_partitions_one_hour\`
|
82
|
+
WHERE ${where}
|
43
83
|
${orderBy}
|
44
84
|
LIMIT 20`;
|
45
85
|
}
|
@@ -80,6 +120,14 @@ const shardsWorkload: Reducer<IShardsWorkloadState, IShardsWorkloadAction> = (
|
|
80
120
|
...state,
|
81
121
|
...action.data,
|
82
122
|
};
|
123
|
+
case SET_TOP_SHARDS_FILTERS:
|
124
|
+
return {
|
125
|
+
...state,
|
126
|
+
filters: {
|
127
|
+
...state.filters,
|
128
|
+
...action.filters,
|
129
|
+
},
|
130
|
+
};
|
83
131
|
default:
|
84
132
|
return state;
|
85
133
|
}
|
@@ -89,21 +137,32 @@ interface SendShardQueryParams {
|
|
89
137
|
database?: string;
|
90
138
|
path?: string;
|
91
139
|
sortOrder?: SortOrder[];
|
140
|
+
filters?: IShardsWorkloadFilters;
|
92
141
|
}
|
93
142
|
|
94
|
-
export const sendShardQuery = ({database, path = '', sortOrder}: SendShardQueryParams) => {
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
143
|
+
export const sendShardQuery = ({database, path = '', sortOrder, filters}: SendShardQueryParams) => {
|
144
|
+
try {
|
145
|
+
return createApiRequest({
|
146
|
+
request: window.api.sendQuery(
|
147
|
+
{
|
148
|
+
schema: 'modern',
|
149
|
+
query: createShardQuery(path, filters, sortOrder, database),
|
150
|
+
database,
|
151
|
+
action: queryAction,
|
152
|
+
},
|
153
|
+
{
|
154
|
+
concurrentId: 'topShards',
|
155
|
+
},
|
156
|
+
),
|
157
|
+
actions: SEND_SHARD_QUERY,
|
158
|
+
dataHandler: parseQueryAPIExecuteResponse,
|
159
|
+
});
|
160
|
+
} catch (error) {
|
161
|
+
return {
|
162
|
+
type: SEND_SHARD_QUERY.FAILURE,
|
163
|
+
error,
|
164
|
+
};
|
165
|
+
}
|
107
166
|
};
|
108
167
|
|
109
168
|
export function setShardQueryOptions(options: Partial<IShardsWorkloadState>) {
|
@@ -113,4 +172,11 @@ export function setShardQueryOptions(options: Partial<IShardsWorkloadState>) {
|
|
113
172
|
} as const;
|
114
173
|
}
|
115
174
|
|
175
|
+
export function setTopShardFilters(filters: Partial<IShardsWorkloadFilters>) {
|
176
|
+
return {
|
177
|
+
type: SET_TOP_SHARDS_FILTERS,
|
178
|
+
filters,
|
179
|
+
} as const;
|
180
|
+
}
|
181
|
+
|
116
182
|
export default shardsWorkload;
|
@@ -48,6 +48,14 @@ const paramSetup = {
|
|
48
48
|
generalTab: {
|
49
49
|
stateKey: 'tenant.diagnosticsTab',
|
50
50
|
},
|
51
|
+
topShardsFrom: {
|
52
|
+
stateKey: 'shardsWorkload.filters.from',
|
53
|
+
type: 'number',
|
54
|
+
},
|
55
|
+
topShardsTo: {
|
56
|
+
stateKey: 'shardsWorkload.filters.to',
|
57
|
+
type: 'number',
|
58
|
+
},
|
51
59
|
},
|
52
60
|
};
|
53
61
|
|
@@ -1,18 +1,27 @@
|
|
1
|
-
import {SEND_SHARD_QUERY, setShardQueryOptions} from '../../store/reducers/shardsWorkload';
|
1
|
+
import {SEND_SHARD_QUERY, setShardQueryOptions, setTopShardFilters} from '../../store/reducers/shardsWorkload';
|
2
2
|
import type {ApiRequestAction} from '../../store/utils';
|
3
3
|
import type {IResponseError} from '../api/error';
|
4
4
|
import type {IQueryResult} from './query';
|
5
5
|
|
6
|
+
export interface IShardsWorkloadFilters {
|
7
|
+
/** ms from epoch */
|
8
|
+
from?: number;
|
9
|
+
/** ms from epoch */
|
10
|
+
to?: number;
|
11
|
+
}
|
12
|
+
|
6
13
|
export interface IShardsWorkloadState {
|
7
14
|
loading: boolean;
|
8
15
|
wasLoaded: boolean;
|
9
16
|
data?: IQueryResult;
|
10
17
|
error?: IResponseError;
|
18
|
+
filters: IShardsWorkloadFilters;
|
11
19
|
}
|
12
20
|
|
13
21
|
export type IShardsWorkloadAction =
|
14
22
|
| ApiRequestAction<typeof SEND_SHARD_QUERY, IQueryResult, IResponseError>
|
15
|
-
| ReturnType<typeof setShardQueryOptions
|
23
|
+
| ReturnType<typeof setShardQueryOptions>
|
24
|
+
| ReturnType<typeof setTopShardFilters>;
|
16
25
|
|
17
26
|
export interface IShardsWorkloadRootStateSlice {
|
18
27
|
shardsWorkload: IShardsWorkloadState;
|
package/dist/utils/query.ts
CHANGED
@@ -186,5 +186,5 @@ export const prepareQueryResponse = (data?: KeyValueRow[]) => {
|
|
186
186
|
};
|
187
187
|
|
188
188
|
export function prepareQueryError(error: any) {
|
189
|
-
return error.data?.error?.message || error.data || error.statusText || JSON.stringify(error);
|
189
|
+
return error.data?.error?.message || error.message || error.data || error.statusText || JSON.stringify(error);
|
190
190
|
}
|