ydb-embedded-ui 4.10.1 → 4.11.0
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 +17 -0
- package/dist/components/QueryResultTable/Cell/Cell.tsx +8 -8
- package/dist/components/QueryResultTable/i18n/en.json +1 -1
- package/dist/components/QueryResultTable/i18n/ru.json +1 -1
- package/dist/components/ShortyString/ShortyString.tsx +3 -6
- package/dist/components/ShortyString/i18n/en.json +8 -8
- package/dist/components/ShortyString/i18n/ru.json +8 -8
- package/dist/components/SpeedMultiMeter/i18n/index.ts +0 -2
- package/dist/components/Stack/Stack.tsx +16 -16
- package/dist/components/TableSkeleton/TableSkeleton.tsx +3 -3
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +7 -3
- package/dist/containers/Header/Header.tsx +2 -2
- package/dist/containers/Header/breadcrumbs.ts +2 -1
- package/dist/containers/Heatmap/Heatmap.tsx +4 -3
- package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +2 -8
- package/dist/containers/Nodes/Nodes.tsx +1 -1
- package/dist/containers/Storage/EmptyFilter/i18n/en.json +2 -2
- package/dist/containers/Storage/EmptyFilter/i18n/ru.json +2 -2
- package/dist/containers/Storage/StorageGroups/i18n/en.json +5 -5
- package/dist/containers/Storage/StorageGroups/i18n/ru.json +5 -5
- package/dist/containers/Storage/UsageFilter/i18n/en.json +3 -8
- package/dist/containers/Storage/UsageFilter/i18n/ru.json +3 -8
- package/dist/containers/Tablet/Tablet.tsx +2 -2
- package/dist/containers/Tenant/Acl/Acl.scss +1 -9
- package/dist/containers/Tenant/Acl/Acl.tsx +137 -0
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -0
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +2 -0
- package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +2 -3
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +0 -6
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +95 -83
- package/dist/containers/Tenant/Query/Issues/Issues.tsx +27 -23
- package/dist/containers/Tenant/Query/Issues/models.ts +0 -11
- package/dist/containers/Tenant/Query/Preview/Preview.tsx +3 -3
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +99 -0
- package/dist/containers/Tenant/Tenant.tsx +1 -5
- package/dist/containers/Tenant/TenantPages.tsx +9 -14
- package/dist/containers/Tenant/i18n/en.json +11 -0
- package/dist/containers/Tenant/i18n/index.ts +11 -0
- package/dist/containers/Tenant/i18n/ru.json +11 -0
- package/dist/containers/Tenant/utils/schema.ts +24 -0
- package/dist/containers/Tenant/utils/schemaActions.ts +28 -24
- package/dist/containers/Tenants/Tenants.tsx +1 -4
- package/dist/services/api.ts +6 -7
- package/dist/store/index.js +1 -1
- package/dist/store/reducers/nodes/nodes.ts +14 -5
- package/dist/store/reducers/nodes/types.ts +22 -3
- package/dist/store/reducers/nodes/utils.ts +23 -10
- package/dist/store/reducers/preview.ts +6 -4
- package/dist/store/reducers/schemaAcl/schemaAcl.ts +17 -0
- package/dist/store/reducers/schemaAcl/types.ts +9 -7
- package/dist/store/reducers/tenant/constants.ts +6 -0
- package/dist/store/reducers/tenant/tenant.ts +15 -0
- package/dist/store/reducers/tenant/types.ts +18 -3
- package/dist/store/state-url-mapping.js +3 -0
- package/dist/types/api/cluster.ts +1 -1
- package/dist/types/api/compute.ts +11 -11
- package/dist/types/api/error.ts +2 -2
- package/dist/types/api/netInfo.ts +3 -3
- package/dist/types/api/nodes.ts +9 -8
- package/dist/types/api/query.ts +1 -1
- package/dist/types/api/schema/schema.ts +3 -0
- package/dist/types/api/schema/shared.ts +3 -3
- package/dist/types/api/schema/table.ts +22 -22
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/assets.d.ts +1 -2
- package/dist/types/store/executeQuery.ts +2 -3
- package/dist/types/store/executeTopQueries.ts +8 -5
- package/dist/types/store/explainQuery.ts +4 -4
- package/dist/types/store/query.ts +4 -3
- package/dist/types/store/shardsWorkload.ts +8 -5
- package/dist/utils/constants.ts +4 -1
- package/dist/utils/error.ts +2 -3
- package/dist/utils/query.ts +3 -9
- package/dist/utils/tests/providers.tsx +6 -9
- package/package.json +6 -2
- package/dist/containers/Tenant/Acl/Acl.js +0 -153
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +0 -94
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.11.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.10.1...v4.11.0) (2023-07-27)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* support external objects in schema tree ([#485](https://github.com/ydb-platform/ydb-embedded-ui/issues/485)) ([cf96f9a](https://github.com/ydb-platform/ydb-embedded-ui/commit/cf96f9af02db1352f3990f21f8a84c1282229517))
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* **ClusterInfo:** change cluster default name ([#478](https://github.com/ydb-platform/ydb-embedded-ui/issues/478)) ([398df6e](https://github.com/ydb-platform/ydb-embedded-ui/commit/398df6e3a5778c245653f61b41ba2e1bd0ea3a51))
|
14
|
+
* fix copy schema action ([#483](https://github.com/ydb-platform/ydb-embedded-ui/issues/483)) ([f6b01c3](https://github.com/ydb-platform/ydb-embedded-ui/commit/f6b01c3cc2808337d5597f990f65ff3e7c010b05))
|
15
|
+
* **Nodes:** support v2 compute ([#476](https://github.com/ydb-platform/ydb-embedded-ui/issues/476)) ([696d43a](https://github.com/ydb-platform/ydb-embedded-ui/commit/696d43a04109c7fc68986e036e66767593af8d00))
|
16
|
+
* **ObjectSummary:** fix issue on object change with active schema tab ([#482](https://github.com/ydb-platform/ydb-embedded-ui/issues/482)) ([b50db5f](https://github.com/ydb-platform/ydb-embedded-ui/commit/b50db5ff742c5c7fc27e292309831b937e5d40bd))
|
17
|
+
* **ObjectSummary:** fix wrong tree alignment bug ([#486](https://github.com/ydb-platform/ydb-embedded-ui/issues/486)) ([e8bfe99](https://github.com/ydb-platform/ydb-embedded-ui/commit/e8bfe99657870c735a41d24febaa907ac1383479))
|
18
|
+
* **Query:** process null issues error ([#480](https://github.com/ydb-platform/ydb-embedded-ui/issues/480)) ([4c4e684](https://github.com/ydb-platform/ydb-embedded-ui/commit/4c4e6845e539296ecbdefa930bc63d3321f277dc))
|
19
|
+
|
3
20
|
## [4.10.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.10.0...v4.10.1) (2023-07-14)
|
4
21
|
|
5
22
|
|
@@ -11,16 +11,16 @@ interface CellProps {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
export const Cell = React.memo(function Cell(props: CellProps) {
|
14
|
-
const {
|
15
|
-
className,
|
16
|
-
value,
|
17
|
-
} = props;
|
14
|
+
const {className, value} = props;
|
18
15
|
|
19
16
|
const dispatch = useDispatch();
|
20
17
|
|
21
|
-
useEffect(
|
22
|
-
|
23
|
-
|
18
|
+
useEffect(
|
19
|
+
() => () => {
|
20
|
+
dispatch(hideTooltip());
|
21
|
+
},
|
22
|
+
[dispatch],
|
23
|
+
);
|
24
24
|
|
25
25
|
return (
|
26
26
|
<span
|
@@ -29,5 +29,5 @@ export const Cell = React.memo(function Cell(props: CellProps) {
|
|
29
29
|
>
|
30
30
|
{value}
|
31
31
|
</span>
|
32
|
-
)
|
32
|
+
);
|
33
33
|
});
|
@@ -33,18 +33,15 @@ export default function ShortyString({
|
|
33
33
|
const [expanded, setExpanded] = React.useState(false);
|
34
34
|
|
35
35
|
const toggleLabelAction = expanded ? collapseLabel : expandLabel;
|
36
|
-
const toggleLabelSymbolsCount =
|
37
|
-
? i18n('chars_count', {count: value.length})
|
38
|
-
: '';
|
36
|
+
const toggleLabelSymbolsCount =
|
37
|
+
displayLength && !expanded ? i18n('chars_count', {count: value.length}) : '';
|
39
38
|
const toggleLabel = toggleLabelAction + toggleLabelSymbolsCount;
|
40
39
|
|
41
40
|
// showing toogle button with a label that is longer than the hidden part is pointless,
|
42
41
|
// hence compare to limit + length in the not-strict mode
|
43
42
|
const hasToggle = value.length > limit + (strict ? 0 : toggleLabel.length);
|
44
43
|
|
45
|
-
const text = expanded || !hasToggle
|
46
|
-
? value
|
47
|
-
: value.slice(0, limit - 4) + '\u00a0...';
|
44
|
+
const text = expanded || !hasToggle ? value : value.slice(0, limit - 4) + '\u00a0...';
|
48
45
|
|
49
46
|
return (
|
50
47
|
<div className={block()}>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
"default_collapse_label": "Show less",
|
3
|
+
"default_expand_label": "Show more",
|
4
|
+
"chars_count": [
|
5
|
+
" ({{count}} symbol)",
|
6
|
+
" ({{count}} symbols)",
|
7
|
+
" ({{count}} symbols)",
|
8
|
+
" ({{count}} symbols)"
|
9
|
+
]
|
10
10
|
}
|
@@ -1,10 +1,10 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
"default_collapse_label": "Показать меньше",
|
3
|
+
"default_expand_label": "Показать ещё",
|
4
|
+
"chars_count": [
|
5
|
+
" ({{count}} символ)",
|
6
|
+
" ({{count}} символа)",
|
7
|
+
" ({{count}} символов)",
|
8
|
+
" ({{count}} символов)"
|
9
|
+
]
|
10
10
|
}
|
@@ -13,23 +13,23 @@ const b = cn('stack');
|
|
13
13
|
|
14
14
|
export const Stack: React.FC<StackProps> = ({children, className}) => (
|
15
15
|
<div className={b(null, className)}>
|
16
|
-
{
|
17
|
-
React.
|
18
|
-
|
19
|
-
|
20
|
-
}
|
16
|
+
{React.Children.map(children, (child, index) => {
|
17
|
+
if (!React.isValidElement(child)) {
|
18
|
+
return null;
|
19
|
+
}
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
return (
|
22
|
+
<div
|
23
|
+
className={b('layer')}
|
24
|
+
style={
|
25
|
+
{
|
26
26
|
[LAYER_CSS_VAR]: index,
|
27
|
-
} as React.CSSProperties
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
}
|
27
|
+
} as React.CSSProperties
|
28
|
+
}
|
29
|
+
>
|
30
|
+
{child}
|
31
|
+
</div>
|
32
|
+
);
|
33
|
+
})}
|
34
34
|
</div>
|
35
35
|
);
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import {
|
1
|
+
import {FC} from 'react';
|
2
2
|
import block from 'bem-cn-lite';
|
3
|
-
import {
|
3
|
+
import {Skeleton} from '@gravity-ui/uikit';
|
4
4
|
|
5
5
|
import './TableSkeleton.scss';
|
6
6
|
|
@@ -11,7 +11,7 @@ interface TableSkeletonProps {
|
|
11
11
|
rows?: number;
|
12
12
|
}
|
13
13
|
|
14
|
-
export const TableSkeleton: FC<TableSkeletonProps> = ({
|
14
|
+
export const TableSkeleton: FC<TableSkeletonProps> = ({rows = 2, className}) => (
|
15
15
|
<div className={b(null, className)}>
|
16
16
|
<div className={b('row')}>
|
17
17
|
<Skeleton className={b('col-1')} />
|
@@ -18,7 +18,11 @@ import type {TClusterInfo} from '../../../types/api/cluster';
|
|
18
18
|
import {backend, customBackend} from '../../../store';
|
19
19
|
import {formatStorageValues} from '../../../utils';
|
20
20
|
import {useSetting, useTypedSelector} from '../../../utils/hooks';
|
21
|
-
import {
|
21
|
+
import {
|
22
|
+
CLUSTER_DEFAULT_TITLE,
|
23
|
+
CLUSTER_INFO_HIDDEN_KEY,
|
24
|
+
DEVELOPER_UI_TITLE,
|
25
|
+
} from '../../../utils/constants';
|
22
26
|
|
23
27
|
import {VersionsBar} from '../VersionsBar/VersionsBar';
|
24
28
|
import {ClusterInfoSkeleton} from '../ClusterInfoSkeleton/ClusterInfoSkeleton';
|
@@ -148,7 +152,7 @@ export const ClusterInfo = ({
|
|
148
152
|
const {info = [], links = []} = additionalClusterProps;
|
149
153
|
|
150
154
|
const clusterInfo = getInfo(cluster, versionsValues, info, [
|
151
|
-
{title:
|
155
|
+
{title: DEVELOPER_UI_TITLE, url: internalLink},
|
152
156
|
...links,
|
153
157
|
]);
|
154
158
|
|
@@ -173,7 +177,7 @@ export const ClusterInfo = ({
|
|
173
177
|
<EntityStatus
|
174
178
|
size="m"
|
175
179
|
status={cluster?.Overall}
|
176
|
-
name={cluster?.Name ??
|
180
|
+
name={cluster?.Name ?? CLUSTER_DEFAULT_TITLE}
|
177
181
|
className={b('title')}
|
178
182
|
/>
|
179
183
|
);
|
@@ -10,7 +10,7 @@ import {ExternalLinkWithIcon} from '../../components/ExternalLinkWithIcon/Extern
|
|
10
10
|
import {backend, customBackend} from '../../store';
|
11
11
|
import {getClusterInfo} from '../../store/reducers/cluster/cluster';
|
12
12
|
import {useTypedSelector} from '../../utils/hooks';
|
13
|
-
import {
|
13
|
+
import {DEVELOPER_UI_TITLE} from '../../utils/constants';
|
14
14
|
import {parseQuery} from '../../routes';
|
15
15
|
|
16
16
|
import {RawBreadcrumbItem, getBreadcrumbs} from './breadcrumbs';
|
@@ -105,7 +105,7 @@ function Header({mainPage}: HeaderProps) {
|
|
105
105
|
</div>
|
106
106
|
|
107
107
|
<ExternalLinkWithIcon
|
108
|
-
title={
|
108
|
+
title={DEVELOPER_UI_TITLE}
|
109
109
|
url={getInternalLink(singleClusterMode)}
|
110
110
|
/>
|
111
111
|
</header>
|
@@ -16,6 +16,7 @@ import {
|
|
16
16
|
TENANT_PAGES_IDS,
|
17
17
|
} from '../../store/reducers/tenant/constants';
|
18
18
|
import routes, {createHref} from '../../routes';
|
19
|
+
import {CLUSTER_DEFAULT_TITLE} from '../../utils/constants';
|
19
20
|
|
20
21
|
import {getClusterPath} from '../Cluster/utils';
|
21
22
|
import {TenantTabsGroups, getTenantPath} from '../Tenant/TenantPages';
|
@@ -39,7 +40,7 @@ const getClusterBreadcrumbs = (
|
|
39
40
|
|
40
41
|
return [
|
41
42
|
{
|
42
|
-
text: clusterName ||
|
43
|
+
text: clusterName || CLUSTER_DEFAULT_TITLE,
|
43
44
|
link: getClusterPath(clusterTab, query),
|
44
45
|
icon: nodesRightIcon,
|
45
46
|
},
|
@@ -4,13 +4,14 @@ import cn from 'bem-cn-lite';
|
|
4
4
|
|
5
5
|
import {Checkbox, Select} from '@gravity-ui/uikit';
|
6
6
|
|
7
|
+
import type {IHeatmapMetricValue} from '../../types/store/heatmap';
|
7
8
|
import {getTabletsInfo, setHeatmapOptions} from '../../store/reducers/heatmap';
|
8
9
|
import {showTooltip, hideTooltip} from '../../store/reducers/tooltip';
|
9
10
|
import {formatNumber} from '../../utils';
|
10
|
-
import {prepareQueryError} from '../../utils/query';
|
11
11
|
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
|
12
|
+
|
12
13
|
import {Loader} from '../../components/Loader';
|
13
|
-
import
|
14
|
+
import {ResponseError} from '../../components/Errors/ResponseError';
|
14
15
|
|
15
16
|
import {COLORS_RANGE_SIZE, getColorRange, getColorIndex, getCurrentMetricLimits} from './util';
|
16
17
|
import {HeatmapCanvas} from './HeatmapCanvas/HeatmapCanvas';
|
@@ -196,7 +197,7 @@ export const Heatmap = ({path}: HeatmapProps) => {
|
|
196
197
|
}
|
197
198
|
|
198
199
|
if (error) {
|
199
|
-
return <
|
200
|
+
return <ResponseError error={error} />;
|
200
201
|
}
|
201
202
|
|
202
203
|
return renderContent();
|
@@ -12,14 +12,8 @@ interface PDiskTitleBadgeProps {
|
|
12
12
|
export function PDiskTitleBadge({label, value, className}: PDiskTitleBadgeProps) {
|
13
13
|
return (
|
14
14
|
<span className={b('pdisk-title-item', className)}>
|
15
|
-
{label && (
|
16
|
-
|
17
|
-
{label}:
|
18
|
-
</span>
|
19
|
-
)}
|
20
|
-
<span className={b('pdisk-title-item-value')}>
|
21
|
-
{value}
|
22
|
-
</span>
|
15
|
+
{label && <span className={b('pdisk-title-item-label')}>{label}:</span>}
|
16
|
+
<span className={b('pdisk-title-item-value')}>{value}</span>
|
23
17
|
</span>
|
24
18
|
);
|
25
19
|
}
|
@@ -71,7 +71,7 @@ export const Nodes = ({path, type, additionalNodesInfo = {}}: NodesProps) => {
|
|
71
71
|
// For not DB entities we always use /compute endpoint instead of /nodes
|
72
72
|
// since /nodes can return data only for tenants
|
73
73
|
if (path && (!useNodesEndpoint || !isDatabaseEntityType(type))) {
|
74
|
-
dispatch(getComputeNodes(path));
|
74
|
+
dispatch(getComputeNodes({path}));
|
75
75
|
} else {
|
76
76
|
dispatch(getNodes({tenant: path}));
|
77
77
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
"empty.default": "No such groups",
|
3
|
+
"empty.out_of_space": "No groups with out of space errors",
|
4
|
+
"empty.degraded": "No degraded groups",
|
5
|
+
"show_all": "Show all groups",
|
6
|
+
"encrypted": "Encrypted group"
|
7
7
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
"empty.default": "Нет групп",
|
3
|
+
"empty.out_of_space": "Нет групп, в которых кончается место",
|
4
|
+
"empty.degraded": "Нет деградировавших групп",
|
5
|
+
"show_all": "Показать все группы",
|
6
|
+
"encrypted": "Зашифрованная группа"
|
7
7
|
}
|
@@ -1,10 +1,5 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
"{{count}} group",
|
6
|
-
"{{count}} groups",
|
7
|
-
"{{count}} groups",
|
8
|
-
"No groups"
|
9
|
-
]
|
2
|
+
"label": "Usage:",
|
3
|
+
"default_value": "Any",
|
4
|
+
"groups_count": ["{{count}} group", "{{count}} groups", "{{count}} groups", "No groups"]
|
10
5
|
}
|
@@ -1,10 +1,5 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
"{{count}} группа",
|
6
|
-
"{{count}} группы",
|
7
|
-
"{{count}} групп",
|
8
|
-
"Нет групп"
|
9
|
-
]
|
2
|
+
"label": "Использование:",
|
3
|
+
"default_value": "Любое",
|
4
|
+
"groups_count": ["{{count}} группа", "{{count}} группы", "{{count}} групп", "Нет групп"]
|
10
5
|
}
|
@@ -9,7 +9,7 @@ import {getTablet, getTabletDescribe, clearTabletData} from '../../store/reducer
|
|
9
9
|
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
|
10
10
|
|
11
11
|
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
|
12
|
-
import {
|
12
|
+
import {DEVELOPER_UI_TITLE} from '../../utils/constants';
|
13
13
|
import '../../services/api';
|
14
14
|
import {parseQuery} from '../../routes';
|
15
15
|
|
@@ -113,7 +113,7 @@ export const Tablet = () => {
|
|
113
113
|
|
114
114
|
const externalLinks = [
|
115
115
|
{
|
116
|
-
name: `${
|
116
|
+
name: `${DEVELOPER_UI_TITLE} - tablet`,
|
117
117
|
path: `/tablets?TabletID=${TabletId}`,
|
118
118
|
},
|
119
119
|
];
|
@@ -1,6 +1,6 @@
|
|
1
1
|
@import '../../../styles/mixins.scss';
|
2
2
|
|
3
|
-
.
|
3
|
+
.ydb-acl {
|
4
4
|
display: flex;
|
5
5
|
overflow: auto;
|
6
6
|
flex-grow: 1;
|
@@ -16,14 +16,6 @@
|
|
16
16
|
padding: 0 12px 16px;
|
17
17
|
}
|
18
18
|
|
19
|
-
&__loader-container {
|
20
|
-
display: flex;
|
21
|
-
justify-content: center;
|
22
|
-
align-items: center;
|
23
|
-
|
24
|
-
height: 100%;
|
25
|
-
}
|
26
|
-
|
27
19
|
&__owner-container {
|
28
20
|
position: sticky;
|
29
21
|
z-index: 2;
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import {useDispatch} from 'react-redux';
|
2
|
+
import {useEffect} from 'react';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
|
5
|
+
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
6
|
+
|
7
|
+
import type {TACE} from '../../../types/api/acl';
|
8
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
|
9
|
+
import {useTypedSelector} from '../../../utils/hooks';
|
10
|
+
import {getSchemaAcl, setAclWasNotLoaded} from '../../../store/reducers/schemaAcl/schemaAcl';
|
11
|
+
|
12
|
+
import {ResponseError} from '../../../components/Errors/ResponseError';
|
13
|
+
import {Loader} from '../../../components/Loader';
|
14
|
+
|
15
|
+
import './Acl.scss';
|
16
|
+
import i18n from '../i18n';
|
17
|
+
|
18
|
+
const b = cn('ydb-acl');
|
19
|
+
|
20
|
+
const TABLE_SETTINGS = {
|
21
|
+
...DEFAULT_TABLE_SETTINGS,
|
22
|
+
dynamicRender: false,
|
23
|
+
stickyTop: 36,
|
24
|
+
};
|
25
|
+
|
26
|
+
const prepareLogin = (value: string | undefined) => {
|
27
|
+
if (value && value.endsWith('@staff') && !value.startsWith('svc_')) {
|
28
|
+
const login = value.split('@')[0];
|
29
|
+
return login;
|
30
|
+
}
|
31
|
+
|
32
|
+
return value;
|
33
|
+
};
|
34
|
+
|
35
|
+
const columns: Column<TACE>[] = [
|
36
|
+
{
|
37
|
+
name: 'AccessType',
|
38
|
+
header: 'Access Type',
|
39
|
+
sortable: false,
|
40
|
+
render: ({row}) => row.AccessType,
|
41
|
+
},
|
42
|
+
{
|
43
|
+
name: 'AccessRights',
|
44
|
+
header: 'Access Rights',
|
45
|
+
render: ({row}) => {
|
46
|
+
return row.AccessRights?.map((item, index) => {
|
47
|
+
return <div key={index}>{item}</div>;
|
48
|
+
});
|
49
|
+
},
|
50
|
+
sortable: false,
|
51
|
+
},
|
52
|
+
{
|
53
|
+
name: 'Subject',
|
54
|
+
sortable: false,
|
55
|
+
render: ({row}) => {
|
56
|
+
return prepareLogin(row.Subject);
|
57
|
+
},
|
58
|
+
width: 140,
|
59
|
+
},
|
60
|
+
{
|
61
|
+
name: 'InheritanceType',
|
62
|
+
header: 'Inheritance Type',
|
63
|
+
render: ({row}) => {
|
64
|
+
return row.InheritanceType?.map((item, index) => {
|
65
|
+
return <div key={index}>{item}</div>;
|
66
|
+
});
|
67
|
+
},
|
68
|
+
sortable: false,
|
69
|
+
},
|
70
|
+
];
|
71
|
+
|
72
|
+
export const Acl = () => {
|
73
|
+
const dispatch = useDispatch();
|
74
|
+
|
75
|
+
const {currentSchemaPath} = useTypedSelector((state) => state.schema);
|
76
|
+
const {loading, error, acl, owner, wasLoaded} = useTypedSelector((state) => state.schemaAcl);
|
77
|
+
|
78
|
+
useEffect(() => {
|
79
|
+
if (currentSchemaPath) {
|
80
|
+
dispatch(getSchemaAcl({path: currentSchemaPath}));
|
81
|
+
}
|
82
|
+
|
83
|
+
return () => {
|
84
|
+
// Ensures correct acl on path change
|
85
|
+
dispatch(setAclWasNotLoaded());
|
86
|
+
};
|
87
|
+
}, [currentSchemaPath, dispatch]);
|
88
|
+
|
89
|
+
const renderTable = () => {
|
90
|
+
if (!acl || !acl.length) {
|
91
|
+
return null;
|
92
|
+
}
|
93
|
+
|
94
|
+
return (
|
95
|
+
<DataTable
|
96
|
+
theme="yandex-cloud"
|
97
|
+
columns={columns}
|
98
|
+
data={acl}
|
99
|
+
settings={TABLE_SETTINGS}
|
100
|
+
/>
|
101
|
+
);
|
102
|
+
};
|
103
|
+
|
104
|
+
const renderOwner = () => {
|
105
|
+
if (!owner) {
|
106
|
+
return null;
|
107
|
+
}
|
108
|
+
|
109
|
+
return (
|
110
|
+
<div className={b('owner-container')}>
|
111
|
+
<span className={b('owner-label')}>{`${i18n('acl.owner')}: `}</span>
|
112
|
+
{prepareLogin(owner)}
|
113
|
+
</div>
|
114
|
+
);
|
115
|
+
};
|
116
|
+
|
117
|
+
if (loading && !wasLoaded) {
|
118
|
+
return <Loader />;
|
119
|
+
}
|
120
|
+
|
121
|
+
if (error) {
|
122
|
+
return <ResponseError error={error} className={b('message-container')} />;
|
123
|
+
}
|
124
|
+
|
125
|
+
if (!loading && !acl && !owner) {
|
126
|
+
return <div className={b('message-container')}>{i18n('acl.empty')}</div>;
|
127
|
+
}
|
128
|
+
|
129
|
+
return (
|
130
|
+
<div className={b()}>
|
131
|
+
<div className={b('result')}>
|
132
|
+
{renderOwner()}
|
133
|
+
{renderTable()}
|
134
|
+
</div>
|
135
|
+
</div>
|
136
|
+
);
|
137
|
+
};
|
@@ -6,8 +6,8 @@ import JSONTree from 'react-json-inspector';
|
|
6
6
|
import 'react-json-inspector/json-inspector.css';
|
7
7
|
|
8
8
|
import {Loader} from '../../../../components/Loader';
|
9
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
9
10
|
|
10
|
-
import {prepareQueryError} from '../../../../utils/query';
|
11
11
|
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
12
12
|
import {
|
13
13
|
getDescribe,
|
@@ -86,7 +86,7 @@ const Describe = ({tenant, type}: IDescribeProps) => {
|
|
86
86
|
}
|
87
87
|
|
88
88
|
if (error) {
|
89
|
-
return <
|
89
|
+
return <ResponseError error={error} className={b('message-container')} />;
|
90
90
|
}
|
91
91
|
|
92
92
|
if (!loading && !preparedDescribeData) {
|
@@ -83,6 +83,9 @@ export const DIR_PAGES = [overview, topShards, nodes, describe];
|
|
83
83
|
export const CDC_STREAM_PAGES = [overview, consumers, partitions, nodes, describe];
|
84
84
|
export const TOPIC_PAGES = [overview, consumers, partitions, nodes, describe];
|
85
85
|
|
86
|
+
export const EXTERNAL_DATA_SOURCE_PAGES = [overview, describe];
|
87
|
+
export const EXTERNAL_TABLE_PAGES = [overview, describe];
|
88
|
+
|
86
89
|
// verbose mapping to guarantee correct tabs for new path types
|
87
90
|
// TS will error when a new type is added but not mapped here
|
88
91
|
const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
|
@@ -101,6 +104,9 @@ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
|
|
101
104
|
[EPathType.EPathTypeCdcStream]: CDC_STREAM_PAGES,
|
102
105
|
|
103
106
|
[EPathType.EPathTypePersQueueGroup]: TOPIC_PAGES,
|
107
|
+
|
108
|
+
[EPathType.EPathTypeExternalDataSource]: EXTERNAL_DATA_SOURCE_PAGES,
|
109
|
+
[EPathType.EPathTypeExternalTable]: EXTERNAL_TABLE_PAGES,
|
104
110
|
};
|
105
111
|
|
106
112
|
export const getPagesByType = (type?: EPathType) => (type && pathTypeToPages[type]) || DIR_PAGES;
|
@@ -5,10 +5,10 @@ import {Loader} from '@gravity-ui/uikit';
|
|
5
5
|
import DataTable from '@gravity-ui/react-data-table';
|
6
6
|
|
7
7
|
import {Icon} from '../../../../components/Icon';
|
8
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
8
9
|
|
9
10
|
import {AutoFetcher} from '../../../../utils/autofetcher';
|
10
11
|
import {getHotKeys, setHotKeysOptions} from '../../../../store/reducers/hotKeys';
|
11
|
-
import {prepareQueryError} from '../../../../utils/query';
|
12
12
|
|
13
13
|
import {isColumnEntityType, isTableType} from '../../utils/schema';
|
14
14
|
|
@@ -86,7 +86,7 @@ function HotKeys({
|
|
86
86
|
sortable: false,
|
87
87
|
align: DataTable.RIGHT,
|
88
88
|
},
|
89
|
-
...keyColumnsIds
|
89
|
+
...keyColumnsIds.map((col, index) => ({
|
90
90
|
name: col,
|
91
91
|
header: (
|
92
92
|
<div className={b('primary-key-column')}>
|
@@ -107,7 +107,7 @@ function HotKeys({
|
|
107
107
|
|
108
108
|
const renderContent = () => {
|
109
109
|
if (error) {
|
110
|
-
return
|
110
|
+
return <ResponseError error={error} />;
|
111
111
|
}
|
112
112
|
return data !== null ? (
|
113
113
|
<div className={b('table-content')}>
|
@@ -124,6 +124,8 @@ function Overview({type, tenantName}: OverviewProps) {
|
|
124
124
|
<ChangefeedInfo data={data} topic={additionalData?.[0]} />
|
125
125
|
),
|
126
126
|
[EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={data} />,
|
127
|
+
[EPathType.EPathTypeExternalTable]: undefined,
|
128
|
+
[EPathType.EPathTypeExternalDataSource]: undefined,
|
127
129
|
};
|
128
130
|
|
129
131
|
return (
|