ydb-embedded-ui 2.4.3 → 2.4.4
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 +13 -0
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +91 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +17 -31
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -7
- package/dist/containers/Tenant/Tenant.tsx +8 -5
- package/dist/services/api.js +14 -5
- package/dist/store/reducers/describe.ts +60 -30
- package/dist/types/api/error.ts +4 -2
- package/dist/types/store/describe.ts +21 -1
- package/package.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.js +0 -130
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.4.4](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.3...v2.4.4) (2022-11-22)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* **api:** update getDescribe and getSchema requests params ([d70ba54](https://github.com/ydb-platform/ydb-embedded-ui/commit/d70ba54b90b9c86a393bd3f7845183114e5afbf1))
|
9
|
+
* **describe:** cancel concurrent requests ([2f39ad0](https://github.com/ydb-platform/ydb-embedded-ui/commit/2f39ad0f736d44c3749d9523f5024151c51fcf6f))
|
10
|
+
* **Describe:** render loader on path change ([baf552a](https://github.com/ydb-platform/ydb-embedded-ui/commit/baf552af8bb67046baa36e9115064b4b192cb015))
|
11
|
+
* **QueryExplain:** fix colors on theme change ([cc0a2d6](https://github.com/ydb-platform/ydb-embedded-ui/commit/cc0a2d67139457748089c6bf1fb1045b0a6b0b93))
|
12
|
+
* **SchemaTree:** remove unneeded fetches ([c7c0489](https://github.com/ydb-platform/ydb-embedded-ui/commit/c7c048937c5ae9e5e243d6e538aab8c2e2921df5))
|
13
|
+
* **SchemaTree:** remove unneeded getDescribe ([1146f13](https://github.com/ydb-platform/ydb-embedded-ui/commit/1146f13a7a5a277a292b3789d45a0872dda0c487))
|
14
|
+
* **Tenant:** make tenant fetch schema only on tenant change ([ccefbff](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccefbffea08fc8f248a3dd1135e82de6db9f0645))
|
15
|
+
|
3
16
|
## [2.4.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.2...v2.4.3) (2022-11-14)
|
4
17
|
|
5
18
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import {useCallback} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
// @ts-ignore
|
5
|
+
import JSONTree from 'react-json-inspector';
|
6
|
+
import 'react-json-inspector/json-inspector.css';
|
7
|
+
|
8
|
+
import {Loader} from '@gravity-ui/uikit';
|
9
|
+
|
10
|
+
import {prepareQueryError} from '../../../../utils/query';
|
11
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
12
|
+
import {
|
13
|
+
getDescribe,
|
14
|
+
setDataWasNotLoaded,
|
15
|
+
setCurrentDescribePath,
|
16
|
+
} from '../../../../store/reducers/describe';
|
17
|
+
|
18
|
+
import './Describe.scss';
|
19
|
+
|
20
|
+
const b = cn('kv-describe');
|
21
|
+
|
22
|
+
const expandMap = new Map();
|
23
|
+
|
24
|
+
interface IDescribeProps {
|
25
|
+
tenant: string;
|
26
|
+
}
|
27
|
+
|
28
|
+
const Describe = ({tenant}: IDescribeProps) => {
|
29
|
+
const dispatch = useDispatch();
|
30
|
+
|
31
|
+
const {currentDescribe, error, loading, wasLoaded} = useTypedSelector(
|
32
|
+
(state) => state.describe,
|
33
|
+
);
|
34
|
+
|
35
|
+
const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
|
36
|
+
|
37
|
+
const fetchData = useCallback(
|
38
|
+
(isBackground: boolean) => {
|
39
|
+
if (!isBackground) {
|
40
|
+
dispatch(setDataWasNotLoaded());
|
41
|
+
}
|
42
|
+
|
43
|
+
const path = currentSchemaPath || tenant;
|
44
|
+
|
45
|
+
dispatch(setCurrentDescribePath(path));
|
46
|
+
dispatch(getDescribe({path}));
|
47
|
+
},
|
48
|
+
[currentSchemaPath, tenant, dispatch],
|
49
|
+
);
|
50
|
+
|
51
|
+
useAutofetcher(fetchData, [fetchData], autorefresh);
|
52
|
+
|
53
|
+
if (loading && !wasLoaded) {
|
54
|
+
return (
|
55
|
+
<div className={b('loader-container')}>
|
56
|
+
<Loader size="m" />
|
57
|
+
</div>
|
58
|
+
);
|
59
|
+
}
|
60
|
+
|
61
|
+
if (error) {
|
62
|
+
return <div className={b('message-container', 'error')}>{prepareQueryError(error)}</div>;
|
63
|
+
}
|
64
|
+
|
65
|
+
if (!loading && !currentDescribe) {
|
66
|
+
return <div className={b('message-container')}>Empty</div>;
|
67
|
+
}
|
68
|
+
|
69
|
+
return (
|
70
|
+
<div className={b()}>
|
71
|
+
<div className={b('result')}>
|
72
|
+
<JSONTree
|
73
|
+
data={currentDescribe}
|
74
|
+
className={b('tree')}
|
75
|
+
onClick={({path}: {path: string}) => {
|
76
|
+
const newValue = !(expandMap.get(path) || false);
|
77
|
+
expandMap.set(path, newValue);
|
78
|
+
}}
|
79
|
+
searchOptions={{
|
80
|
+
debounceTime: 300,
|
81
|
+
}}
|
82
|
+
isExpanded={(keypath: string) => {
|
83
|
+
return expandMap.get(keypath) || false;
|
84
|
+
}}
|
85
|
+
/>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
);
|
89
|
+
};
|
90
|
+
|
91
|
+
export default Describe;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import React, {useEffect, useRef, useState} from 'react';
|
1
|
+
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import MonacoEditor from 'react-monaco-editor';
|
4
4
|
import {Loader, RadioButton} from '@gravity-ui/uikit';
|
@@ -25,20 +25,6 @@ import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
|
25
25
|
|
26
26
|
const b = cn('kv-query-explain');
|
27
27
|
|
28
|
-
const DARK_COLORS = {
|
29
|
-
success: 'rgba(59,201,53,0.75)',
|
30
|
-
error: '#bf3230',
|
31
|
-
warning: '#cc6810',
|
32
|
-
mute: 'rgba(255,255,255,0.15)',
|
33
|
-
stroke: 'rgba(255,255,255,0.17)',
|
34
|
-
fill: '#313037',
|
35
|
-
nodeFill: '#3b3a41',
|
36
|
-
nodeShadow: 'rgba(0,0,0,0.2)',
|
37
|
-
titleColor: 'rgba(255,255,255,0.7)',
|
38
|
-
textColor: 'rgba(255,255,255,0.55)',
|
39
|
-
buttonBorderColor: 'rgba(255,255,255,0.07)',
|
40
|
-
};
|
41
|
-
|
42
28
|
const EDITOR_OPTIONS = {
|
43
29
|
automaticLayout: true,
|
44
30
|
selectOnLineNumbers: true,
|
@@ -64,9 +50,15 @@ const explainOptions = [
|
|
64
50
|
function GraphRoot(props) {
|
65
51
|
const paranoid = useRef();
|
66
52
|
|
67
|
-
const
|
68
|
-
|
53
|
+
const {data, opts, shapes, version, theme} = props;
|
54
|
+
|
55
|
+
const [componentTheme, updateComponentTheme] = useState(theme);
|
69
56
|
|
57
|
+
useEffect(() => {
|
58
|
+
updateComponentTheme(theme);
|
59
|
+
}, [theme]);
|
60
|
+
|
61
|
+
const render = useCallback(() => {
|
70
62
|
if (version === explainVersions.v2) {
|
71
63
|
paranoid.current = getTopology('graphRoot', data, opts, shapes);
|
72
64
|
paranoid.current.render();
|
@@ -74,7 +66,7 @@ function GraphRoot(props) {
|
|
74
66
|
paranoid.current = getCompactTopology('graphRoot', data, opts);
|
75
67
|
paranoid.current.renderCompactTopology();
|
76
68
|
}
|
77
|
-
}
|
69
|
+
}, [data, opts, shapes, version]);
|
78
70
|
|
79
71
|
useEffect(() => {
|
80
72
|
render();
|
@@ -94,7 +86,7 @@ function GraphRoot(props) {
|
|
94
86
|
graphRoot.innerHTML = '';
|
95
87
|
|
96
88
|
render();
|
97
|
-
}, [
|
89
|
+
}, [componentTheme, render]);
|
98
90
|
|
99
91
|
useEffect(() => {
|
100
92
|
paranoid.current?.updateData?.(props.data);
|
@@ -134,11 +126,7 @@ function QueryExplain(props) {
|
|
134
126
|
};
|
135
127
|
|
136
128
|
const renderStub = () => {
|
137
|
-
return (
|
138
|
-
<div className={b('text-message')}>
|
139
|
-
There is no explanation for the request
|
140
|
-
</div>
|
141
|
-
);
|
129
|
+
return <div className={b('text-message')}>There is no explanation for the request</div>;
|
142
130
|
};
|
143
131
|
|
144
132
|
const hasContent = () => {
|
@@ -204,12 +192,12 @@ function QueryExplain(props) {
|
|
204
192
|
})}
|
205
193
|
>
|
206
194
|
<GraphRoot
|
195
|
+
theme={theme}
|
207
196
|
version={version}
|
208
197
|
data={{links, nodes}}
|
209
198
|
opts={{
|
210
199
|
renderNodeTitle: renderExplainNode,
|
211
200
|
textOverflow: TextOverflow.Normal,
|
212
|
-
colors: theme === 'dark' ? DARK_COLORS : {},
|
213
201
|
initialZoomFitsCanvas: true,
|
214
202
|
}}
|
215
203
|
shapes={{
|
@@ -237,11 +225,7 @@ function QueryExplain(props) {
|
|
237
225
|
message = error;
|
238
226
|
}
|
239
227
|
|
240
|
-
return (
|
241
|
-
<div className={b('text-message')}>
|
242
|
-
{message}
|
243
|
-
</div>
|
244
|
-
);
|
228
|
+
return <div className={b('text-message')}>{message}</div>;
|
245
229
|
};
|
246
230
|
|
247
231
|
const renderContent = () => {
|
@@ -292,7 +276,9 @@ function QueryExplain(props) {
|
|
292
276
|
)}
|
293
277
|
</div>
|
294
278
|
<div className={b('controls-left')}>
|
295
|
-
<EnableFullscreenButton
|
279
|
+
<EnableFullscreenButton
|
280
|
+
disabled={Boolean(props.error) || !hasContent()}
|
281
|
+
/>
|
296
282
|
<PaneVisibilityToggleButtons
|
297
283
|
onCollapse={props.onCollapseResults}
|
298
284
|
onExpand={props.onExpandResults}
|
@@ -3,9 +3,7 @@ import {useDispatch} from 'react-redux';
|
|
3
3
|
|
4
4
|
import {NavigationTree} from 'ydb-ui-components';
|
5
5
|
|
6
|
-
import {setCurrentSchemaPath,
|
7
|
-
import {getDescribe} from '../../../../store/reducers/describe';
|
8
|
-
import {getSchemaAcl} from '../../../../store/reducers/schemaAcl';
|
6
|
+
import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema';
|
9
7
|
import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
|
10
8
|
|
11
9
|
import {mapPathTypeToNavigationTreeType} from '../../utils/schema';
|
@@ -30,7 +28,7 @@ export function SchemaTree(props: SchemaTreeProps) {
|
|
30
28
|
const {PathDescription: {Children = []} = {}} = data;
|
31
29
|
|
32
30
|
const preloadedData: Record<string, TEvDescribeSchemeResult> = {
|
33
|
-
[path]: data
|
31
|
+
[path]: data,
|
34
32
|
};
|
35
33
|
|
36
34
|
const childItems = Children.map((childData) => {
|
@@ -55,9 +53,6 @@ export function SchemaTree(props: SchemaTreeProps) {
|
|
55
53
|
|
56
54
|
const handleActivePathUpdate = (activePath: string) => {
|
57
55
|
dispatch(setCurrentSchemaPath(activePath));
|
58
|
-
dispatch(getSchema({path: activePath}));
|
59
|
-
dispatch(getDescribe({path: activePath}));
|
60
|
-
dispatch(getSchemaAcl({path: activePath}));
|
61
56
|
};
|
62
57
|
|
63
58
|
useEffect(() => {
|
@@ -78,12 +78,15 @@ function Tenant(props: TenantProps) {
|
|
78
78
|
const tenantName = name as string;
|
79
79
|
|
80
80
|
useEffect(() => {
|
81
|
-
const schemaPath = currentSchemaPath || tenantName;
|
82
|
-
dispatch(resetLoadingState());
|
83
81
|
dispatch(getSchema({path: tenantName}));
|
84
|
-
dispatch(
|
85
|
-
|
86
|
-
|
82
|
+
dispatch(getSchemaAcl({path: tenantName}));
|
83
|
+
}, [tenantName, dispatch]);
|
84
|
+
|
85
|
+
useEffect(() => {
|
86
|
+
dispatch(resetLoadingState());
|
87
|
+
dispatch(getSchema({path: currentSchemaPath}));
|
88
|
+
dispatch(getSchemaAcl({path: currentSchemaPath}));
|
89
|
+
}, [currentSchemaPath, dispatch]);
|
87
90
|
|
88
91
|
useEffect(() => {
|
89
92
|
dispatch(disableAutorefresh());
|
package/dist/services/api.js
CHANGED
@@ -83,17 +83,26 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
83
83
|
path,
|
84
84
|
enums: true,
|
85
85
|
backup: false,
|
86
|
+
private: true,
|
87
|
+
partition_config: false,
|
86
88
|
partition_stats: false,
|
87
89
|
partitioning_info: false,
|
90
|
+
subs: 1,
|
88
91
|
},
|
89
92
|
{concurrentId: concurrentId || `getSchema|${path}`},
|
90
93
|
);
|
91
94
|
}
|
92
|
-
getDescribe({path}) {
|
93
|
-
return this.get(
|
94
|
-
|
95
|
-
|
96
|
-
|
95
|
+
getDescribe({path}, {concurrentId} = {}) {
|
96
|
+
return this.get(
|
97
|
+
this.getPath('/viewer/json/describe'),
|
98
|
+
{
|
99
|
+
path,
|
100
|
+
enums: true,
|
101
|
+
partition_stats: true,
|
102
|
+
subs: 0,
|
103
|
+
},
|
104
|
+
{concurrentId: concurrentId || `getDescribe|${path}`},
|
105
|
+
);
|
97
106
|
}
|
98
107
|
getSchemaAcl({path}) {
|
99
108
|
return this.get(
|
@@ -2,24 +2,23 @@ import {createSelector, Selector} from 'reselect';
|
|
2
2
|
import {Reducer} from 'redux';
|
3
3
|
|
4
4
|
import '../../services/api';
|
5
|
-
import {TEvDescribeSchemeResult} from '../../types/api/schema';
|
6
5
|
import {IConsumer} from '../../types/api/consumers';
|
7
|
-
import {IDescribeRootStateSlice, IDescribeState} from '../../types/store/describe';
|
8
|
-
import {
|
9
|
-
import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
|
6
|
+
import {IDescribeRootStateSlice, IDescribeState, IDescribeAction} from '../../types/store/describe';
|
7
|
+
import {createRequestActionTypes, createApiRequest} from '../utils';
|
10
8
|
|
11
|
-
const FETCH_DESCRIBE = createRequestActionTypes('describe', 'FETCH_DESCRIBE');
|
9
|
+
export const FETCH_DESCRIBE = createRequestActionTypes('describe', 'FETCH_DESCRIBE');
|
10
|
+
const SET_CURRENT_DESCRIBE_PATH = 'describe/SET_CURRENT_DESCRIBE_PATH';
|
11
|
+
const SET_DATA_WAS_NOT_LOADED = 'describe/SET_DATA_WAS_NOT_LOADED';
|
12
12
|
|
13
13
|
const initialState = {
|
14
14
|
loading: false,
|
15
15
|
wasLoaded: false,
|
16
16
|
data: {},
|
17
|
+
currentDescribe: undefined,
|
18
|
+
currentDescribePath: undefined,
|
17
19
|
};
|
18
20
|
|
19
|
-
const describe: Reducer<
|
20
|
-
IDescribeState,
|
21
|
-
ApiRequestAction<typeof FETCH_DESCRIBE, TEvDescribeSchemeResult, IResponseError>
|
22
|
-
> = (state = initialState, action) => {
|
21
|
+
const describe: Reducer<IDescribeState, IDescribeAction> = (state = initialState, action) => {
|
23
22
|
switch (action.type) {
|
24
23
|
case FETCH_DESCRIBE.REQUEST: {
|
25
24
|
return {
|
@@ -28,52 +27,83 @@ const describe: Reducer<
|
|
28
27
|
};
|
29
28
|
}
|
30
29
|
case FETCH_DESCRIBE.SUCCESS: {
|
31
|
-
|
30
|
+
const data = action.data;
|
32
31
|
|
33
|
-
|
32
|
+
const isCurrentDescribePath = data.Path === state.currentDescribePath;
|
33
|
+
|
34
|
+
let newData = state.data;
|
35
|
+
|
36
|
+
if (data.Path) {
|
34
37
|
newData = JSON.parse(JSON.stringify(state.data));
|
35
|
-
newData[
|
36
|
-
}
|
37
|
-
|
38
|
+
newData[data.Path] = data;
|
39
|
+
}
|
40
|
+
|
41
|
+
if (!isCurrentDescribePath) {
|
42
|
+
return {
|
43
|
+
...state,
|
44
|
+
data: newData,
|
45
|
+
};
|
38
46
|
}
|
39
47
|
|
40
48
|
return {
|
41
49
|
...state,
|
42
50
|
data: newData,
|
43
|
-
currentDescribe:
|
51
|
+
currentDescribe: data,
|
44
52
|
loading: false,
|
45
53
|
wasLoaded: true,
|
46
54
|
error: undefined,
|
47
55
|
};
|
48
56
|
}
|
57
|
+
|
49
58
|
case FETCH_DESCRIBE.FAILURE: {
|
59
|
+
if (action.error?.isCancelled) {
|
60
|
+
return state;
|
61
|
+
}
|
62
|
+
|
50
63
|
return {
|
51
64
|
...state,
|
52
65
|
error: action.error,
|
53
66
|
loading: false,
|
54
67
|
};
|
55
68
|
}
|
69
|
+
case SET_CURRENT_DESCRIBE_PATH: {
|
70
|
+
return {
|
71
|
+
...state,
|
72
|
+
currentDescribePath: action.data,
|
73
|
+
};
|
74
|
+
}
|
75
|
+
case SET_DATA_WAS_NOT_LOADED: {
|
76
|
+
return {
|
77
|
+
...state,
|
78
|
+
wasLoaded: false,
|
79
|
+
};
|
80
|
+
}
|
56
81
|
default:
|
57
82
|
return state;
|
58
83
|
}
|
59
84
|
};
|
60
85
|
|
86
|
+
export const setCurrentDescribePath = (path: string) => {
|
87
|
+
return {
|
88
|
+
type: SET_CURRENT_DESCRIBE_PATH,
|
89
|
+
data: path,
|
90
|
+
} as const;
|
91
|
+
};
|
92
|
+
|
93
|
+
export const setDataWasNotLoaded = () => {
|
94
|
+
return {
|
95
|
+
type: SET_DATA_WAS_NOT_LOADED,
|
96
|
+
} as const;
|
97
|
+
};
|
98
|
+
|
61
99
|
// Consumers selectors
|
62
|
-
const selectConsumersNames:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[]> =
|
68
|
-
selectConsumersNames,
|
69
|
-
(names = []) => {
|
70
|
-
const consumers = names.map((name) => {
|
71
|
-
return {name};
|
72
|
-
});
|
73
|
-
|
74
|
-
return consumers;
|
75
|
-
},
|
76
|
-
);
|
100
|
+
const selectConsumersNames = (state: IDescribeRootStateSlice, path: string | undefined) =>
|
101
|
+
path
|
102
|
+
? state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules
|
103
|
+
: undefined;
|
104
|
+
|
105
|
+
export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[], [string | undefined]> =
|
106
|
+
createSelector(selectConsumersNames, (names = []) => names.map((name) => ({name})));
|
77
107
|
|
78
108
|
export function getDescribe({path}: {path: string}) {
|
79
109
|
return createApiRequest({
|
package/dist/types/api/error.ts
CHANGED
@@ -1,14 +1,34 @@
|
|
1
|
+
import {
|
2
|
+
FETCH_DESCRIBE,
|
3
|
+
setCurrentDescribePath,
|
4
|
+
setDataWasNotLoaded,
|
5
|
+
} from '../../store/reducers/describe';
|
6
|
+
import {ApiRequestAction} from '../../store/utils';
|
1
7
|
import {IResponseError} from '../api/error';
|
2
8
|
import {TEvDescribeSchemeResult} from '../api/schema';
|
3
9
|
|
10
|
+
export type IDescribeData = Record<string, TEvDescribeSchemeResult>;
|
11
|
+
|
4
12
|
export interface IDescribeState {
|
5
13
|
loading: boolean;
|
6
14
|
wasLoaded: boolean;
|
7
|
-
data:
|
15
|
+
data: IDescribeData;
|
8
16
|
currentDescribe?: TEvDescribeSchemeResult;
|
17
|
+
currentDescribePath?: string;
|
9
18
|
error?: IResponseError;
|
10
19
|
}
|
11
20
|
|
21
|
+
type IDescribeApiRequestAction = ApiRequestAction<
|
22
|
+
typeof FETCH_DESCRIBE,
|
23
|
+
TEvDescribeSchemeResult,
|
24
|
+
IResponseError
|
25
|
+
>;
|
26
|
+
|
27
|
+
export type IDescribeAction =
|
28
|
+
| IDescribeApiRequestAction
|
29
|
+
| ReturnType<typeof setCurrentDescribePath>
|
30
|
+
| ReturnType<typeof setDataWasNotLoaded>;
|
31
|
+
|
12
32
|
export interface IDescribeRootStateSlice {
|
13
33
|
describe: IDescribeState;
|
14
34
|
}
|
package/package.json
CHANGED
@@ -1,130 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
|
-
import cn from 'bem-cn-lite';
|
4
|
-
import {connect} from 'react-redux';
|
5
|
-
import {Loader} from '@gravity-ui/uikit';
|
6
|
-
import JSONTree from 'react-json-inspector';
|
7
|
-
|
8
|
-
import {getDescribe} from '../../../../store/reducers/describe';
|
9
|
-
|
10
|
-
import './Describe.scss';
|
11
|
-
import 'react-json-inspector/json-inspector.css';
|
12
|
-
import {AutoFetcher} from '../../../../utils/autofetcher';
|
13
|
-
|
14
|
-
const b = cn('kv-describe');
|
15
|
-
|
16
|
-
const expandMap = new Map();
|
17
|
-
|
18
|
-
class Describe extends React.Component {
|
19
|
-
static propTypes = {
|
20
|
-
error: PropTypes.string,
|
21
|
-
data: PropTypes.array,
|
22
|
-
loading: PropTypes.bool,
|
23
|
-
wasLoaded: PropTypes.bool,
|
24
|
-
autorefresh: PropTypes.bool,
|
25
|
-
tenant: PropTypes.string,
|
26
|
-
};
|
27
|
-
|
28
|
-
autofetcher;
|
29
|
-
|
30
|
-
componentDidMount() {
|
31
|
-
const {autorefresh} = this.props;
|
32
|
-
this.autofetcher = new AutoFetcher();
|
33
|
-
this.fetchData();
|
34
|
-
if (autorefresh) {
|
35
|
-
this.autofetcher.start();
|
36
|
-
this.autofetcher.fetch(() => this.fetchData);
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
componentDidUpdate(prevProps) {
|
41
|
-
const {autorefresh} = this.props;
|
42
|
-
|
43
|
-
if (autorefresh && !prevProps.autorefresh) {
|
44
|
-
this.fetchData();
|
45
|
-
this.autofetcher.stop();
|
46
|
-
this.autofetcher.start();
|
47
|
-
this.autofetcher.fetch(() => this.fetchData());
|
48
|
-
}
|
49
|
-
if (!autorefresh && prevProps.autorefresh) {
|
50
|
-
this.autofetcher.stop();
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
componentWillUnmount() {
|
55
|
-
this.autofetcher.stop();
|
56
|
-
}
|
57
|
-
|
58
|
-
fetchData() {
|
59
|
-
const {getDescribe, tenant, currentSchemaPath} = this.props;
|
60
|
-
const path = currentSchemaPath || tenant;
|
61
|
-
getDescribe({path});
|
62
|
-
}
|
63
|
-
|
64
|
-
renderDescribeJson = () => {
|
65
|
-
const {data} = this.props;
|
66
|
-
|
67
|
-
return (
|
68
|
-
<JSONTree
|
69
|
-
data={data}
|
70
|
-
className={b('tree')}
|
71
|
-
onClick={({path}) => {
|
72
|
-
const newValue = !(expandMap.get(path) || false);
|
73
|
-
expandMap.set(path, newValue);
|
74
|
-
}}
|
75
|
-
searchOptions={{
|
76
|
-
debounceTime: 300,
|
77
|
-
}}
|
78
|
-
isExpanded={(keypath) => {
|
79
|
-
return expandMap.get(keypath) || false;
|
80
|
-
}}
|
81
|
-
/>
|
82
|
-
);
|
83
|
-
};
|
84
|
-
|
85
|
-
render() {
|
86
|
-
const {error, loading, data, wasLoaded} = this.props;
|
87
|
-
|
88
|
-
if (loading && !wasLoaded) {
|
89
|
-
return (
|
90
|
-
<div className={b('loader-container')}>
|
91
|
-
<Loader size="m" />
|
92
|
-
</div>
|
93
|
-
);
|
94
|
-
}
|
95
|
-
|
96
|
-
if (error) {
|
97
|
-
return <div className={b('message-container')}>{error.data || error}</div>;
|
98
|
-
}
|
99
|
-
|
100
|
-
if (!loading && !data) {
|
101
|
-
return <div className={b('message-container')}>Empty</div>;
|
102
|
-
}
|
103
|
-
|
104
|
-
return (
|
105
|
-
<div className={b()}>
|
106
|
-
<div className={b('result')}>{this.renderDescribeJson()}</div>
|
107
|
-
</div>
|
108
|
-
);
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
|
-
const mapStateToProps = (state) => {
|
113
|
-
const {loading, error, currentDescribe, wasLoaded} = state.describe;
|
114
|
-
const {autorefresh, currentSchemaPath} = state.schema;
|
115
|
-
|
116
|
-
return {
|
117
|
-
data: currentDescribe,
|
118
|
-
loading,
|
119
|
-
error,
|
120
|
-
wasLoaded,
|
121
|
-
autorefresh,
|
122
|
-
currentSchemaPath,
|
123
|
-
};
|
124
|
-
};
|
125
|
-
|
126
|
-
const mapDispatchToProps = {
|
127
|
-
getDescribe,
|
128
|
-
};
|
129
|
-
|
130
|
-
export default connect(mapStateToProps, mapDispatchToProps)(Describe);
|