ydb-embedded-ui 2.4.1 → 2.4.3

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.4.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.2...v2.4.3) (2022-11-14)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * fix app crash on ColumnTable path type ([a1aefa8](https://github.com/ydb-platform/ydb-embedded-ui/commit/a1aefa876600b1b459bf3024f0704883431df5a2))
9
+ * **schema:** add types for ColumnTable and ColumnStore ([dc13307](https://github.com/ydb-platform/ydb-embedded-ui/commit/dc13307dcea801c05863b7dd5ee19f01aa074c85))
10
+
11
+ ## [2.4.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.1...v2.4.2) (2022-11-09)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **QueryExplain:** apply all node types ([06d26de](https://github.com/ydb-platform/ydb-embedded-ui/commit/06d26def15496f8e2de00d941b39bf6a68382f14))
17
+
3
18
  ## [2.4.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.0...v2.4.1) (2022-11-01)
4
19
 
5
20
 
@@ -1,11 +1,11 @@
1
1
  import {useEffect, useState} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
2
+ import {useDispatch} from 'react-redux';
3
3
  import block from 'bem-cn-lite';
4
4
 
5
5
  import DataTable, {Column} from '@yandex-cloud/react-data-table';
6
6
 
7
7
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
8
- import {useAutofetcher} from '../../../../utils/hooks';
8
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
9
9
  import {Search} from '../../../../components/Search';
10
10
  import {getDescribe, selectConsumers} from '../../../../store/reducers/describe';
11
11
 
@@ -28,7 +28,7 @@ export const Consumers = ({path}: ConsumersProps) => {
28
28
 
29
29
  useAutofetcher(fetchData, [path]);
30
30
 
31
- const consumers = useSelector((state) => selectConsumers(state, path));
31
+ const consumers = useTypedSelector((state) => selectConsumers(state, path));
32
32
 
33
33
  const [consumersToRender, setConsumersToRender] = useState(consumers);
34
34
 
@@ -1,16 +1,16 @@
1
1
  import {useCallback} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
2
+ import {useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Loader} from '@gravity-ui/uikit';
6
6
 
7
7
  import {SelfCheckResult} from '../../../../types/api/healthcheck';
8
+ import {useTypedSelector, useAutofetcher} from '../../../../utils/hooks';
8
9
  import {
9
10
  getHealthcheckInfo,
10
11
  selectIssuesTreeById,
11
12
  selectIssuesTreesRoots,
12
13
  } from '../../../../store/reducers/healthcheckInfo';
13
- import {useAutofetcher} from '../../../../utils/hooks';
14
14
 
15
15
  import {Details} from './Details';
16
16
  import {Preview} from './Preview';
@@ -33,13 +33,15 @@ export const Healthcheck = (props: HealthcheckProps) => {
33
33
 
34
34
  const dispatch = useDispatch();
35
35
 
36
- const {data, loading, wasLoaded, error} = useSelector((state: any) => state.healthcheckInfo);
36
+ const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
37
37
  const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
38
38
 
39
- const issuesTreesRoots = useSelector(selectIssuesTreesRoots);
40
- const expandedIssueTree = useSelector((state) => selectIssuesTreeById(state, expandedIssueId));
39
+ const issuesTreesRoots = useTypedSelector(selectIssuesTreesRoots);
40
+ const expandedIssueTree = useTypedSelector((state) =>
41
+ selectIssuesTreeById(state, expandedIssueId),
42
+ );
41
43
 
42
- const {autorefresh} = useSelector((state: any) => state.schema);
44
+ const {autorefresh} = useTypedSelector((state) => state.schema);
43
45
 
44
46
  const fetchHealthcheck = useCallback(() => {
45
47
  dispatch(getHealthcheckInfo(tenant));
@@ -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 = () => {
@@ -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;
@@ -0,0 +1,27 @@
1
+ import url from 'url';
2
+
3
+ export const getUrlData = (href: string, singleClusterMode: boolean) => {
4
+ if (!singleClusterMode) {
5
+ const {backend, clusterName} = url.parse(href, true).query;
6
+ return {
7
+ basename: '/',
8
+ backend,
9
+ clusterName,
10
+ };
11
+ } else if (window.custom_backend) {
12
+ const {backend} = url.parse(href, true).query;
13
+ return {
14
+ basename: '/',
15
+ backend: backend || window.custom_backend,
16
+ };
17
+ } else {
18
+ const parsedPrefix = window.location.pathname.match(/.*(?=\/monitoring)/) || [];
19
+ const basenamePrefix = Boolean(parsedPrefix.length) && parsedPrefix[0];
20
+ const basename = [basenamePrefix, 'monitoring'].filter(Boolean).join('/');
21
+
22
+ return {
23
+ basename,
24
+ backend: basenamePrefix || '',
25
+ };
26
+ }
27
+ };
@@ -1,47 +1,16 @@
1
1
  import {createStore, applyMiddleware, compose} from 'redux';
2
2
  import thunkMiddleware from 'redux-thunk';
3
- import getLocationMiddleware from './state-url-mapping';
4
3
  import {createBrowserHistory} from 'history';
5
4
  import {listenForHistoryChange} from 'redux-location-state';
6
5
 
6
+ import {getUrlData} from './getUrlData';
7
+ import getLocationMiddleware from './state-url-mapping';
7
8
  import rootReducer from './reducers';
8
9
 
9
- import url from 'url';
10
-
11
- export const webVersion = window.web_version;
12
-
13
- export const customBackend = window.custom_backend;
14
-
15
- export const getUrlData = (href, singleClusterMode) => {
16
- if (!singleClusterMode) {
17
- const {backend, clusterName} = url.parse(href, true).query;
18
- return {
19
- basename: '/',
20
- backend,
21
- clusterName,
22
- };
23
- } else if (customBackend) {
24
- const {backend} = url.parse(href, true).query;
25
- return {
26
- basename: '/',
27
- backend: backend || window.custom_backend,
28
- };
29
- } else {
30
- const parsedPrefix = window.location.pathname.match(/.*(?=\/monitoring)/) || [];
31
- const basenamePrefix = Boolean(parsedPrefix.length) && parsedPrefix[0];
32
- const basename = [basenamePrefix, 'monitoring'].filter(Boolean).join('/');
33
-
34
- return {
35
- basename,
36
- backend: basenamePrefix || '',
37
- };
38
- }
39
- };
10
+ export let backend, basename, clusterName;
40
11
 
41
12
  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
42
13
 
43
- export let backend, basename, clusterName;
44
-
45
14
  function _configureStore(aRootReducer, history, singleClusterMode) {
46
15
  const {locationMiddleware, reducersWithLocation} = getLocationMiddleware(history, aRootReducer);
47
16
  const middlewares = applyMiddleware(thunkMiddleware, locationMiddleware);
@@ -51,7 +20,7 @@ function _configureStore(aRootReducer, history, singleClusterMode) {
51
20
  });
52
21
  }
53
22
 
54
- export default function configureStore(aRootReducer = rootReducer, singleClusterMode = true) {
23
+ function configureStore(aRootReducer = rootReducer, singleClusterMode = true) {
55
24
  ({backend, basename, clusterName} = getUrlData(window.location.href, singleClusterMode));
56
25
  const history = createBrowserHistory({basename});
57
26
 
@@ -59,3 +28,10 @@ export default function configureStore(aRootReducer = rootReducer, singleCluster
59
28
  listenForHistoryChange(store, history);
60
29
  return {history, store};
61
30
  }
31
+
32
+ export const webVersion = window.web_version;
33
+ export const customBackend = window.custom_backend;
34
+
35
+ export * from "./reducers"
36
+
37
+ export default configureStore;
@@ -1,16 +1,25 @@
1
- import {createSelector} from 'reselect';
1
+ import {createSelector, Selector} from 'reselect';
2
+ import {Reducer} from 'redux';
2
3
 
3
4
  import '../../services/api';
4
5
  import {TEvDescribeSchemeResult} from '../../types/api/schema';
5
6
  import {IConsumer} from '../../types/api/consumers';
7
+ import {IDescribeRootStateSlice, IDescribeState} from '../../types/store/describe';
8
+ import {IResponseError} from '../../types/api/error';
6
9
  import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
7
10
 
8
11
  const FETCH_DESCRIBE = createRequestActionTypes('describe', 'FETCH_DESCRIBE');
9
12
 
10
- const describe = (
11
- state = {loading: false, wasLoaded: false, data: {}},
12
- action: ApiRequestAction<typeof FETCH_DESCRIBE, TEvDescribeSchemeResult, unknown>,
13
- ) => {
13
+ const initialState = {
14
+ loading: false,
15
+ wasLoaded: false,
16
+ data: {},
17
+ };
18
+
19
+ const describe: Reducer<
20
+ IDescribeState,
21
+ ApiRequestAction<typeof FETCH_DESCRIBE, TEvDescribeSchemeResult, IResponseError>
22
+ > = (state = initialState, action) => {
14
23
  switch (action.type) {
15
24
  case FETCH_DESCRIBE.REQUEST: {
16
25
  return {
@@ -50,16 +59,21 @@ const describe = (
50
59
  };
51
60
 
52
61
  // Consumers selectors
53
- const selectConsumersNames = (state: any, path: string): string[] | undefined =>
54
- state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules;
62
+ const selectConsumersNames: Selector<IDescribeRootStateSlice, string[] | undefined, [string]> = (
63
+ state,
64
+ path,
65
+ ) => state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules;
55
66
 
56
- export const selectConsumers = createSelector(selectConsumersNames, (names = []): IConsumer[] => {
57
- const consumers = names.map((name) => {
58
- return {name};
59
- });
67
+ export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[]> = createSelector(
68
+ selectConsumersNames,
69
+ (names = []) => {
70
+ const consumers = names.map((name) => {
71
+ return {name};
72
+ });
60
73
 
61
- return consumers;
62
- });
74
+ return consumers;
75
+ },
76
+ );
63
77
 
64
78
  export function getDescribe({path}: {path: string}) {
65
79
  return createApiRequest({
@@ -1,10 +1,13 @@
1
+ import {Reducer} from 'redux';
2
+
1
3
  const SET_HEADER = 'SET_HEADER';
2
4
 
5
+ type IHeaderAction = ReturnType<typeof setHeader>;
3
6
  export type HeaderItemType = {text: string; link?: string};
4
7
 
5
8
  const initialState: HeaderItemType[] = [];
6
9
 
7
- const header = function (state = initialState, action: ReturnType<typeof setHeader>) {
10
+ const header: Reducer<HeaderItemType[], IHeaderAction> = function (state = initialState, action) {
8
11
  switch (action.type) {
9
12
  case SET_HEADER:
10
13
  return action.data;
@@ -2,10 +2,16 @@ import _flow from 'lodash/fp/flow';
2
2
  import _sortBy from 'lodash/fp/sortBy';
3
3
  import _uniqBy from 'lodash/fp/uniqBy';
4
4
  import _omit from 'lodash/omit';
5
- import {createSelector} from 'reselect';
6
-
7
- import {IIssuesTree} from '../../types/store/healthcheck';
5
+ import {createSelector, Selector} from 'reselect';
6
+ import {Reducer} from 'redux';
7
+
8
+ import {
9
+ IHealthcheckInfoState,
10
+ IHealthcheckInfoRootStateSlice,
11
+ IIssuesTree,
12
+ } from '../../types/store/healthcheck';
8
13
  import {HealthCheckAPIResponse, IssueLog, StatusFlag} from '../../types/api/healthcheck';
14
+ import {IResponseError} from '../../types/api/error';
9
15
 
10
16
  import '../../services/api';
11
17
  import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
@@ -14,10 +20,10 @@ const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK
14
20
 
15
21
  const initialState = {loading: false, wasLoaded: false};
16
22
 
17
- const healthcheckInfo = function (
18
- state = initialState,
19
- action: ApiRequestAction<typeof FETCH_HEALTHCHECK, HealthCheckAPIResponse, unknown>,
20
- ) {
23
+ const healthcheckInfo: Reducer<
24
+ IHealthcheckInfoState,
25
+ ApiRequestAction<typeof FETCH_HEALTHCHECK, HealthCheckAPIResponse, IResponseError>
26
+ > = function (state = initialState, action) {
21
27
  switch (action.type) {
22
28
  case FETCH_HEALTHCHECK.REQUEST: {
23
29
  return {
@@ -60,7 +66,7 @@ const getReasonsForIssue = ({issue, data}: {issue: IssueLog; data: IssueLog[]})
60
66
  return data.filter((item) => issue.reason && issue.reason.indexOf(item.id) !== -1);
61
67
  };
62
68
 
63
- const getRoots = (data: IssueLog[]) => {
69
+ const getRoots = (data: IssueLog[]): IssueLog[] => {
64
70
  let roots = data.filter((item) => {
65
71
  return !data.find((issue) => issue.reason && issue.reason.indexOf(item.id) !== -1);
66
72
  });
@@ -95,22 +101,23 @@ const getInvertedConsequencesTree = ({
95
101
  : [];
96
102
  };
97
103
 
98
- const getIssuesLog = (state: any): IssueLog[] | undefined => state.healthcheckInfo.data?.issue_log;
104
+ const getIssuesLog = (state: IHealthcheckInfoRootStateSlice) =>
105
+ state.healthcheckInfo.data?.issue_log;
99
106
 
100
- export const selectIssuesTreesRoots = createSelector(getIssuesLog, (issues = []) =>
101
- getRoots(issues),
102
- );
107
+ export const selectIssuesTreesRoots: Selector<IHealthcheckInfoRootStateSlice, IssueLog[]> =
108
+ createSelector(getIssuesLog, (issues = []) => getRoots(issues));
103
109
 
104
- export const selectIssuesTrees = createSelector(
105
- [getIssuesLog, selectIssuesTreesRoots],
106
- (data = [], roots = []) => {
110
+ export const selectIssuesTrees: Selector<IHealthcheckInfoRootStateSlice, IIssuesTree[]> =
111
+ createSelector([getIssuesLog, selectIssuesTreesRoots], (data = [], roots = []) => {
107
112
  return getInvertedConsequencesTree({data, roots});
108
- },
109
- );
113
+ });
110
114
 
111
- export const selectIssuesTreeById = createSelector(
112
- [selectIssuesTrees, (_: any, id: string | undefined) => id],
113
- (issuesTrees = [], id) => issuesTrees.find((issuesTree: IIssuesTree) => issuesTree.id === id),
115
+ export const selectIssuesTreeById: Selector<
116
+ IHealthcheckInfoRootStateSlice,
117
+ IIssuesTree | undefined,
118
+ [string | undefined]
119
+ > = createSelector([selectIssuesTrees, (_, id: string | undefined) => id], (issuesTrees = [], id) =>
120
+ issuesTrees.find((issuesTree) => issuesTree.id === id),
114
121
  );
115
122
 
116
123
  export function getHealthcheckInfo(database: string) {
@@ -34,10 +34,7 @@ import authentication from './authentication';
34
34
  import header from './header';
35
35
  import saveQuery from './saveQuery';
36
36
  import fullscreen from './fullscreen';
37
-
38
- function singleClusterMode(state = true) {
39
- return state;
40
- }
37
+ import singleClusterMode from './singleClusterMode';
41
38
 
42
39
  export const rootReducer = {
43
40
  singleClusterMode,
@@ -77,6 +74,11 @@ export const rootReducer = {
77
74
  fullscreen,
78
75
  };
79
76
 
80
- export default combineReducers({
77
+ const combinedReducer = combineReducers({
81
78
  ...rootReducer,
82
79
  });
80
+
81
+ export type IRootReducer = typeof combinedReducer;
82
+ export type IRootState = ReturnType<IRootReducer>;
83
+
84
+ export default combinedReducer;
@@ -1,6 +1,6 @@
1
1
  import '../../services/api';
2
2
 
3
- import type {ErrorRepsonse, ExecuteActions} from '../../types/api/query';
3
+ import type {ErrorResponse, ExecuteActions} from '../../types/api/query';
4
4
  import type {IQueryResult} from '../../types/store/query';
5
5
  import {parseQueryAPIExecuteResponse} from '../../utils/query';
6
6
 
@@ -16,7 +16,7 @@ const initialState = {
16
16
 
17
17
  const preview = (
18
18
  state = initialState,
19
- action: ApiRequestAction<typeof SEND_QUERY, IQueryResult, ErrorRepsonse> | ReturnType<typeof setQueryOptions>,
19
+ action: ApiRequestAction<typeof SEND_QUERY, IQueryResult, ErrorResponse> | ReturnType<typeof setQueryOptions>,
20
20
  ) => {
21
21
  switch (action.type) {
22
22
  case SEND_QUERY.REQUEST: {
@@ -1,12 +1,14 @@
1
+ import {Reducer} from 'redux';
2
+
1
3
  const SET_QUERY_NAME_TO_EDIT = 'SET_QUERY_NAME_TO_EDIT';
2
4
  const CLEAR_QUERY_NAME_TO_EDIT = 'CLEAR_QUERY_NAME_TO_EDIT';
3
5
 
6
+ type IAction = ReturnType<typeof setQueryNameToEdit> | ReturnType<typeof clearQueryNameToEdit>;
7
+ type ISaveQueryState = string | null;
8
+
4
9
  const initialState = null;
5
10
 
6
- const saveQuery = function (
7
- state = initialState,
8
- action: ReturnType<typeof setQueryNameToEdit> | ReturnType<typeof clearQueryNameToEdit>,
9
- ) {
11
+ const saveQuery: Reducer<ISaveQueryState, IAction> = function (state = initialState, action) {
10
12
  switch (action.type) {
11
13
  case SET_QUERY_NAME_TO_EDIT:
12
14
  return action.data;
@@ -0,0 +1,5 @@
1
+ function singleClusterMode(state = true) {
2
+ return state;
3
+ }
4
+
5
+ export default singleClusterMode;
@@ -0,0 +1,4 @@
1
+ export interface IResponseError {
2
+ status: number;
3
+ statusText: string;
4
+ }
@@ -8,16 +8,16 @@ export interface CommonFields {
8
8
  ast?: AST;
9
9
  plan?: Plan;
10
10
  stats?: Stats;
11
- };
11
+ }
12
12
 
13
13
  interface DeprecatedCommonFields {
14
14
  stats?: Stats;
15
15
  }
16
16
 
17
- export interface ErrorRepsonse {
17
+ export interface ErrorResponse {
18
18
  error?: any;
19
19
  issues?: any;
20
- };
20
+ }
21
21
 
22
22
  export type ExecuteActions = 'execute-script' | 'execute' | 'execute-scan' | undefined;
23
23
  export type ExplainActions = 'explain' | 'explain-ast';
@@ -34,7 +34,7 @@ type CellValue = string | number | null | undefined;
34
34
 
35
35
  export type KeyValueRow<T = CellValue> = {
36
36
  [key: string]: T;
37
- }
37
+ };
38
38
 
39
39
  export type ArrayRow<T = CellValue> = Array<T>;
40
40
 
@@ -63,17 +63,15 @@ export type ExecuteYdbResponse = {
63
63
  result: KeyValueRow[];
64
64
  } & CommonFields;
65
65
 
66
- type ExecuteResponse<Schema extends Schemas> =
66
+ type ExecuteResponse<Schema extends Schemas> =
67
67
  | CommonFields // result can be undefined for queries like `insert into`
68
- | (
69
- Schema extends 'modern'
70
- ? ExecuteModernResponse
71
- : Schema extends 'ydb'
72
- ? ExecuteYdbResponse
73
- : Schema extends 'classic' | undefined
74
- ? ExecuteClassicResponse
75
- : unknown
76
- );
68
+ | (Schema extends 'modern'
69
+ ? ExecuteModernResponse
70
+ : Schema extends 'ydb'
71
+ ? ExecuteYdbResponse
72
+ : Schema extends 'classic' | undefined
73
+ ? ExecuteClassicResponse
74
+ : unknown);
77
75
 
78
76
  // deprecated response from older versions, backward compatibility
79
77
 
@@ -92,8 +90,9 @@ export type DeprecatedExecuteResponseDeep = {
92
90
  // can be undefined for queries like `insert into`
93
91
  export type DeprecatedExecuteResponsePlain = DeprecatedExecuteResponseValue | undefined;
94
92
 
95
- export type DeprecatedExecuteResponse = DeprecatedExecuteResponseDeep | DeprecatedExecuteResponsePlain;
96
-
93
+ export type DeprecatedExecuteResponse =
94
+ | DeprecatedExecuteResponseDeep
95
+ | DeprecatedExecuteResponsePlain;
97
96
 
98
97
  // ==== EXPLAIN ====
99
98
 
@@ -103,14 +102,12 @@ type ExplainResponse = CommonFields;
103
102
 
104
103
  // deprecated response from older versions, backward compatibility
105
104
 
106
- type DeprecatedExplainResponse<Action extends ExplainActions> = (
105
+ type DeprecatedExplainResponse<Action extends ExplainActions> =
107
106
  Action extends 'explain-ast'
108
107
  ? ({result: {ast: AST}} & Required<DeprecatedCommonFields>) | {ast: AST}
109
108
  : Action extends 'explain'
110
109
  ? ({result: Plan} & Required<DeprecatedCommonFields>) | Plan
111
- : unknown
112
- );
113
-
110
+ : unknown;
114
111
 
115
112
  // ==== COMBINED API RESPONSE ====
116
113
 
@@ -124,15 +121,14 @@ export type QueryAPIExplainResponse<Action extends ExplainActions> =
124
121
  | DeprecatedExplainResponse<Action>
125
122
  | null;
126
123
 
127
- export type QueryAPIResponse<Action extends Actions, Schema extends Schemas = undefined> = (
124
+ export type QueryAPIResponse<Action extends Actions, Schema extends Schemas = undefined> =
128
125
  Action extends ExecuteActions
129
126
  ? QueryAPIExecuteResponse<Schema>
130
127
  : Action extends ExplainActions
131
128
  ? QueryAPIExplainResponse<Action>
132
- : unknown
133
- );
129
+ : unknown;
134
130
 
135
- export type AnyExecuteResponse =
131
+ export type AnyExecuteResponse =
136
132
  | ExecuteModernResponse
137
133
  | ExecuteClassicResponse
138
134
  | ExecuteYdbResponse
@@ -146,13 +142,11 @@ export type DeepExecuteResponse =
146
142
  | ExecuteYdbResponse
147
143
  | DeprecatedExecuteResponseDeep;
148
144
 
149
- export type AnyExplainResponse =
145
+ export type AnyExplainResponse =
150
146
  | ExplainResponse
151
147
  | CommonFields
152
148
  | DeprecatedExplainResponse<'explain'>
153
149
  | DeprecatedExplainResponse<'explain-ast'>
154
150
  | null;
155
151
 
156
- export type AnyResponse =
157
- | AnyExecuteResponse
158
- | AnyExplainResponse;
152
+ export type AnyResponse = AnyExecuteResponse | AnyExplainResponse;
@@ -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
+ }
@@ -0,0 +1,14 @@
1
+ import {IResponseError} from '../api/error';
2
+ import {TEvDescribeSchemeResult} from '../api/schema';
3
+
4
+ export interface IDescribeState {
5
+ loading: boolean;
6
+ wasLoaded: boolean;
7
+ data: Record<string, TEvDescribeSchemeResult>;
8
+ currentDescribe?: TEvDescribeSchemeResult;
9
+ error?: IResponseError;
10
+ }
11
+
12
+ export interface IDescribeRootStateSlice {
13
+ describe: IDescribeState;
14
+ }
@@ -1,3 +1,4 @@
1
+ import {IResponseError} from '../api/error';
1
2
  import type {HealthCheckAPIResponse, IssueLog} from '../api/healthcheck';
2
3
 
3
4
  export interface IIssuesTree extends IssueLog {
@@ -5,3 +6,14 @@ export interface IIssuesTree extends IssueLog {
5
6
  }
6
7
 
7
8
  export type IHealthCheck = HealthCheckAPIResponse;
9
+
10
+ export interface IHealthcheckInfoState {
11
+ loading: boolean;
12
+ wasLoaded: boolean;
13
+ data?: HealthCheckAPIResponse;
14
+ error?: IResponseError;
15
+ }
16
+
17
+ export interface IHealthcheckInfoRootStateSlice {
18
+ healthcheckInfo: IHealthcheckInfoState;
19
+ }
@@ -30,4 +30,11 @@ interface Window {
30
30
  Ya?: {
31
31
  Rum?: RumCounter;
32
32
  };
33
+
34
+ // eslint-disable-next-line
35
+ web_version?: boolean;
36
+ // eslint-disable-next-line
37
+ custom_backend?: string;
38
+
39
+ __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof import('redux').compose;
33
40
  }
@@ -1 +1,2 @@
1
1
  export * from './useAutofetcher';
2
+ export * from './useTypedSelector';
@@ -0,0 +1,5 @@
1
+ import {TypedUseSelectorHook, useSelector} from 'react-redux';
2
+
3
+ import {IRootState} from '../../store';
4
+
5
+ export const useTypedSelector: TypedUseSelectorHook<IRootState> = useSelector;
@@ -4,6 +4,7 @@ import {
4
4
  TopologyNodeDataStats,
5
5
  TopologyNodeDataStatsSection,
6
6
  ExplainPlanNodeData,
7
+ TopologyNodeDataStatsItem,
7
8
  } from '@yandex-cloud/paranoid';
8
9
 
9
10
  interface PlanOperator {
@@ -25,6 +26,8 @@ export interface RootPlan {
25
26
  Plan: Plan;
26
27
  }
27
28
 
29
+ const CONNECTION_NODE_META_FIELDS = new Set(['PlanNodeId', 'PlanNodeType', 'Node Type', 'Plans']);
30
+
28
31
  function prepareStats(plan: Plan) {
29
32
  const stats: TopologyNodeDataStats[] = [];
30
33
 
@@ -52,21 +55,52 @@ function prepareStats(plan: Plan) {
52
55
  });
53
56
  }
54
57
 
58
+ if (plan.PlanNodeType === 'Connection') {
59
+ const attrStats: TopologyNodeDataStatsItem[] = [];
60
+
61
+ for (const [key, value] of Object.entries(plan)) {
62
+ if (CONNECTION_NODE_META_FIELDS.has(key)) {
63
+ continue;
64
+ }
65
+
66
+ attrStats.push({name: key, value: String(value)});
67
+ }
68
+
69
+ if (attrStats.length > 0) {
70
+ stats.push({
71
+ group: 'Attributes',
72
+ stats: attrStats,
73
+ });
74
+ }
75
+ }
76
+
55
77
  return stats;
56
78
  }
57
79
 
80
+ function getNodeType(plan: Plan) {
81
+ switch (plan.PlanNodeType) {
82
+ case 'Connection':
83
+ return 'connection';
84
+ case 'ResultSet':
85
+ return 'result';
86
+ case 'Query':
87
+ return 'query';
88
+ default:
89
+ return 'stage';
90
+ }
91
+ }
92
+
58
93
  export function preparePlan(plan: Plan) {
59
94
  const nodes: GraphNode[] = [];
60
95
  const links: Link[] = [];
61
96
 
62
- function parsePlans(plans: Plan[] = [], from: string, curDepth: number) {
63
- const depth = curDepth + 1;
97
+ function parsePlans(plans: Plan[] = [], from: string) {
64
98
  plans.forEach((p) => {
65
99
  const node: GraphNode<ExplainPlanNodeData> = {
66
100
  name: String(p.PlanNodeId),
67
101
  data: {
68
102
  id: p.PlanNodeId,
69
- type: p.PlanNodeType === 'Connection' ? 'connection' : 'stage',
103
+ type: getNodeType(p),
70
104
  name: p['Node Type'],
71
105
  operators: p.Operators?.map((o) => o.Name),
72
106
  stats: prepareStats(p),
@@ -75,7 +109,7 @@ export function preparePlan(plan: Plan) {
75
109
  };
76
110
  nodes.push(node);
77
111
  links.push({from, to: node.name});
78
- parsePlans(p.Plans, node.name, depth);
112
+ parsePlans(p.Plans, node.name);
79
113
  });
80
114
  }
81
115
 
@@ -84,12 +118,13 @@ export function preparePlan(plan: Plan) {
84
118
  name: String(rootPlan.PlanNodeId),
85
119
  data: {
86
120
  id: rootPlan.PlanNodeId,
87
- type: 'stage',
121
+ type: getNodeType(rootPlan),
88
122
  name: rootPlan['Node Type'],
89
123
  },
90
124
  };
91
125
  nodes.push(rootNode);
92
- parsePlans(rootPlan.Plans, rootNode.name, 0);
126
+ parsePlans(rootPlan.Plans, rootNode.name);
127
+
93
128
  return {
94
129
  nodes,
95
130
  links,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "2.4.1",
3
+ "version": "2.4.3",
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",
@@ -33,7 +33,7 @@
33
33
  "redux": "4.0.1",
34
34
  "redux-location-state": "2.6.0",
35
35
  "redux-thunk": "2.3.0",
36
- "reselect": "4.0.0",
36
+ "reselect": "4.1.6",
37
37
  "sass": "1.32.8",
38
38
  "web-vitals": "1.1.2",
39
39
  "ydb-ui-components": "3.0.1"