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.
Files changed (80) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/components/QueryResultTable/Cell/Cell.tsx +8 -8
  3. package/dist/components/QueryResultTable/i18n/en.json +1 -1
  4. package/dist/components/QueryResultTable/i18n/ru.json +1 -1
  5. package/dist/components/ShortyString/ShortyString.tsx +3 -6
  6. package/dist/components/ShortyString/i18n/en.json +8 -8
  7. package/dist/components/ShortyString/i18n/ru.json +8 -8
  8. package/dist/components/SpeedMultiMeter/i18n/index.ts +0 -2
  9. package/dist/components/Stack/Stack.tsx +16 -16
  10. package/dist/components/TableSkeleton/TableSkeleton.tsx +3 -3
  11. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +7 -3
  12. package/dist/containers/Header/Header.tsx +2 -2
  13. package/dist/containers/Header/breadcrumbs.ts +2 -1
  14. package/dist/containers/Heatmap/Heatmap.tsx +4 -3
  15. package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +2 -8
  16. package/dist/containers/Nodes/Nodes.tsx +1 -1
  17. package/dist/containers/Storage/EmptyFilter/i18n/en.json +2 -2
  18. package/dist/containers/Storage/EmptyFilter/i18n/ru.json +2 -2
  19. package/dist/containers/Storage/StorageGroups/i18n/en.json +5 -5
  20. package/dist/containers/Storage/StorageGroups/i18n/ru.json +5 -5
  21. package/dist/containers/Storage/UsageFilter/i18n/en.json +3 -8
  22. package/dist/containers/Storage/UsageFilter/i18n/ru.json +3 -8
  23. package/dist/containers/Tablet/Tablet.tsx +2 -2
  24. package/dist/containers/Tenant/Acl/Acl.scss +1 -9
  25. package/dist/containers/Tenant/Acl/Acl.tsx +137 -0
  26. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -2
  27. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -0
  28. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  29. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +2 -0
  30. package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +2 -3
  31. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +0 -6
  32. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +95 -83
  33. package/dist/containers/Tenant/Query/Issues/Issues.tsx +27 -23
  34. package/dist/containers/Tenant/Query/Issues/models.ts +0 -11
  35. package/dist/containers/Tenant/Query/Preview/Preview.tsx +3 -3
  36. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
  37. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +99 -0
  38. package/dist/containers/Tenant/Tenant.tsx +1 -5
  39. package/dist/containers/Tenant/TenantPages.tsx +9 -14
  40. package/dist/containers/Tenant/i18n/en.json +11 -0
  41. package/dist/containers/Tenant/i18n/index.ts +11 -0
  42. package/dist/containers/Tenant/i18n/ru.json +11 -0
  43. package/dist/containers/Tenant/utils/schema.ts +24 -0
  44. package/dist/containers/Tenant/utils/schemaActions.ts +28 -24
  45. package/dist/containers/Tenants/Tenants.tsx +1 -4
  46. package/dist/services/api.ts +6 -7
  47. package/dist/store/index.js +1 -1
  48. package/dist/store/reducers/nodes/nodes.ts +14 -5
  49. package/dist/store/reducers/nodes/types.ts +22 -3
  50. package/dist/store/reducers/nodes/utils.ts +23 -10
  51. package/dist/store/reducers/preview.ts +6 -4
  52. package/dist/store/reducers/schemaAcl/schemaAcl.ts +17 -0
  53. package/dist/store/reducers/schemaAcl/types.ts +9 -7
  54. package/dist/store/reducers/tenant/constants.ts +6 -0
  55. package/dist/store/reducers/tenant/tenant.ts +15 -0
  56. package/dist/store/reducers/tenant/types.ts +18 -3
  57. package/dist/store/state-url-mapping.js +3 -0
  58. package/dist/types/api/cluster.ts +1 -1
  59. package/dist/types/api/compute.ts +11 -11
  60. package/dist/types/api/error.ts +2 -2
  61. package/dist/types/api/netInfo.ts +3 -3
  62. package/dist/types/api/nodes.ts +9 -8
  63. package/dist/types/api/query.ts +1 -1
  64. package/dist/types/api/schema/schema.ts +3 -0
  65. package/dist/types/api/schema/shared.ts +3 -3
  66. package/dist/types/api/schema/table.ts +22 -22
  67. package/dist/types/api/storage.ts +1 -1
  68. package/dist/types/assets.d.ts +1 -2
  69. package/dist/types/store/executeQuery.ts +2 -3
  70. package/dist/types/store/executeTopQueries.ts +8 -5
  71. package/dist/types/store/explainQuery.ts +4 -4
  72. package/dist/types/store/query.ts +4 -3
  73. package/dist/types/store/shardsWorkload.ts +8 -5
  74. package/dist/utils/constants.ts +4 -1
  75. package/dist/utils/error.ts +2 -3
  76. package/dist/utils/query.ts +3 -9
  77. package/dist/utils/tests/providers.tsx +6 -9
  78. package/package.json +6 -2
  79. package/dist/containers/Tenant/Acl/Acl.js +0 -153
  80. 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
- dispatch(hideTooltip());
23
- }, [dispatch]);
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
  });
@@ -1,3 +1,3 @@
1
1
  {
2
- "empty": "Table is empty"
2
+ "empty": "Table is empty"
3
3
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "empty": "Таблица пустая"
2
+ "empty": "Таблица пустая"
3
3
  }
@@ -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 = displayLength && !expanded
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
- "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
- ]
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
- "default_collapse_label": "Показать меньше",
3
- "default_expand_label": "Показать ещё",
4
- "chars_count": [
5
- " ({{count}} символ)",
6
- " ({{count}} символа)",
7
- " ({{count}} символов)",
8
- " ({{count}} символов)"
9
- ]
2
+ "default_collapse_label": "Показать меньше",
3
+ "default_expand_label": "Показать ещё",
4
+ "chars_count": [
5
+ " ({{count}} символ)",
6
+ " ({{count}} символа)",
7
+ " ({{count}} символов)",
8
+ " ({{count}} символов)"
9
+ ]
10
10
  }
@@ -9,5 +9,3 @@ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
9
  i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
10
 
11
11
  export default i18n.keyset(COMPONENT);
12
-
13
-
@@ -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.Children.map(children, (child, index) => {
18
- if (!React.isValidElement(child)) {
19
- return null;
20
- }
16
+ {React.Children.map(children, (child, index) => {
17
+ if (!React.isValidElement(child)) {
18
+ return null;
19
+ }
21
20
 
22
- return (
23
- <div
24
- className={b('layer')}
25
- style={{
21
+ return (
22
+ <div
23
+ className={b('layer')}
24
+ style={
25
+ {
26
26
  [LAYER_CSS_VAR]: index,
27
- } as React.CSSProperties}
28
- >
29
- {child}
30
- </div>
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 { FC } from 'react';
1
+ import {FC} from 'react';
2
2
  import block from 'bem-cn-lite';
3
- import { Skeleton } from '@gravity-ui/uikit';
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> = ({ rows = 2, className }) => (
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 {CLUSTER_INFO_HIDDEN_KEY, DEVELOPER_UI} from '../../../utils/constants';
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: DEVELOPER_UI, url: internalLink},
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 ?? 'Unknown cluster'}
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 {DEVELOPER_UI} from '../../utils/constants';
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={DEVELOPER_UI}
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 || 'Cluster',
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 type {IHeatmapMetricValue} from '../../types/store/heatmap';
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 <div>{prepareQueryError(error)}</div>;
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
- <span className={b('pdisk-title-item-label')}>
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,4 +1,4 @@
1
1
  {
2
- "default_message": "Everything is fine!",
3
- "default_button_label": "Show All"
2
+ "default_message": "Everything is fine!",
3
+ "default_button_label": "Show All"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
- "default_message": "Всё в порядке!",
3
- "default_button_label": "Показать все"
2
+ "default_message": "Всё в порядке!",
3
+ "default_button_label": "Показать все"
4
4
  }
@@ -1,7 +1,7 @@
1
1
  {
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"
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
- "empty.default": "Нет групп",
3
- "empty.out_of_space": "Нет групп, в которых кончается место",
4
- "empty.degraded": "Нет деградировавших групп",
5
- "show_all": "Показать все группы",
6
- "encrypted": "Зашифрованная группа"
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
- "label": "Usage:",
3
- "default_value": "Any",
4
- "groups_count": [
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
- "label": "Использование:",
3
- "default_value": "Любое",
4
- "groups_count": [
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 {DEVELOPER_UI} from '../../utils/constants';
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: `${DEVELOPER_UI} - tablet`,
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
- .kv-acl {
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 <div className={b('message-container', 'error')}>{prepareQueryError(error)}</div>;
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?.map((col, index) => ({
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 prepareQueryError(error);
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 (