ydb-embedded-ui 2.4.2 → 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 +21 -0
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +91 -0
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +4 -3
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +45 -24
- 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/containers/Tenant/utils/schema.ts +2 -3
- 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/api/schema.ts +163 -9
- package/dist/types/store/describe.ts +21 -1
- package/package.json +2 -2
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.js +0 -130
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
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
|
+
|
16
|
+
## [2.4.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.2...v2.4.3) (2022-11-14)
|
17
|
+
|
18
|
+
|
19
|
+
### Bug Fixes
|
20
|
+
|
21
|
+
* fix app crash on ColumnTable path type ([a1aefa8](https://github.com/ydb-platform/ydb-embedded-ui/commit/a1aefa876600b1b459bf3024f0704883431df5a2))
|
22
|
+
* **schema:** add types for ColumnTable and ColumnStore ([dc13307](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc13307dcea801c05863b7dd5ee19f01aa074c85))
|
23
|
+
|
3
24
|
## [2.4.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.1...v2.4.2) (2022-11-09)
|
4
25
|
|
5
26
|
|
@@ -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;
|
@@ -12,7 +12,7 @@ import {
|
|
12
12
|
PersQueueGroupInfo,
|
13
13
|
} from '../../../../components/InfoViewer/schemaInfo';
|
14
14
|
|
15
|
-
import {EPathType} from '../../../../types/api/schema';
|
15
|
+
import {EPathType, TColumnTableDescription} from '../../../../types/api/schema';
|
16
16
|
import {isColumnEntityType, isTableType} from '../../utils/schema';
|
17
17
|
//@ts-ignore
|
18
18
|
import {getSchema, resetLoadingState} from '../../../../store/reducers/schema';
|
@@ -25,7 +25,7 @@ import {useAutofetcher} from '../../../../utils/hooks';
|
|
25
25
|
|
26
26
|
import './Overview.scss';
|
27
27
|
|
28
|
-
function prepareOlapTableGeneral(tableData:
|
28
|
+
function prepareOlapTableGeneral(tableData: TColumnTableDescription = {}, olapStats?: any[]) {
|
29
29
|
const {ColumnShardCount} = tableData;
|
30
30
|
const Bytes = olapStats?.reduce((acc, el) => {
|
31
31
|
acc += parseInt(el.Bytes) || 0;
|
@@ -103,7 +103,8 @@ function Overview(props: OverviewProps) {
|
|
103
103
|
|
104
104
|
const schemaData = useMemo(() => {
|
105
105
|
return isTableType(type) && isColumnEntityType(type)
|
106
|
-
?
|
106
|
+
? // process data for ColumnTable
|
107
|
+
prepareOlapTableGeneral(tableSchema, olapStats)
|
107
108
|
: currentItem;
|
108
109
|
}, [type, tableSchema, olapStats, currentItem]);
|
109
110
|
|
@@ -20,7 +20,12 @@ import {
|
|
20
20
|
} from '../../../components/InfoViewer/schemaOverview';
|
21
21
|
import Icon from '../../../components/Icon/Icon';
|
22
22
|
|
23
|
-
import {
|
23
|
+
import {
|
24
|
+
EPathSubType,
|
25
|
+
EPathType,
|
26
|
+
TColumnTableDescription,
|
27
|
+
TDirEntry,
|
28
|
+
} from '../../../types/api/schema';
|
24
29
|
import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
|
25
30
|
|
26
31
|
import {
|
@@ -57,19 +62,26 @@ const initialTenantCommonInfoState = {
|
|
57
62
|
collapsed: getInitialIsSummaryCollapsed(),
|
58
63
|
};
|
59
64
|
|
60
|
-
function prepareOlapTableSchema(tableSchema:
|
61
|
-
const {Name, Schema
|
62
|
-
|
63
|
-
|
64
|
-
const
|
65
|
-
|
66
|
-
|
65
|
+
function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) {
|
66
|
+
const {Name, Schema} = tableSchema;
|
67
|
+
|
68
|
+
if (Schema) {
|
69
|
+
const {Columns, KeyColumnNames} = Schema;
|
70
|
+
const KeyColumnIds = KeyColumnNames?.map((name: string) => {
|
71
|
+
const column = Columns?.find((el) => el.Name === name);
|
72
|
+
return column?.Id;
|
73
|
+
});
|
74
|
+
|
75
|
+
return {
|
76
|
+
Columns,
|
77
|
+
KeyColumnNames,
|
78
|
+
Name,
|
79
|
+
KeyColumnIds,
|
80
|
+
};
|
81
|
+
}
|
67
82
|
|
68
83
|
return {
|
69
|
-
Columns,
|
70
|
-
KeyColumnNames,
|
71
84
|
Name,
|
72
|
-
KeyColumnIds,
|
73
85
|
};
|
74
86
|
}
|
75
87
|
|
@@ -104,15 +116,23 @@ function ObjectSummary(props: ObjectSummaryProps) {
|
|
104
116
|
});
|
105
117
|
|
106
118
|
const {name: tenantName, info: infoTab} = queryParams;
|
107
|
-
const pathData: TDirEntry | undefined = _.get(
|
108
|
-
|
119
|
+
const pathData: TDirEntry | undefined = _.get(
|
120
|
+
data[tenantName as string],
|
121
|
+
'PathDescription.Self',
|
122
|
+
);
|
123
|
+
const currentSchemaData: TDirEntry | undefined = _.get(
|
124
|
+
data[currentSchemaPath],
|
125
|
+
'PathDescription.Self',
|
126
|
+
);
|
109
127
|
|
110
128
|
const tableSchema =
|
111
129
|
currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
|
112
130
|
|
113
|
-
const schema =
|
114
|
-
|
115
|
-
|
131
|
+
const schema =
|
132
|
+
isTableType(props.type) && isColumnEntityType(props.type)
|
133
|
+
? // process data for ColumnTable
|
134
|
+
prepareOlapTableSchema(tableSchema)
|
135
|
+
: tableSchema;
|
116
136
|
|
117
137
|
useEffect(() => {
|
118
138
|
const {type} = props;
|
@@ -166,11 +186,16 @@ function ObjectSummary(props: ObjectSummaryProps) {
|
|
166
186
|
[EPathType.EPathTypeExtSubDomain]: undefined,
|
167
187
|
[EPathType.EPathTypeColumnStore]: undefined,
|
168
188
|
[EPathType.EPathTypeColumnTable]: undefined,
|
169
|
-
[EPathType.EPathTypeCdcStream]: () =>
|
170
|
-
|
189
|
+
[EPathType.EPathTypeCdcStream]: () => (
|
190
|
+
<CDCStreamOverview data={data[currentSchemaPath]} />
|
191
|
+
),
|
192
|
+
[EPathType.EPathTypePersQueueGroup]: () => (
|
193
|
+
<PersQueueGroupOverview data={data[currentSchemaPath]} />
|
194
|
+
),
|
171
195
|
};
|
172
196
|
|
173
|
-
let component =
|
197
|
+
let component =
|
198
|
+
currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.();
|
174
199
|
|
175
200
|
if (!component) {
|
176
201
|
const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
|
@@ -182,11 +207,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
|
|
182
207
|
component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
|
183
208
|
}
|
184
209
|
|
185
|
-
return (
|
186
|
-
<div className={b('overview-wrapper')}>
|
187
|
-
{component}
|
188
|
-
</div>
|
189
|
-
);
|
210
|
+
return <div className={b('overview-wrapper')}>{component}</div>;
|
190
211
|
};
|
191
212
|
|
192
213
|
const renderTabContent = () => {
|
@@ -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());
|
@@ -35,7 +35,7 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
|
|
35
35
|
export const mapPathTypeToNavigationTreeType = (
|
36
36
|
type: EPathType = EPathType.EPathTypeDir,
|
37
37
|
subType?: EPathSubType,
|
38
|
-
defaultType: NavigationTreeNodeType = 'directory'
|
38
|
+
defaultType: NavigationTreeNodeType = 'directory',
|
39
39
|
): NavigationTreeNodeType =>
|
40
40
|
(subType && pathSubTypeToNodeType[subType]) || pathTypeToNodeType[type] || defaultType;
|
41
41
|
|
@@ -87,5 +87,4 @@ const pathTypeToIsColumn: Record<EPathType, boolean> = {
|
|
87
87
|
[EPathType.EPathTypePersQueueGroup]: false,
|
88
88
|
};
|
89
89
|
|
90
|
-
export const isColumnEntityType = (type?: EPathType) =>
|
91
|
-
(type && pathTypeToIsColumn[type]) ?? false;
|
90
|
+
export const isColumnEntityType = (type?: EPathType) => (type && pathTypeToIsColumn[type]) ?? false;
|
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
package/dist/types/api/schema.ts
CHANGED
@@ -52,8 +52,8 @@ interface TPathDescription {
|
|
52
52
|
TabletMetrics?: unknown;
|
53
53
|
TablePartitions?: unknown[];
|
54
54
|
|
55
|
-
ColumnStoreDescription?:
|
56
|
-
ColumnTableDescription?:
|
55
|
+
ColumnStoreDescription?: TColumnStoreDescription;
|
56
|
+
ColumnTableDescription?: TColumnTableDescription;
|
57
57
|
|
58
58
|
TableIndex?: TIndexDescription;
|
59
59
|
|
@@ -85,12 +85,12 @@ export interface TDirEntry {
|
|
85
85
|
Version?: TPathVersion;
|
86
86
|
}
|
87
87
|
|
88
|
-
// incomplete
|
88
|
+
// FIXME: incomplete
|
89
89
|
export interface TTableDescription {
|
90
90
|
PartitionConfig?: TPartitionConfig;
|
91
91
|
}
|
92
92
|
|
93
|
-
// incomplete
|
93
|
+
// FIXME: incomplete
|
94
94
|
export interface TPartitionConfig {
|
95
95
|
/** uint64 */
|
96
96
|
FollowerCount?: string;
|
@@ -263,7 +263,6 @@ export enum EPathType {
|
|
263
263
|
EPathTypeColumnStore = 'EPathTypeColumnStore',
|
264
264
|
EPathTypeColumnTable = 'EPathTypeColumnTable',
|
265
265
|
EPathTypeCdcStream = 'EPathTypeCdcStream',
|
266
|
-
|
267
266
|
}
|
268
267
|
|
269
268
|
export enum EPathSubType {
|
@@ -414,7 +413,7 @@ interface TPQPartitionConfig {
|
|
414
413
|
ExplicitChannelProfiles?: TChannelProfile[];
|
415
414
|
|
416
415
|
MirrorFrom?: TMirrorPartitionConfig;
|
417
|
-
}
|
416
|
+
}
|
418
417
|
|
419
418
|
interface TPQTabletConfig {
|
420
419
|
/** uint64 */
|
@@ -437,7 +436,7 @@ interface TPQTabletConfig {
|
|
437
436
|
ReadFromTimestampsMs?: number[];
|
438
437
|
/** uint64[] */
|
439
438
|
ConsumerFormatVersions?: number[];
|
440
|
-
|
439
|
+
|
441
440
|
ConsumerCodecs?: TCodecs[];
|
442
441
|
ReadRuleServiceTypes?: string;
|
443
442
|
|
@@ -461,7 +460,7 @@ interface TPQTabletConfig {
|
|
461
460
|
PartitionKeySchema?: TKeyComponentSchema[];
|
462
461
|
|
463
462
|
Partitions?: TPartition[];
|
464
|
-
|
463
|
+
|
465
464
|
MeteringMode?: EMeteringMode;
|
466
465
|
}
|
467
466
|
|
@@ -481,7 +480,7 @@ export interface TPersQueueGroupDescription {
|
|
481
480
|
/** uint64 */
|
482
481
|
PathId?: string;
|
483
482
|
TotalGroupCount: number;
|
484
|
-
|
483
|
+
|
485
484
|
PartitionsToAdd?: TPartitionToAdd[];
|
486
485
|
PartitionsToDelete?: number[];
|
487
486
|
NextPartitionId?: number;
|
@@ -497,3 +496,158 @@ export interface TPersQueueGroupDescription {
|
|
497
496
|
|
498
497
|
BootstrapConfig?: TBootstrapConfig;
|
499
498
|
}
|
499
|
+
|
500
|
+
export interface TColumnTableDescription {
|
501
|
+
Name?: string;
|
502
|
+
|
503
|
+
Schema?: TColumnTableSchema;
|
504
|
+
TtlSettings?: TColumnDataLifeCycle;
|
505
|
+
|
506
|
+
SchemaPresetId?: number;
|
507
|
+
SchemaPresetName?: string;
|
508
|
+
|
509
|
+
ColumnStorePathId?: TPathID;
|
510
|
+
|
511
|
+
ColumnShardCount?: number;
|
512
|
+
Sharding?: TColumnTableSharding;
|
513
|
+
|
514
|
+
/** uint64 */
|
515
|
+
SchemaPresetVersionAdj?: string;
|
516
|
+
/** uint64 */
|
517
|
+
TtlSettingsPresetVersionAdj?: string;
|
518
|
+
|
519
|
+
StorageConfig?: TColumnStorageConfig;
|
520
|
+
}
|
521
|
+
|
522
|
+
interface TColumnTableSchema {
|
523
|
+
Columns: TOlapColumnDescription[];
|
524
|
+
KeyColumnNames: string[];
|
525
|
+
Engine?: EColumnTableEngine;
|
526
|
+
NextColumnId?: number;
|
527
|
+
|
528
|
+
/** uint64 */
|
529
|
+
Version?: string;
|
530
|
+
|
531
|
+
DefaultCompression?: TCompressionOptions;
|
532
|
+
EnableTiering?: boolean;
|
533
|
+
}
|
534
|
+
|
535
|
+
interface TOlapColumnDescription {
|
536
|
+
Id?: number;
|
537
|
+
Name?: string;
|
538
|
+
Type?: string;
|
539
|
+
TypeId?: number;
|
540
|
+
TypeInfo?: TTypeInfo;
|
541
|
+
}
|
542
|
+
|
543
|
+
interface TTypeInfo {
|
544
|
+
PgTypeId?: number;
|
545
|
+
}
|
546
|
+
|
547
|
+
enum EColumnTableEngine {
|
548
|
+
COLUMN_ENGINE_NONE = 'COLUMN_ENGINE_NONE',
|
549
|
+
COLUMN_ENGINE_REPLACING_TIMESERIES = 'COLUMN_ENGINE_REPLACING_TIMESERIES',
|
550
|
+
}
|
551
|
+
|
552
|
+
interface TCompressionOptions {
|
553
|
+
CompressionCodec?: EColumnCodec;
|
554
|
+
CompressionLevel?: number;
|
555
|
+
}
|
556
|
+
|
557
|
+
enum EColumnCodec {
|
558
|
+
ColumnCodecPlain = 'ColumnCodecPlain',
|
559
|
+
ColumnCodecLZ4 = 'ColumnCodecLZ4',
|
560
|
+
ColumnCodecZSTD = 'ColumnCodecZSTD',
|
561
|
+
}
|
562
|
+
|
563
|
+
interface TColumnDataLifeCycle {
|
564
|
+
Enabled?: TTtl;
|
565
|
+
Disabled?: {};
|
566
|
+
Tiering?: TStorageTiering;
|
567
|
+
|
568
|
+
/** uint64 */
|
569
|
+
Version?: string;
|
570
|
+
}
|
571
|
+
|
572
|
+
interface TTtl {
|
573
|
+
ColumnName?: string;
|
574
|
+
|
575
|
+
ExpireAfterSeconds?: number;
|
576
|
+
|
577
|
+
/** uint64 */
|
578
|
+
ExpireAfterBytes?: string;
|
579
|
+
|
580
|
+
ColumnUnit?: EUnit;
|
581
|
+
}
|
582
|
+
|
583
|
+
interface TStorageTier {
|
584
|
+
Name?: string;
|
585
|
+
Eviction?: TTtl;
|
586
|
+
}
|
587
|
+
interface TStorageTiering {
|
588
|
+
Tiers: TStorageTier[];
|
589
|
+
}
|
590
|
+
|
591
|
+
enum EUnit {
|
592
|
+
UNIT_AUTO = 'UNIT_AUTO',
|
593
|
+
UNIT_SECONDS = 'UNIT_SECONDS',
|
594
|
+
UNIT_MILLISECONDS = 'UNIT_MILLISECONDS',
|
595
|
+
UNIT_MICROSECONDS = 'UNIT_MICROSECONDS',
|
596
|
+
UNIT_NANOSECONDS = 'UNIT_NANOSECONDS',
|
597
|
+
}
|
598
|
+
|
599
|
+
interface TColumnTableSharding {
|
600
|
+
/** uint64 */
|
601
|
+
Version?: string;
|
602
|
+
|
603
|
+
/** uint64 */
|
604
|
+
ColumnShards: string[];
|
605
|
+
|
606
|
+
/** uint64 */
|
607
|
+
AdditionalColumnShards: string[];
|
608
|
+
|
609
|
+
UniquePrimaryKey?: boolean;
|
610
|
+
|
611
|
+
RandomSharding?: {};
|
612
|
+
HashSharding?: THashSharding;
|
613
|
+
}
|
614
|
+
|
615
|
+
interface THashSharding {
|
616
|
+
Function?: EHashFunction;
|
617
|
+
Columns: string[];
|
618
|
+
UniqueShardKey?: boolean;
|
619
|
+
ActiveShardsCount?: number;
|
620
|
+
}
|
621
|
+
enum EHashFunction {
|
622
|
+
HASH_FUNCTION_MODULO_N = 'HASH_FUNCTION_MODULO_N',
|
623
|
+
HASH_FUNCTION_CLOUD_LOGS = 'HASH_FUNCTION_CLOUD_LOGS',
|
624
|
+
}
|
625
|
+
interface TColumnStorageConfig {
|
626
|
+
SysLog?: TStorageSettings;
|
627
|
+
Log?: TStorageSettings;
|
628
|
+
Data?: TStorageSettings;
|
629
|
+
DataChannelCount?: number;
|
630
|
+
}
|
631
|
+
interface TStorageSettings {
|
632
|
+
PreferredPoolKind?: string;
|
633
|
+
AllowOtherKinds?: boolean;
|
634
|
+
}
|
635
|
+
export interface TColumnStoreDescription {
|
636
|
+
Name?: string;
|
637
|
+
ColumnShardCount?: number;
|
638
|
+
|
639
|
+
/** uint64 */
|
640
|
+
ColumnShards: string[];
|
641
|
+
|
642
|
+
SchemaPresets: TColumnTableSchemaPreset[];
|
643
|
+
StorageConfig?: TColumnStorageConfig;
|
644
|
+
|
645
|
+
NextSchemaPresetId?: number;
|
646
|
+
NextTtlSettingsPresetId?: number;
|
647
|
+
}
|
648
|
+
|
649
|
+
interface TColumnTableSchemaPreset {
|
650
|
+
Id?: number;
|
651
|
+
Name?: string;
|
652
|
+
Schema?: TColumnTableSchema;
|
653
|
+
}
|
@@ -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,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ydb-embedded-ui",
|
3
|
-
"version": "2.4.
|
3
|
+
"version": "2.4.4",
|
4
4
|
"files": [
|
5
5
|
"dist"
|
6
6
|
],
|
@@ -10,7 +10,7 @@
|
|
10
10
|
},
|
11
11
|
"dependencies": {
|
12
12
|
"@gravity-ui/i18n": "^1.0.0",
|
13
|
-
"@yandex-cloud/paranoid": "^1.
|
13
|
+
"@yandex-cloud/paranoid": "^1.3.0",
|
14
14
|
"@yandex-cloud/react-data-table": "0.2.1",
|
15
15
|
"axios": "0.19.2",
|
16
16
|
"bem-cn-lite": "4.0.0",
|
@@ -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);
|