ydb-embedded-ui 2.4.2 → 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
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: any, olapStats?: any[]) {
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
- ? prepareOlapTableGeneral(tableSchema, olapStats)
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 {EPathSubType, EPathType, TDirEntry} from '../../../types/api/schema';
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: any) {
61
- const {Name, Schema = {}} = tableSchema;
62
- const {Columns, KeyColumnNames} = Schema;
63
- const KeyColumnIds = KeyColumnNames?.map((name: string) => {
64
- const column = Columns?.find((el: any) => el.Name === name);
65
- return column.Id;
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(data[tenantName as string], 'PathDescription.Self');
108
- const currentSchemaData: TDirEntry | undefined = _.get(data[currentSchemaPath], 'PathDescription.Self');
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 = isTableType(props.type) && isColumnEntityType(props.type)
114
- ? prepareOlapTableSchema(tableSchema)
115
- : tableSchema;
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]: () => <CDCStreamOverview data={data[currentSchemaPath]} />,
170
- [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupOverview data={data[currentSchemaPath]} />,
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 = currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.();
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 render = () => {
68
- const {data, opts, shapes, version} = props;
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
- }, [props.opts.colors]);
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 disabled={Boolean(props.error) || !hasContent()} />
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, getSchema, preloadSchemas} from '../../../../store/reducers/schema';
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(getSchema({path: schemaPath}));
85
- dispatch(getSchemaAcl({path: schemaPath}));
86
- }, [tenantName, currentSchemaPath, dispatch]);
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;
@@ -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(this.getPath('/viewer/json/describe'), {
94
- path,
95
- enums: true,
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 {IResponseError} from '../../types/api/error';
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
- let newData;
30
+ const data = action.data;
32
31
 
33
- if (action.data.Path) {
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[action.data.Path] = action.data;
36
- } else {
37
- newData = state.data;
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: action.data,
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: Selector<IDescribeRootStateSlice, string[] | undefined, [string]> = (
63
- state,
64
- path,
65
- ) => state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules;
66
-
67
- export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[]> = createSelector(
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({
@@ -1,4 +1,6 @@
1
1
  export interface IResponseError {
2
- status: number;
3
- statusText: string;
2
+ data?: unknown;
3
+ status?: number;
4
+ statusText?: string;
5
+ isCancelled?: boolean;
4
6
  }
@@ -52,8 +52,8 @@ interface TPathDescription {
52
52
  TabletMetrics?: unknown;
53
53
  TablePartitions?: unknown[];
54
54
 
55
- ColumnStoreDescription?: unknown;
56
- ColumnTableDescription?: unknown;
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: Record<string, TEvDescribeSchemeResult>;
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.2",
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.2.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);