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 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);