{
const isFullData = isFullVDiskData(data);
+ const isViewerUser = useIsViewerUser();
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
@@ -247,7 +261,7 @@ export const VDiskPopup = ({data}: VDiskPopupProps) => {
const donors = data.Donors;
for (const donor of donors) {
donorsInfo.push({
- label: 'VDisk',
+ label: vDiskPopupKeyset('label_vdisk'),
value: {donor.StringifiedId},
});
}
@@ -257,7 +271,7 @@ export const VDiskPopup = ({data}: VDiskPopupProps) => {
{data.DonorMode && }
- {pdiskInfo && }
+ {pdiskInfo && isViewerUser && }
{donorsInfo.length > 0 && }
);
diff --git a/src/components/VDiskPopup/i18n/en.json b/src/components/VDiskPopup/i18n/en.json
new file mode 100644
index 0000000000..4d82fa9a17
--- /dev/null
+++ b/src/components/VDiskPopup/i18n/en.json
@@ -0,0 +1,21 @@
+{
+ "context_not-available": "not available",
+ "label_state": "State",
+ "label_storage-pool": "StoragePool",
+ "label_node-id": "NodeId",
+ "label_pdisk-id": "PDiskId",
+ "label_vslot-id": "VSlotId",
+ "label_links": "Links",
+ "label_vdisk": "VDisk",
+ "label_fresh": "Fresh",
+ "label_level": "Level",
+ "label_space": "Space",
+ "label_front-queues": "FrontQueues",
+ "label_replicated": "Replicated",
+ "label_progress": "Progress",
+ "label_remaining": "Remaining",
+ "label_unsync-vdisks": "UnsyncVDisks",
+ "label_allocated": "Allocated",
+ "label_read": "Read",
+ "label_write": "Write"
+}
diff --git a/src/components/VDiskPopup/i18n/index.ts b/src/components/VDiskPopup/i18n/index.ts
new file mode 100644
index 0000000000..d68cbc6df9
--- /dev/null
+++ b/src/components/VDiskPopup/i18n/index.ts
@@ -0,0 +1,7 @@
+import {registerKeysets} from '../../../utils/i18n';
+
+import en from './en.json';
+
+const COMPONENT = 'ydb-vDisk-popup';
+
+export const vDiskPopupKeyset = registerKeysets(COMPONENT, {en});
diff --git a/src/components/nodesColumns/constants.ts b/src/components/nodesColumns/constants.ts
index 86e67f30e0..7416714baa 100644
--- a/src/components/nodesColumns/constants.ts
+++ b/src/components/nodesColumns/constants.ts
@@ -43,6 +43,12 @@ export function isMonitoringUserNodesColumn(columnId: string): boolean {
return MONITORING_USER_COLUMNS_IDS.includes(columnId as NodesColumnId);
}
+// Columns, that should displayed only for users with isViewerAllowed:true
+const VIEWER_USER_COLUMNS_IDS: NodesColumnId[] = ['LoadAverage'];
+export function isViewerUserNodesColumn(columnId: string): boolean {
+ return VIEWER_USER_COLUMNS_IDS.some((el) => el === columnId);
+}
+
// This code is running when module is initialized and correct language may not be set yet
// get functions guarantee that i18n fields will be inited on render with current render language
export const NODES_COLUMNS_TITLES = {
diff --git a/src/containers/Nodes/Nodes.tsx b/src/containers/Nodes/Nodes.tsx
index 6b0fb97776..8751e76378 100644
--- a/src/containers/Nodes/Nodes.tsx
+++ b/src/containers/Nodes/Nodes.tsx
@@ -1,12 +1,18 @@
import React from 'react';
import type {Column} from '../../components/PaginatedTable';
-import {isMonitoringUserNodesColumn} from '../../components/nodesColumns/constants';
+import {
+ isMonitoringUserNodesColumn,
+ isViewerUserNodesColumn,
+} from '../../components/nodesColumns/constants';
import type {NodesColumnId} from '../../components/nodesColumns/constants';
import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
import type {AdditionalNodesProps} from '../../types/additionalProps';
import type {NodesGroupByField} from '../../types/api/nodes';
-import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
+import {
+ useIsUserAllowedToMakeChanges,
+ useIsViewerUser,
+} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
import {PaginatedNodes} from './PaginatedNodes';
import {getNodesColumns} from './columns/columns';
@@ -45,13 +51,20 @@ export function Nodes({
groupByParams = ALL_NODES_GROUP_BY_PARAMS,
}: NodesProps) {
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
+ const isViewerUser = useIsViewerUser();
const preparedColumns = React.useMemo(() => {
if (isUserAllowedToMakeChanges) {
return columns;
}
- return columns.filter((column) => !isMonitoringUserNodesColumn(column.name));
- }, [columns, isUserAllowedToMakeChanges]);
+ const filteredColumns = columns.filter(
+ (column) => !isMonitoringUserNodesColumn(column.name),
+ );
+ if (isViewerUser) {
+ return filteredColumns;
+ }
+ return filteredColumns.filter((column) => !isViewerUserNodesColumn(column.name));
+ }, [columns, isUserAllowedToMakeChanges, isViewerUser]);
return (
{
handleGroupByParamChange(value[0]);
diff --git a/src/containers/Nodes/useNodesPageQueryParams.ts b/src/containers/Nodes/useNodesPageQueryParams.ts
index b06f903998..158810b4ff 100644
--- a/src/containers/Nodes/useNodesPageQueryParams.ts
+++ b/src/containers/Nodes/useNodesPageQueryParams.ts
@@ -2,6 +2,7 @@ import {StringParam, useQueryParams} from 'use-query-params';
import {useViewerNodesHandlerHasGroupingBySystemState} from '../../store/reducers/capabilities/hooks';
import type {NodesGroupByField, NodesPeerRole} from '../../types/api/nodes';
+import {useIsViewerUser} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
import type {NodesUptimeFilterValues} from '../../utils/nodes';
import {nodesUptimeFilterValuesSchema} from '../../utils/nodes';
@@ -16,9 +17,13 @@ export function useNodesPageQueryParams(groupByParams: NodesGroupByField[] | und
nodesGroupBy: StringParam,
});
+ const isViewerUser = useIsViewerUser();
+
const uptimeFilter = nodesUptimeFilterValuesSchema.parse(queryParams.uptimeFilter);
const searchValue = queryParams.search ?? '';
- const peerRoleFilter = parseNodesPeerRoleFilter(queryParams.peerRole);
+ const peerRoleFilter = isViewerUser
+ ? parseNodesPeerRoleFilter(queryParams.peerRole)
+ : 'database';
const systemStateGroupingAvailable = useViewerNodesHandlerHasGroupingBySystemState();
const groupByParam = parseNodesGroupByParam(
diff --git a/src/containers/Storage/PaginatedStorageGroupsTable/columns/constants.ts b/src/containers/Storage/PaginatedStorageGroupsTable/columns/constants.ts
index ac9a7c3546..8125fee8d4 100644
--- a/src/containers/Storage/PaginatedStorageGroupsTable/columns/constants.ts
+++ b/src/containers/Storage/PaginatedStorageGroupsTable/columns/constants.ts
@@ -55,6 +55,12 @@ export const DEFAULT_STORAGE_GROUPS_COLUMNS: StorageGroupsColumnId[] = [
'VDisks',
];
+// Columns, that should displayed only for users with isViewerAllowed:true
+const VIEWER_USER_COLUMNS_IDS: StorageGroupsColumnId[] = ['DiskSpace'];
+export function isViewerGroupsColumn(columnId: string): boolean {
+ return VIEWER_USER_COLUMNS_IDS.some((el) => el === columnId);
+}
+
export const REQUIRED_STORAGE_GROUPS_COLUMNS: StorageGroupsColumnId[] = ['GroupId'];
// This code is running when module is initialized and correct language may not be set yet
diff --git a/src/containers/Storage/PaginatedStorageGroupsTable/columns/hooks.ts b/src/containers/Storage/PaginatedStorageGroupsTable/columns/hooks.ts
index bcb422ba30..cbc95dce41 100644
--- a/src/containers/Storage/PaginatedStorageGroupsTable/columns/hooks.ts
+++ b/src/containers/Storage/PaginatedStorageGroupsTable/columns/hooks.ts
@@ -1,7 +1,10 @@
import React from 'react';
import {VISIBLE_ENTITIES} from '../../../../store/reducers/storage/constants';
-import {useIsUserAllowedToMakeChanges} from '../../../../utils/hooks/useIsUserAllowedToMakeChanges';
+import {
+ useIsUserAllowedToMakeChanges,
+ useIsViewerUser,
+} from '../../../../utils/hooks/useIsUserAllowedToMakeChanges';
import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns';
import {getStorageGroupsColumns} from './columns';
@@ -12,6 +15,7 @@ import {
STORAGE_GROUPS_COLUMNS_TITLES,
STORAGE_GROUPS_SELECTED_COLUMNS_LS_KEY,
isMonitoringUserGroupsColumn,
+ isViewerGroupsColumn,
} from './constants';
import type {GetStorageGroupsColumnsParams} from './types';
@@ -20,6 +24,7 @@ export function useStorageGroupsSelectedColumns({
viewContext,
}: GetStorageGroupsColumnsParams) {
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
+ const isViewerUser = useIsViewerUser();
const columns = React.useMemo(() => {
const allColumns = getStorageGroupsColumns({viewContext});
@@ -27,8 +32,14 @@ export function useStorageGroupsSelectedColumns({
if (isUserAllowedToMakeChanges) {
return allColumns;
}
- return allColumns.filter((column) => !isMonitoringUserGroupsColumn(column.name));
- }, [isUserAllowedToMakeChanges, viewContext]);
+ const filteredColumns = allColumns.filter(
+ (column) => !isMonitoringUserGroupsColumn(column.name),
+ );
+ if (isViewerUser) {
+ return filteredColumns;
+ }
+ return filteredColumns.filter((column) => !isViewerGroupsColumn(column.name));
+ }, [isUserAllowedToMakeChanges, viewContext, isViewerUser]);
const requiredColumns = React.useMemo(() => {
if (visibleEntities === VISIBLE_ENTITIES.missing) {
diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx
index 55a25031fa..3a3d4e5e93 100644
--- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx
+++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx
@@ -10,13 +10,13 @@ import {
useFeatureFlagsAvailable,
useTopicDataAvailable,
} from '../../../store/reducers/capabilities/hooks';
-import {useClusterBaseInfo} from '../../../store/reducers/cluster/cluster';
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
-import {setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
+import {setDiagnosticsTab, useTenantBaseInfo} from '../../../store/reducers/tenant/tenant';
import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../types/additionalProps';
import {uiFactory} from '../../../uiFactory/uiFactory';
import {cn} from '../../../utils/cn';
import {useScrollPosition, useTypedDispatch, useTypedSelector} from '../../../utils/hooks';
+import {useIsViewerUser} from '../../../utils/hooks/useIsUserAllowedToMakeChanges';
import {Heatmap} from '../../Heatmap';
import {Nodes} from '../../Nodes/Nodes';
import {Operations} from '../../Operations';
@@ -50,7 +50,7 @@ const b = cn('kv-tenant-diagnostics');
function Diagnostics(props: DiagnosticsProps) {
const {path, database, type, subType} = useCurrentSchema();
- const {control_plane: controlPlane} = useClusterBaseInfo();
+ const {controlPlane} = useTenantBaseInfo(path);
const containerRef = React.useRef(null);
const dispatch = useTypedDispatch();
const {diagnosticsTab = TENANT_DIAGNOSTICS_TABS_IDS.overview} = useTypedSelector(
@@ -63,11 +63,13 @@ function Diagnostics(props: DiagnosticsProps) {
const hasFeatureFlags = useFeatureFlagsAvailable();
const hasTopicData = useTopicDataAvailable();
+ const isViewerUser = useIsViewerUser();
const pages = getPagesByType(type, subType, {
hasFeatureFlags,
hasTopicData,
isTopLevel: path === database,
hasBackups: typeof uiFactory.renderBackups === 'function' && Boolean(controlPlane),
+ hasConfigs: isViewerUser,
});
let activeTab = pages.find((el) => el.id === diagnosticsTab);
if (!activeTab) {
diff --git a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts
index 673c0c2321..6ffef9c123 100644
--- a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts
+++ b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts
@@ -176,6 +176,7 @@ export const getPagesByType = (
hasTopicData?: boolean;
isTopLevel?: boolean;
hasBackups?: boolean;
+ hasConfigs?: boolean;
},
) => {
const subTypePages = subType ? pathSubTypeToPages[subType] : undefined;
@@ -183,16 +184,19 @@ export const getPagesByType = (
let pages = subTypePages || typePages || DIR_PAGES;
if (isTopicEntityType(type) && !options?.hasTopicData) {
- return pages?.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData);
+ pages = pages?.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData);
}
if (isDatabaseEntityType(type) || options?.isTopLevel) {
pages = DATABASE_PAGES;
if (!options?.hasFeatureFlags) {
- return pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
+ pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
}
}
if (!options?.hasBackups) {
- return pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.backups);
+ pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.backups);
+ }
+ if (!options?.hasConfigs) {
+ pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs);
}
return pages;
};
diff --git a/src/store/reducers/cluster/cluster.ts b/src/store/reducers/cluster/cluster.ts
index 403e883cbb..0484e84f33 100644
--- a/src/store/reducers/cluster/cluster.ts
+++ b/src/store/reducers/cluster/cluster.ts
@@ -10,6 +10,7 @@ import type {TClusterInfo} from '../../../types/api/cluster';
import type {TTabletStateInfo} from '../../../types/api/tablet';
import {CLUSTER_DEFAULT_TITLE, DEFAULT_CLUSTER_TAB_KEY} from '../../../utils/constants';
import {useClusterNameFromQuery} from '../../../utils/hooks/useDatabaseFromQuery';
+import {useIsViewerUser} from '../../../utils/hooks/useIsUserAllowedToMakeChanges';
import {isQueryErrorResponse} from '../../../utils/query';
import type {RootState} from '../../defaultStore';
import {api} from '../api';
@@ -139,8 +140,11 @@ export const clusterApi = api.injectEndpoints({
export function useClusterBaseInfo() {
const clusterNameFromQuery = useClusterNameFromQuery();
+ const isViewerUser = useIsViewerUser();
- const {currentData} = clusterApi.useGetClusterBaseInfoQuery(clusterNameFromQuery ?? skipToken);
+ const {currentData} = clusterApi.useGetClusterBaseInfoQuery(clusterNameFromQuery ?? skipToken, {
+ skip: !isViewerUser,
+ });
const {solomon: monitoring, name, title, trace_view: traceView, ...data} = currentData || {};
diff --git a/src/store/reducers/tenant/tenant.ts b/src/store/reducers/tenant/tenant.ts
index e01b02d51f..0dac9732b0 100644
--- a/src/store/reducers/tenant/tenant.ts
+++ b/src/store/reducers/tenant/tenant.ts
@@ -4,6 +4,7 @@ import type {PayloadAction} from '@reduxjs/toolkit';
import {DEFAULT_USER_SETTINGS, settingsManager} from '../../../services/settings';
import type {TTenantInfo} from '../../../types/api/tenant';
import {TENANT_INITIAL_PAGE_KEY} from '../../../utils/constants';
+import {useClusterNameFromQuery} from '../../../utils/hooks/useDatabaseFromQuery';
import {api} from '../api';
import {TENANT_DIAGNOSTICS_TABS_IDS, TENANT_METRICS_TABS_IDS} from './constants';
@@ -100,3 +101,18 @@ export const tenantApi = api.injectEndpoints({
}),
overrideExisting: 'throw',
});
+
+export function useTenantBaseInfo(path: string) {
+ const clusterNameFromQuery = useClusterNameFromQuery();
+
+ const {currentData} = tenantApi.useGetTenantInfoQuery({
+ path,
+ clusterName: clusterNameFromQuery,
+ });
+
+ const {ControlPlane} = currentData || {};
+
+ return {
+ controlPlane: ControlPlane,
+ };
+}