diff --git a/src/components/LogsButton/LogsButton.tsx b/src/components/LogsButton/LogsButton.tsx deleted file mode 100644 index e206c74b6..000000000 --- a/src/components/LogsButton/LogsButton.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import {FileText} from '@gravity-ui/icons'; -import type {ButtonSize} from '@gravity-ui/uikit'; -import {Button, Icon} from '@gravity-ui/uikit'; - -interface LogsButtonProps { - className?: string; - href: string; - size?: ButtonSize; -} - -export function LogsButton({href, className, size = 'xs'}: LogsButtonProps) { - return ( - - ); -} diff --git a/src/components/MonitoringButton/MonitoringButton.tsx b/src/components/MonitoringButton/MonitoringButton.tsx deleted file mode 100644 index 2c57b5dd1..000000000 --- a/src/components/MonitoringButton/MonitoringButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type {ButtonSize} from '@gravity-ui/uikit'; -import {Button, Icon} from '@gravity-ui/uikit'; - -import monitoringIcon from '../../assets/icons/monitoring.svg'; - -interface MonitoringButtonProps { - className?: string; - href: string; - size?: ButtonSize; -} - -export function MonitoringButton({href, className, size = 'xs'}: MonitoringButtonProps) { - return ( - - ); -} diff --git a/src/components/TenantNameWrapper/TenantNameWrapper.tsx b/src/components/TenantNameWrapper/TenantNameWrapper.tsx index 78afc779f..44e0297d3 100644 --- a/src/components/TenantNameWrapper/TenantNameWrapper.tsx +++ b/src/components/TenantNameWrapper/TenantNameWrapper.tsx @@ -3,6 +3,7 @@ import {DefinitionList, Flex} from '@gravity-ui/uikit'; import {getTenantPath} from '../../containers/Tenant/TenantPages'; import type {PreparedTenant} from '../../store/reducers/tenants/types'; import type {AdditionalTenantsProps} from '../../types/additionalProps'; +import {getDatabaseLinks} from '../../utils/additionalProps'; import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges'; import {EntityStatus} from '../EntityStatus/EntityStatus'; import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon'; @@ -37,23 +38,16 @@ export function TenantNameWrapper({tenant, additionalTenantsProps}: TenantNameWr const backend = getTenantBackend(tenant, additionalTenantsProps); const isExternalLink = Boolean(backend); - const monitoringLink = additionalTenantsProps?.getMonitoringLink?.(tenant.Name, tenant.Type); - const logsLink = additionalTenantsProps?.getLogsLink?.(tenant.Name); + const links = getDatabaseLinks(additionalTenantsProps, tenant?.Name, tenant?.Type); const infoPopoverContent = - (monitoringLink || logsLink) && isUserAllowedToMakeChanges ? ( + links.length > 0 && isUserAllowedToMakeChanges ? ( - {monitoringLink && ( - - )} - {logsLink && ( - - )} + {links.map(({title, url}) => ( + + ))} diff --git a/src/containers/AppWithClusters/AppWithClusters.tsx b/src/containers/AppWithClusters/AppWithClusters.tsx index a388eba3c..c1442b554 100644 --- a/src/containers/AppWithClusters/AppWithClusters.tsx +++ b/src/containers/AppWithClusters/AppWithClusters.tsx @@ -28,6 +28,8 @@ export function AppWithClusters({store, history, userSettings, children}: AppWit getLogsLink={uiFactory.getLogsLink} getMonitoringLink={uiFactory.getMonitoringLink} getMonitoringClusterLink={uiFactory.getMonitoringClusterLink} + getDatabaseLinks={uiFactory.getDatabaseLinks} + getClusterLinks={uiFactory.getClusterLinks} /> ); }} @@ -39,6 +41,7 @@ export function AppWithClusters({store, history, userSettings, children}: AppWit component={component} getLogsLink={uiFactory.getLogsLink} getMonitoringLink={uiFactory.getMonitoringLink} + getDatabaseLinks={uiFactory.getDatabaseLinks} /> ); }} diff --git a/src/containers/AppWithClusters/ExtendedCluster/ExtendedCluster.tsx b/src/containers/AppWithClusters/ExtendedCluster/ExtendedCluster.tsx index 7f6e2f0b6..ab924de72 100644 --- a/src/containers/AppWithClusters/ExtendedCluster/ExtendedCluster.tsx +++ b/src/containers/AppWithClusters/ExtendedCluster/ExtendedCluster.tsx @@ -1,18 +1,15 @@ import {ClipboardButton} from '@gravity-ui/uikit'; -import {isNil} from 'lodash'; import {useClusterBaseInfo} from '../../../store/reducers/cluster/cluster'; -import type {AdditionalClusterProps, AdditionalTenantsProps} from '../../../types/additionalProps'; -import type {ETenantType} from '../../../types/api/tenant'; +import type {AdditionalClusterProps} from '../../../types/additionalProps'; +import type {GetClusterLinks, GetDatabaseLinks} from '../../../uiFactory/types'; import {cn} from '../../../utils/cn'; -import {USE_CLUSTER_BALANCER_AS_BACKEND_KEY} from '../../../utils/constants'; -import {useSetting} from '../../../utils/hooks'; import {useAdditionalNodesProps} from '../../../utils/hooks/useAdditionalNodesProps'; import type {GetLogsLink} from '../../../utils/logs'; import type {GetMonitoringClusterLink, GetMonitoringLink} from '../../../utils/monitoring'; -import {getCleanBalancerValue, prepareBackendFromBalancer} from '../../../utils/parseBalancer'; -import {getBackendFromBalancerAndNodeId} from '../../../utils/prepareBackend'; +import {getCleanBalancerValue} from '../../../utils/parseBalancer'; import type {Cluster} from '../../Cluster/Cluster'; +import {useAdditionalTenantsProps} from '../utils/useAdditionalTenantsProps'; import './ExtendedCluster.scss'; @@ -33,28 +30,32 @@ const getAdditionalBalancerInfo = (balancer: string) => { }; interface GetAdditionalClusterProps { - clusterName: string | undefined; - monitoring: string | undefined; - balancer: string | undefined; getMonitoringClusterLink?: GetMonitoringClusterLink; + getClusterLinks?: GetClusterLinks; } -const getAdditionalClusterProps = ({ - clusterName, - monitoring, - balancer, +const useAdditionalClusterProps = ({ getMonitoringClusterLink, + getClusterLinks, }: GetAdditionalClusterProps) => { - const additionalClusterProps: AdditionalClusterProps = {}; + const clusterInfo = useClusterBaseInfo(); + const {name: clusterName, balancer, monitoring} = clusterInfo; + const additionalClusterProps: AdditionalClusterProps = {}; + additionalClusterProps.links = []; if (monitoring && getMonitoringClusterLink) { const clusterLink = getMonitoringClusterLink(monitoring, clusterName); if (clusterLink) { - additionalClusterProps.links = [{title: 'Monitoring', url: clusterLink}]; + additionalClusterProps.links.push({title: 'Monitoring', url: clusterLink}); } } + if (getClusterLinks) { + const clusterLinks = getClusterLinks({clusterInfo}); + additionalClusterProps.links.push(...clusterLinks); + } + if (balancer) { additionalClusterProps.info = [getAdditionalBalancerInfo(balancer)]; } @@ -62,104 +63,35 @@ const getAdditionalClusterProps = ({ return additionalClusterProps; }; -interface GetAdditionalTenantsProps { - clusterName: string | undefined; - monitoring: string | undefined; - balancer: string | undefined; - logging: string | undefined; - useClusterBalancerAsBackend: boolean | undefined; - getMonitoringLink?: GetMonitoringLink; - getLogsLink?: GetLogsLink; -} - -const getAdditionalTenantsProps = ({ - clusterName, - monitoring, - balancer, - logging, - useClusterBalancerAsBackend, - getMonitoringLink, - getLogsLink, -}: GetAdditionalTenantsProps) => { - const additionalTenantsProps: AdditionalTenantsProps = {}; - - additionalTenantsProps.prepareTenantBackend = (nodeId) => { - // Balancer value is used to create path, so it's necessary - if (!balancer) { - return undefined; - } - - if (useClusterBalancerAsBackend) { - return prepareBackendFromBalancer(balancer); - } - - if (isNil(nodeId)) { - return undefined; - } - - return getBackendFromBalancerAndNodeId(nodeId, balancer) ?? undefined; - }; - - if (monitoring && getMonitoringLink) { - additionalTenantsProps.getMonitoringLink = (dbName?: string, dbType?: ETenantType) => { - if (dbName && dbType) { - return getMonitoringLink({monitoring, dbName, dbType, clusterName}); - } - - return null; - }; - } - - if (logging && getLogsLink) { - additionalTenantsProps.getLogsLink = (dbName?: string) => { - if (dbName) { - return getLogsLink({ - dbName, - logging, - }); - } - - return null; - }; - } - - return additionalTenantsProps; -}; - interface ExtendedClusterProps { component: typeof Cluster; getMonitoringLink?: GetMonitoringLink; getMonitoringClusterLink?: GetMonitoringClusterLink; getLogsLink?: GetLogsLink; + getDatabaseLinks?: GetDatabaseLinks; + getClusterLinks?: GetClusterLinks; } export function ExtendedCluster({ component: ClusterComponent, getMonitoringLink, getMonitoringClusterLink, getLogsLink, + getDatabaseLinks, + getClusterLinks, }: ExtendedClusterProps) { const additionalNodesProps = useAdditionalNodesProps(); - const {name, balancer, monitoring, logging} = useClusterBaseInfo(); - - const [useClusterBalancerAsBackend] = useSetting(USE_CLUSTER_BALANCER_AS_BACKEND_KEY); return (
diff --git a/src/containers/AppWithClusters/ExtendedTenant/ExtendedTenant.tsx b/src/containers/AppWithClusters/ExtendedTenant/ExtendedTenant.tsx index c96aea58f..bf8dda86c 100644 --- a/src/containers/AppWithClusters/ExtendedTenant/ExtendedTenant.tsx +++ b/src/containers/AppWithClusters/ExtendedTenant/ExtendedTenant.tsx @@ -1,47 +1,29 @@ -import {useClusterBaseInfo} from '../../../store/reducers/cluster/cluster'; -import type {ETenantType} from '../../../types/api/tenant'; +import type {GetDatabaseLinks} from '../../../uiFactory/types'; import {useAdditionalNodesProps} from '../../../utils/hooks/useAdditionalNodesProps'; import type {GetLogsLink} from '../../../utils/logs'; import type {GetMonitoringLink} from '../../../utils/monitoring'; import type {Tenant} from '../../Tenant/Tenant'; +import {useAdditionalTenantsProps} from '../utils/useAdditionalTenantsProps'; export interface ExtendedTenantProps { component: typeof Tenant; getMonitoringLink?: GetMonitoringLink; getLogsLink?: GetLogsLink; + getDatabaseLinks?: GetDatabaseLinks; } export function ExtendedTenant({ component: TenantComponent, getMonitoringLink, getLogsLink, + getDatabaseLinks, }: ExtendedTenantProps) { - const {monitoring, logging} = useClusterBaseInfo(); const additionalNodesProps = useAdditionalNodesProps(); - - const additionalTenantProps = { - getMonitoringLink: (dbName?: string, dbType?: ETenantType) => { - if (monitoring && dbName && dbType && getMonitoringLink) { - return getMonitoringLink({ - monitoring, - dbName, - dbType, - }); - } - - return null; - }, - getLogsLink: (dbName?: string) => { - if (logging && dbName && getLogsLink) { - return getLogsLink({ - dbName, - logging, - }); - } - - return null; - }, - }; + const additionalTenantProps = useAdditionalTenantsProps({ + getMonitoringLink, + getLogsLink, + getDatabaseLinks, + }); return ( (USE_CLUSTER_BALANCER_AS_BACKEND_KEY); + + const {balancer, monitoring, logging, name: clusterName} = clusterInfo; + + const additionalTenantsProps: AdditionalTenantsProps = {}; + additionalTenantsProps.prepareTenantBackend = (nodeId) => { + // Balancer value is used to create path, so it's necessary + if (!balancer) { + return undefined; + } + + if (useClusterBalancerAsBackend) { + return prepareBackendFromBalancer(balancer); + } + + if (isNil(nodeId)) { + return undefined; + } + + return getBackendFromBalancerAndNodeId(nodeId, balancer) ?? undefined; + }; + + if (monitoring && getMonitoringLink) { + additionalTenantsProps.getMonitoringLink = (dbName?: string, dbType?: ETenantType) => { + if (dbName && dbType) { + return getMonitoringLink({monitoring, clusterName, dbName, dbType}); + } + + return null; + }; + } + + if (logging && getLogsLink) { + additionalTenantsProps.getLogsLink = (dbName?: string) => { + if (dbName) { + return getLogsLink({logging, dbName}); + } + + return null; + }; + } + + if (getDatabaseLinks) { + additionalTenantsProps.getLinks = (dbName?: string, dbType?: ETenantType) => { + if (dbName && dbType) { + return getDatabaseLinks({clusterInfo, dbName, dbType}); + } + return []; + }; + } + + return additionalTenantsProps; +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx index 192e22e3a..91b2347eb 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx @@ -1,14 +1,13 @@ -import {Flex} from '@gravity-ui/uikit'; +import {Button, Flex, Icon} from '@gravity-ui/uikit'; import {EntityStatus} from '../../../../components/EntityStatus/EntityStatus'; import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper'; -import {LogsButton} from '../../../../components/LogsButton/LogsButton'; -import {MonitoringButton} from '../../../../components/MonitoringButton/MonitoringButton'; import {overviewApi} from '../../../../store/reducers/overview/overview'; import {TENANT_METRICS_TABS_IDS} from '../../../../store/reducers/tenant/constants'; import {tenantApi} from '../../../../store/reducers/tenant/tenant'; import {calculateTenantMetrics} from '../../../../store/reducers/tenants/utils'; import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../../types/additionalProps'; +import {getDatabaseLinks} from '../../../../utils/additionalProps'; import {TENANT_DEFAULT_TITLE} from '../../../../utils/constants'; import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks'; import {useClusterNameFromQuery} from '../../../../utils/hooks/useDatabaseFromQuery'; @@ -141,8 +140,7 @@ export function TenantOverview({ } }; - const monitoringLink = additionalTenantProps?.getMonitoringLink?.(Name, Type); - const logsLink = additionalTenantProps?.getLogsLink?.(Name); + const links = getDatabaseLinks(additionalTenantProps, Name, Type); return ( @@ -152,8 +150,17 @@ export function TenantOverview({ {renderName()} - {monitoringLink && } - {logsLink && } + {links.map(({title, url, icon}) => ( + + ))} diff --git a/src/lib.ts b/src/lib.ts index e76d3fb2c..5e1013c8d 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -33,3 +33,5 @@ export type { export type {SettingProps, SettingsInfoFieldProps} from './containers/UserSettings/Setting'; export type {AsideNavigationProps} from './containers/AsideNavigation/AsideNavigation'; export type {GetMonitoringLink, GetMonitoringClusterLink} from './utils/monitoring'; + +export {configureUIFactory} from './uiFactory/uiFactory'; diff --git a/src/services/parsers/parseMetaCluster.ts b/src/services/parsers/parseMetaCluster.ts index 88561be50..2094af6e3 100644 --- a/src/services/parsers/parseMetaCluster.ts +++ b/src/services/parsers/parseMetaCluster.ts @@ -1,6 +1,9 @@ +import type {TTraceView} from '../../types/api/trace'; import {traceViewSchema} from '../../types/api/trace'; -export function parseTraceFields({traceView}: {traceView?: string}) { +export function parseTraceFields({traceView}: {traceView?: string}): { + traceView?: TTraceView; +} { try { return { traceView: traceView ? traceViewSchema.parse(JSON.parse(traceView)) : undefined, diff --git a/src/store/reducers/cluster/cluster.ts b/src/store/reducers/cluster/cluster.ts index a8a378ed7..faa76f42d 100644 --- a/src/store/reducers/cluster/cluster.ts +++ b/src/store/reducers/cluster/cluster.ts @@ -158,6 +158,8 @@ export function useClusterBaseInfo() { }; } +export type ClusterInfo = ReturnType & Record; + const createClusterInfoSelector = createSelector( (clusterName?: string) => clusterName, (clusterName) => clusterApi.endpoints.getClusterInfo.select(clusterName), diff --git a/src/store/reducers/tenant/tenant.ts b/src/store/reducers/tenant/tenant.ts index 56460b321..23638ce3e 100644 --- a/src/store/reducers/tenant/tenant.ts +++ b/src/store/reducers/tenant/tenant.ts @@ -67,7 +67,11 @@ export const tenantApi = api.injectEndpoints({ } else { tenantData = await window.api.viewer.getTenantInfo({path}, {signal}); } - return {data: tenantData.TenantInfo?.[0] ?? null}; + const databases = tenantData.TenantInfo || []; + // previous meta versions do not support filtering databases by name + const data = + databases.find((tenant) => tenant.Name === path) ?? databases[0] ?? null; + return {data}; } catch (error) { return {error}; } diff --git a/src/types/additionalProps.ts b/src/types/additionalProps.ts index 600d2cd3d..a34ffd516 100644 --- a/src/types/additionalProps.ts +++ b/src/types/additionalProps.ts @@ -1,3 +1,5 @@ +import type {IconData} from '@gravity-ui/uikit'; + import type {TSystemStateInfo} from './api/nodes'; import type {ETenantType} from './api/tenant'; import type {InfoItem} from './components'; @@ -7,6 +9,12 @@ export interface ClusterLink { url: string; } +export interface DatabaseLink { + title: string; + url: string; + icon: IconData; +} + export interface AdditionalClusterProps { info?: InfoItem[]; links?: ClusterLink[]; @@ -16,6 +24,7 @@ export interface AdditionalTenantsProps { prepareTenantBackend?: (nodeId?: string | number) => string | undefined; getMonitoringLink?: (name?: string, type?: ETenantType) => string | null; getLogsLink?: (name?: string) => string | null; + getLinks?: (name?: string, type?: ETenantType) => DatabaseLink[]; } export type NodeAddress = Pick; diff --git a/src/uiFactory/types.ts b/src/uiFactory/types.ts index a739a9cee..d5ba682cf 100644 --- a/src/uiFactory/types.ts +++ b/src/uiFactory/types.ts @@ -3,9 +3,12 @@ import type { GetHealthcheckViewTitles, GetHealthcheckViewsOrder, } from '../containers/Tenant/Healthcheck/shared'; +import type {ClusterInfo} from '../store/reducers/cluster/cluster'; import type {IssuesTree} from '../store/reducers/healthcheckInfo/types'; import type {PreparedTenant} from '../store/reducers/tenants/types'; +import type {ClusterLink, DatabaseLink} from '../types/additionalProps'; import type {MetaBaseClusterInfo} from '../types/api/meta'; +import type {ETenantType} from '../types/api/tenant'; import type {GetLogsLink} from '../utils/logs'; import type {GetMonitoringClusterLink, GetMonitoringLink} from '../utils/monitoring'; @@ -22,6 +25,9 @@ export interface UIFactory { getMonitoringLink?: GetMonitoringLink; getMonitoringClusterLink?: GetMonitoringClusterLink; + getDatabaseLinks?: GetDatabaseLinks; + getClusterLinks?: GetClusterLinks; + healthcheck: { getHealthckechViewTitles: GetHealthcheckViewTitles; getHealthcheckViewsOrder: GetHealthcheckViewsOrder; @@ -48,3 +54,11 @@ export type HandleAddCluster = () => Promise; export type HandleEditCluster = (params: {clusterData: MetaBaseClusterInfo}) => Promise; export type HandleDeleteCluster = (params: {clusterData: MetaBaseClusterInfo}) => Promise; + +export type GetDatabaseLinks = (params: { + clusterInfo: ClusterInfo; + dbName?: string; + dbType?: ETenantType; +}) => DatabaseLink[]; + +export type GetClusterLinks = (params: {clusterInfo: ClusterInfo}) => ClusterLink[]; diff --git a/src/utils/additionalProps.ts b/src/utils/additionalProps.ts index d635d629b..f7996da28 100644 --- a/src/utils/additionalProps.ts +++ b/src/utils/additionalProps.ts @@ -1,10 +1,51 @@ +import {FileText} from '@gravity-ui/icons'; + +import i18n from '../components/TenantNameWrapper/i18n'; import {backend} from '../store'; -import type {AdditionalNodesProps} from '../types/additionalProps'; +import type { + AdditionalNodesProps, + AdditionalTenantsProps, + DatabaseLink, +} from '../types/additionalProps'; +import type {ETenantType} from '../types/api/tenant'; import {getBackendFromBalancerAndNodeId} from './prepareBackend'; +import monitoringIcon from '../assets/icons/monitoring.svg'; + export const getAdditionalNodesProps = (balancer = backend): AdditionalNodesProps => { return { getNodeRef: (node) => getBackendFromBalancerAndNodeId(node?.NodeId, balancer ?? ''), }; }; + +export function getDatabaseLinks( + additionalProps?: AdditionalTenantsProps, + name?: string, + type?: ETenantType, +) { + if (!additionalProps) { + return []; + } + + const links: DatabaseLink[] = []; + if (additionalProps.getMonitoringLink) { + const link = additionalProps.getMonitoringLink(name, type); + if (link) { + links.push({title: i18n('field_monitoring-link'), url: link, icon: monitoringIcon}); + } + } + + if (additionalProps.getLogsLink) { + const link = additionalProps.getLogsLink(name); + if (link) { + links.push({title: i18n('field_logs-link'), url: link, icon: FileText}); + } + } + + if (additionalProps.getLinks) { + links.push(...additionalProps.getLinks(name, type)); + } + + return links; +}