From 1e4e6143c321bbc24083cc1ff3ae7259b8657297 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Thu, 25 Sep 2025 15:20:45 +0200 Subject: [PATCH 01/12] PMM-14348 Add toggle for PMM Server QAN --- .../percona/inventory/Inventory.service.ts | 4 + .../app/percona/inventory/Inventory.types.ts | 91 +++++++++++++++++++ .../app/percona/settings/Settings.messages.ts | 3 + .../components/Advanced/Advanced.constants.ts | 3 + .../settings/components/Advanced/Advanced.tsx | 59 ++++++++++-- .../components/Advanced/Advanced.types.ts | 2 + .../components/Advanced/Advanced.utils.ts | 18 +++- .../app/percona/shared/core/reducers/index.ts | 5 +- .../services/services/Services.types.ts | 1 + 9 files changed, 176 insertions(+), 10 deletions(-) diff --git a/public/app/percona/inventory/Inventory.service.ts b/public/app/percona/inventory/Inventory.service.ts index 22f0a061ed169..fd3b26d124add 100644 --- a/public/app/percona/inventory/Inventory.service.ts +++ b/public/app/percona/inventory/Inventory.service.ts @@ -11,6 +11,7 @@ import { NodeListDBPayload, RemoveNodeBody, ServiceAgentListPayload, + UpdateAgentBody, } from './Inventory.types'; const BASE_URL = `/v1/inventory`; @@ -25,6 +26,9 @@ export const InventoryService = { }, }); }, + updateAgent(agentId: string, payload: UpdateAgentBody, token?: CancelToken) { + return api.put(`${BASE_URL}/agents/${agentId}`, payload, false, token); + }, removeAgent(agentId: string, forceMode = false, token?: CancelToken) { // todo: address forceMode return api.delete(`${BASE_URL}/agents/${agentId}`, false, token, { force: forceMode }); diff --git a/public/app/percona/inventory/Inventory.types.ts b/public/app/percona/inventory/Inventory.types.ts index 82a2c70102a95..6b6e96b97765c 100644 --- a/public/app/percona/inventory/Inventory.types.ts +++ b/public/app/percona/inventory/Inventory.types.ts @@ -1,3 +1,4 @@ +import { MetricsResolutions } from '../settings/Settings.types'; import { Databases } from '../shared/core'; import { DbNode, NodeType } from '../shared/services/nodes/Nodes.types'; import { @@ -187,3 +188,93 @@ export interface AgentsOption { value: string; label: string; } + +export interface UpdateAgentBody { + node_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + mysqld_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + mongodb_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + postgres_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + proxysql_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + external_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + rds_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + azure_database_exporter?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_mysql_perfschema_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_mysql_slowlog_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_mongodb_profiler_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_mongodb_mongolog_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_postgresql_pgstatements_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + qan_postgresql_pgstatmonitor_agent?: { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; + }; + nomad_agent?: { + enable?: boolean; + }; +} diff --git a/public/app/percona/settings/Settings.messages.ts b/public/app/percona/settings/Settings.messages.ts index 9ec8d27ed2ee0..563041e3c7e0a 100644 --- a/public/app/percona/settings/Settings.messages.ts +++ b/public/app/percona/settings/Settings.messages.ts @@ -38,6 +38,9 @@ export const Messages = { backupLabel: 'Backup Management', backupTooltip: 'Option to enable/disable Backup Management features.', backupLink: `https://per.co.na/backup_management`, + pmmServerMonitoringLabel: 'QAN for PMM Server', + pmmServerMonitoringTooltip: 'Enable/disable QAN for PMM Server', + pmmServerMonitoringLink: '', technicalPreviewLegend: 'Technical preview features', technicalPreviewDescription: 'These are technical preview features, not recommended to be used in production environments. Read more\n' + diff --git a/public/app/percona/settings/components/Advanced/Advanced.constants.ts b/public/app/percona/settings/components/Advanced/Advanced.constants.ts index f7fc1d92dac00..6fb18eef8032b 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.constants.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.constants.ts @@ -36,3 +36,6 @@ export const TECHNICAL_PREVIEW_DOC_URL = 'https://per.co.na/pmm-feature-status'; // all feature flags export const FEATURE_KEYS: Array = ['alerting', 'backup', 'stt', 'azureDiscover']; + +export const PMM_SERVER_AGENT_SERVICE_NAME = 'pmm-server-postgresql'; +export const PMM_SERVER_AGENT_NODE_ID = 'pmm-server'; diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index 071231c1fd589..ee5d648081fad 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -1,5 +1,5 @@ import { cx } from '@emotion/css'; -import { FC, useState } from 'react'; +import { FC, useEffect, useMemo, useState } from 'react'; import { Field, withTypes } from 'react-final-form'; import { Button, Icon, Spinner, useStyles2 } from '@grafana/ui'; @@ -12,7 +12,7 @@ import { TextInputField } from 'app/percona/shared/components/Form/TextInput'; import { TabbedPage, TabbedPageContents } from 'app/percona/shared/components/TabbedPage'; import { useCancelToken } from 'app/percona/shared/components/hooks/cancelToken.hook'; import { updateSettingsAction } from 'app/percona/shared/core/reducers'; -import { getPerconaSettings } from 'app/percona/shared/core/selectors'; +import { getPerconaSettings, getServices } from 'app/percona/shared/core/selectors'; import validators from 'app/percona/shared/helpers/validators'; import { useAppDispatch } from 'app/store/store'; import { useSelector } from 'app/types'; @@ -31,8 +31,16 @@ import { } from './Advanced.constants'; import { getStyles } from './Advanced.styles'; import { AdvancedFormProps } from './Advanced.types'; -import { convertCheckIntervalsToHours, convertHoursStringToSeconds, convertSecondsToDays } from './Advanced.utils'; +import { + convertCheckIntervalsToHours, + convertHoursStringToSeconds, + convertSecondsToDays, + getMonitoringAgent, +} from './Advanced.utils'; import { SwitchRow } from './SwitchRow'; +import { InventoryService } from 'app/percona/inventory/Inventory.service'; +import { fetchServicesAction } from 'app/percona/shared/core/reducers/services'; +import { GET_SERVICES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; const { advanced: { sttCheckIntervalsLabel, sttCheckIntervalTooltip, sttCheckIntervalUnit }, @@ -42,7 +50,9 @@ export const Advanced: FC = () => { const styles = useStyles2(getStyles); const [generateToken] = useCancelToken(); const { result: settings } = useSelector(getPerconaSettings); + const { services } = useSelector(getServices); const dispatch = useAppDispatch(); + const agent = useMemo(() => getMonitoringAgent(services), [services]); const { advisorRunIntervals: sttCheckIntervals, dataRetention, @@ -111,6 +121,8 @@ export const Advanced: FC = () => { frequentInterval, telemetrySummaries, accessControl: enableAccessControl, + pmmServerMonitoringAgentId: agent?.agentId, + pmmServerMonitoringEnabled: !agent?.disabled, }; const [loading, setLoading] = useState(false); @@ -128,6 +140,8 @@ export const Advanced: FC = () => { frequentInterval, updates, accessControl, + pmmServerMonitoringEnabled, + pmmServerMonitoringAgentId, } = values; const sttCheckIntervals = { rare_interval: `${convertHoursStringToSeconds(rareInterval)}s`, @@ -149,16 +163,35 @@ export const Advanced: FC = () => { }; setLoading(true); - await dispatch( - updateSettingsAction({ - body, - token: generateToken(SET_SETTINGS_CANCEL_TOKEN), - }) + const promises = new Array>(); + + if (pmmServerMonitoringAgentId) { + promises.push( + InventoryService.updateAgent(pmmServerMonitoringAgentId, { + qan_postgresql_pgstatements_agent: { + enable: pmmServerMonitoringEnabled, + }, + }) + ); + } + + promises.push( + dispatch( + updateSettingsAction({ + body, + token: generateToken(SET_SETTINGS_CANCEL_TOKEN), + }) + ) ); + await Promise.all(promises); setLoading(false); }; const { Form } = withTypes(); + useEffect(() => { + dispatch(fetchServicesAction({ token: generateToken(GET_SERVICES_CANCEL_TOKEN) })); + }, []); + return ( @@ -256,6 +289,16 @@ export const Advanced: FC = () => { dataTestId="advanced-backup" component={SwitchRow} /> +
diff --git a/public/app/percona/settings/components/Advanced/Advanced.types.ts b/public/app/percona/settings/components/Advanced/Advanced.types.ts index 7007e91dd9040..65d80b5b46052 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.types.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.types.ts @@ -12,4 +12,6 @@ export interface AdvancedFormProps { frequentInterval: string; telemetrySummaries: string[]; accessControl?: boolean; + pmmServerMonitoringEnabled?: boolean; + pmmServerMonitoringAgentId?: string; } diff --git a/public/app/percona/settings/components/Advanced/Advanced.utils.ts b/public/app/percona/settings/components/Advanced/Advanced.utils.ts index e52d923e83f25..619c2d37679c3 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.utils.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.utils.ts @@ -2,7 +2,15 @@ import moment from 'moment/moment'; import { AdvisorRunIntervalsSettings } from 'app/percona/settings/Settings.types'; -import { HOURS, MINUTES_IN_HOUR, SECONDS_IN_DAY } from './Advanced.constants'; +import { + HOURS, + MINUTES_IN_HOUR, + PMM_SERVER_AGENT_NODE_ID, + PMM_SERVER_AGENT_SERVICE_NAME, + SECONDS_IN_DAY, +} from './Advanced.constants'; +import { AgentType } from 'app/percona/inventory/Inventory.types'; +import { Service } from 'app/percona/shared/services/services/Services.types'; export const convertSecondsToDays = (dataRetention: string) => { const [count, units] = [+dataRetention.slice(0, -1), dataRetention.slice(-1)]; @@ -36,3 +44,11 @@ export const convertCheckIntervalsToHours = (sttCheckIntervals: AdvisorRunInterv frequentInterval: `${convertSecondsStringToHour(rawFrequentInterval)}`, }; }; + +export const getMonitoringAgent = (services: Service[]) => { + const service = services.find( + (s) => s.params.serviceName === PMM_SERVER_AGENT_SERVICE_NAME && s.params.nodeId === PMM_SERVER_AGENT_NODE_ID + ); + + return service?.params.agents?.find((a) => a.agentType === AgentType.qanPostgresql_pgstatements_agent); +}; diff --git a/public/app/percona/shared/core/reducers/index.ts b/public/app/percona/shared/core/reducers/index.ts index b88b28dc82004..359d159304777 100644 --- a/public/app/percona/shared/core/reducers/index.ts +++ b/public/app/percona/shared/core/reducers/index.ts @@ -21,7 +21,7 @@ import navigationReducer from './navigation'; import nodesReducer from './nodes'; import pmmDumpsReducers from './pmmDump/pmmDump'; import rolesReducers from './roles/roles'; -import servicesReducer from './services'; +import servicesReducer, { fetchServicesAction } from './services'; import tourReducer from './tour/tour'; import updatesReducers from './updates'; import perconaUserReducers from './user/user'; @@ -113,6 +113,9 @@ export const updateSettingsAction = createAsyncThunk( } const settings = await SettingsService.setSettings(args.body, args.token, true); await thunkAPI.dispatch(fetchSettingsAction({ usedPassword: password, testEmail })); + // Refetch services for QAN for PMM Server field + // @ts-ignore + await thunkAPI.dispatch(fetchServicesAction({})); return settings; })() ), diff --git a/public/app/percona/shared/services/services/Services.types.ts b/public/app/percona/shared/services/services/Services.types.ts index 92bbaa41fab72..7dfdeda02b5ca 100644 --- a/public/app/percona/shared/services/services/Services.types.ts +++ b/public/app/percona/shared/services/services/Services.types.ts @@ -73,6 +73,7 @@ export interface DbAgent { status?: ServiceAgentStatus; agentType?: string; isConnected?: boolean; + disabled?: boolean; } export interface DbService { From 3f11de2a5ea57fa3670bccf3ca8e7df0bb2c24ef Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Thu, 2 Oct 2025 14:34:28 +0200 Subject: [PATCH 02/12] PMM-14348 Add unit tests --- .../components/Advanced/Advanced.test.tsx | 121 ++++++++++++++++++ .../settings/components/Advanced/Advanced.tsx | 4 +- 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/public/app/percona/settings/components/Advanced/Advanced.test.tsx b/public/app/percona/settings/components/Advanced/Advanced.test.tsx index d1a2775103289..4f8ce6559eee0 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.test.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.test.tsx @@ -7,10 +7,77 @@ import { configureStore } from 'app/store/configureStore'; import { StoreState } from 'app/types'; import { Advanced } from './Advanced'; +import { Service, ServiceStatus } from 'app/percona/shared/services/services/Services.types'; +import { PMM_SERVER_AGENT_NODE_ID, PMM_SERVER_AGENT_SERVICE_NAME } from './Advanced.constants'; +import { AgentType } from 'app/percona/inventory/Inventory.types'; +import { Databases } from 'app/percona/shared/core'; +import { InventoryService } from 'app/percona/inventory/Inventory.service'; jest.mock('app/percona/settings/Settings.service'); +const updateAgentSpy = jest.spyOn(InventoryService, 'updateAgent').mockImplementation(() => Promise.resolve({})); + +const setup = (pmmMonitoringEnabled = true) => { + const pmmServerService: Service = { + type: Databases.postgresql, + params: { + serviceId: 'service-id', + nodeName: 'node-name', + status: ServiceStatus.UP, + serviceName: PMM_SERVER_AGENT_SERVICE_NAME, + nodeId: PMM_SERVER_AGENT_NODE_ID, + agents: [ + { + disabled: !pmmMonitoringEnabled, + agentId: 'agent-id', + agentType: AgentType.qanPostgresql_pgstatements_agent, + }, + ], + }, + }; + + return render( + + {wrapWithGrafanaContextMock()} + + ); +}; + describe('Advanced::', () => { + beforeEach(() => { + updateAgentSpy.mockClear(); + }); + it('Renders correctly with props', () => { render( { }) ); }); + + it('updates agent when pmm server monitoring is turned on', async () => { + const { container } = setup(); + + const monitoringSwitch = container.querySelector( + '[data-testid="pmm-server-monitoring"] [name="pmmServerMonitoringEnabled"]' + ); + + expect(monitoringSwitch).toBeInTheDocument(); + + fireEvent.click(monitoringSwitch!); + + fireEvent.submit(screen.getByTestId('advanced-button')); + + await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); + + expect(updateAgentSpy).toHaveBeenCalledWith('agent-id', { + qan_postgresql_pgstatements_agent: { + enable: false, + }, + }); + }); + + it('updates agent when pmm server monitoring is turned off', async () => { + const { container } = setup(false); + + const monitoringSwitch = container.querySelector( + '[data-testid="pmm-server-monitoring"] [name="pmmServerMonitoringEnabled"]' + ); + + expect(monitoringSwitch).toBeInTheDocument(); + + fireEvent.click(monitoringSwitch!); + + fireEvent.submit(screen.getByTestId('advanced-button')); + + await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); + + expect(updateAgentSpy).toHaveBeenCalledWith('agent-id', { + qan_postgresql_pgstatements_agent: { + enable: true, + }, + }); + }); + + it("doesn't updates agent when pmm server monitoring doesn't change", async () => { + setup(); + + fireEvent.submit(screen.getByTestId('advanced-button')); + + await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); + + expect(updateAgentSpy).not.toHaveBeenCalled(); + }); }); diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index ee5d648081fad..6c5cace02cb7e 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -165,7 +165,7 @@ export const Advanced: FC = () => { setLoading(true); const promises = new Array>(); - if (pmmServerMonitoringAgentId) { + if (pmmServerMonitoringAgentId && initialValues.pmmServerMonitoringEnabled !== pmmServerMonitoringEnabled) { promises.push( InventoryService.updateAgent(pmmServerMonitoringAgentId, { qan_postgresql_pgstatements_agent: { @@ -296,7 +296,7 @@ export const Advanced: FC = () => { tooltip={Messages.advanced.pmmServerMonitoringTooltip} tooltipLinkText={Messages.advanced.pmmServerMonitoringTooltip} link={Messages.advanced.pmmServerMonitoringLink} - dataTestId="access-control" + dataTestId="pmm-server-monitoring" component={SwitchRow} />
From b9972f337e40293c0bb479abba0eefc560762a02 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Thu, 2 Oct 2025 14:37:32 +0200 Subject: [PATCH 03/12] PMM-14348 Run lint --- .../settings/components/Advanced/Advanced.test.tsx | 8 ++++---- .../percona/settings/components/Advanced/Advanced.tsx | 10 +++++----- .../settings/components/Advanced/Advanced.utils.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/app/percona/settings/components/Advanced/Advanced.test.tsx b/public/app/percona/settings/components/Advanced/Advanced.test.tsx index 4f8ce6559eee0..bf769b7b1b279 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.test.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.test.tsx @@ -1,17 +1,17 @@ import { render, screen, fireEvent, waitForElementToBeRemoved } from '@testing-library/react'; import { Provider } from 'react-redux'; +import { InventoryService } from 'app/percona/inventory/Inventory.service'; +import { AgentType } from 'app/percona/inventory/Inventory.types'; +import { Databases } from 'app/percona/shared/core'; import * as reducers from 'app/percona/shared/core/reducers'; import { wrapWithGrafanaContextMock } from 'app/percona/shared/helpers/testUtils'; +import { Service, ServiceStatus } from 'app/percona/shared/services/services/Services.types'; import { configureStore } from 'app/store/configureStore'; import { StoreState } from 'app/types'; import { Advanced } from './Advanced'; -import { Service, ServiceStatus } from 'app/percona/shared/services/services/Services.types'; import { PMM_SERVER_AGENT_NODE_ID, PMM_SERVER_AGENT_SERVICE_NAME } from './Advanced.constants'; -import { AgentType } from 'app/percona/inventory/Inventory.types'; -import { Databases } from 'app/percona/shared/core'; -import { InventoryService } from 'app/percona/inventory/Inventory.service'; jest.mock('app/percona/settings/Settings.service'); diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index 6c5cace02cb7e..8bd385f012a0c 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -3,6 +3,8 @@ import { FC, useEffect, useMemo, useState } from 'react'; import { Field, withTypes } from 'react-final-form'; import { Button, Icon, Spinner, useStyles2 } from '@grafana/ui'; +import { GET_SERVICES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; +import { InventoryService } from 'app/percona/inventory/Inventory.service'; import { Messages } from 'app/percona/settings/Settings.messages'; import { getSettingsStyles } from 'app/percona/settings/Settings.styles'; import { FeatureLoader } from 'app/percona/shared/components/Elements/FeatureLoader'; @@ -12,6 +14,7 @@ import { TextInputField } from 'app/percona/shared/components/Form/TextInput'; import { TabbedPage, TabbedPageContents } from 'app/percona/shared/components/TabbedPage'; import { useCancelToken } from 'app/percona/shared/components/hooks/cancelToken.hook'; import { updateSettingsAction } from 'app/percona/shared/core/reducers'; +import { fetchServicesAction } from 'app/percona/shared/core/reducers/services'; import { getPerconaSettings, getServices } from 'app/percona/shared/core/selectors'; import validators from 'app/percona/shared/helpers/validators'; import { useAppDispatch } from 'app/store/store'; @@ -38,9 +41,6 @@ import { getMonitoringAgent, } from './Advanced.utils'; import { SwitchRow } from './SwitchRow'; -import { InventoryService } from 'app/percona/inventory/Inventory.service'; -import { fetchServicesAction } from 'app/percona/shared/core/reducers/services'; -import { GET_SERVICES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; const { advanced: { sttCheckIntervalsLabel, sttCheckIntervalTooltip, sttCheckIntervalUnit }, @@ -163,7 +163,7 @@ export const Advanced: FC = () => { }; setLoading(true); - const promises = new Array>(); + const promises: Array> = []; if (pmmServerMonitoringAgentId && initialValues.pmmServerMonitoringEnabled !== pmmServerMonitoringEnabled) { promises.push( @@ -190,7 +190,7 @@ export const Advanced: FC = () => { useEffect(() => { dispatch(fetchServicesAction({ token: generateToken(GET_SERVICES_CANCEL_TOKEN) })); - }, []); + }, [dispatch, generateToken]); return ( diff --git a/public/app/percona/settings/components/Advanced/Advanced.utils.ts b/public/app/percona/settings/components/Advanced/Advanced.utils.ts index 619c2d37679c3..1b56d659991ac 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.utils.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.utils.ts @@ -1,6 +1,8 @@ import moment from 'moment/moment'; +import { AgentType } from 'app/percona/inventory/Inventory.types'; import { AdvisorRunIntervalsSettings } from 'app/percona/settings/Settings.types'; +import { Service } from 'app/percona/shared/services/services/Services.types'; import { HOURS, @@ -9,8 +11,6 @@ import { PMM_SERVER_AGENT_SERVICE_NAME, SECONDS_IN_DAY, } from './Advanced.constants'; -import { AgentType } from 'app/percona/inventory/Inventory.types'; -import { Service } from 'app/percona/shared/services/services/Services.types'; export const convertSecondsToDays = (dataRetention: string) => { const [count, units] = [+dataRetention.slice(0, -1), dataRetention.slice(-1)]; From be17be5584ef388fe516f7305af9bfc8b9ad17a1 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Thu, 2 Oct 2025 15:34:16 +0200 Subject: [PATCH 04/12] PMM-14348 Fix unit tests --- .../AddInstance/AddInstance.test.tsx | 1 + .../OptionsCell/OptionsCell.test.tsx | 1 + .../components/Advanced/Advanced.test.tsx | 24 ++++++++++++------- .../Platform/Connected/Connected.test.tsx | 1 + .../components/SSHKey/SSHKey.test.tsx | 1 + .../CheckPermissions.test.tsx | 1 + .../PermissionLoader.test.tsx | 1 + .../services/__mocks__/Services.service.ts | 5 ++++ 8 files changed, 26 insertions(+), 9 deletions(-) diff --git a/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx b/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx index 4a6bce2e02c9e..53d50ace73ab5 100644 --- a/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx +++ b/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx @@ -12,6 +12,7 @@ import { AddInstance } from './AddInstance'; import { instanceList } from './AddInstance.constants'; jest.mock('app/percona/settings/Settings.service'); +jest.mock('app/percona/shared/services/services/Services.service'); const selectedInstanceType: InstanceAvailable = { type: '' }; diff --git a/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx b/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx index 1d81ebaca828d..cc73cd688b172 100644 --- a/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx +++ b/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx @@ -14,6 +14,7 @@ import OptionsCell from './OptionsCell'; jest.mock('app/percona/shared/services/roles/Roles.service'); jest.mock('app/percona/settings/Settings.service'); +jest.mock('app/percona/shared/services/services/Services.service'); const wrapWithProvider = (children: ReactElement) => ( Promise.resolve({})); @@ -78,7 +80,7 @@ describe('Advanced::', () => { updateAgentSpy.mockClear(); }); - it('Renders correctly with props', () => { + it('renders correctly with props', async () => { render( { }, }, }, - } as StoreState)} + } as unknown as StoreState)} > {wrapWithGrafanaContextMock()} ); - expect(screen.getByTestId('retention-number-input')).toHaveValue(30); - expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost'); + await waitFor(() => expect(screen.getByTestId('retention-number-input')).toHaveValue(30)); + await waitFor(() => expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('localhost')); }); - it('Calls apply changes', async () => { + it('calls apply changes', async () => { const spy = jest.spyOn(reducers, 'updateSettingsAction'); render( { {wrapWithGrafanaContextMock()} ); + fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } }); fireEvent.submit(screen.getByTestId('advanced-button')); + await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); expect(spy).toHaveBeenCalled(); }); - it('Sets correct URL from browser', async () => { + it('sets correct URL from browser', async () => { const location = { ...window.location, host: 'pmmtest.percona.com', @@ -196,10 +200,11 @@ describe('Advanced::', () => { ); fireEvent.click(screen.getByTestId('public-address-button')); - expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('pmmtest.percona.com'); + + await waitFor(() => expect(screen.getByTestId('publicAddress-text-input')).toHaveValue('pmmtest.percona.com')); }); - it('Does not include STT check intervals in the change request if STT checks are disabled', async () => { + it('does not include STT check intervals in the change request if STT checks are disabled', async () => { const spy = jest.spyOn(reducers, 'updateSettingsAction'); render( @@ -235,6 +240,7 @@ describe('Advanced::', () => { fireEvent.change(screen.getByTestId('retention-number-input'), { target: { value: 70 } }); fireEvent.submit(screen.getByTestId('advanced-button')); + await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); // expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeUndefined(); diff --git a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx index 700297b6c3b18..8110154c1714d 100644 --- a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx +++ b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx @@ -10,6 +10,7 @@ import { Connected } from './Connected'; import { Messages } from './Connected.messages'; jest.mock('app/percona/settings/components/Platform/Platform.service'); +jest.mock('app/percona/shared/services/services/Services.service'); jest.mock('app/percona/settings/Settings.service'); describe('Connected:', () => { diff --git a/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx b/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx index b1dbfc3b98dfd..480af03545fc0 100644 --- a/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx +++ b/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx @@ -9,6 +9,7 @@ import { StoreState } from 'app/types'; import { SSHKey } from './SSHKey'; jest.mock('app/percona/settings/Settings.service'); +jest.mock('app/percona/shared/services/services/Services.service'); describe('SSHKey::', () => { it('Renders correctly', () => { diff --git a/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx b/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx index 32fffeacf47a7..d64cac3ad299e 100644 --- a/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx +++ b/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx @@ -5,6 +5,7 @@ import { SettingsService } from 'app/percona/settings/Settings.service'; import { CheckPermissions } from './CheckPermissions'; jest.mock('app/percona/settings/Settings.service'); +jest.mock('app/percona/shared/services/services/Services.service'); jest.mock('app/percona/shared/helpers/logger', () => { const originalModule = jest.requireActual('app/percona/shared/helpers/logger'); return { diff --git a/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx b/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx index b454e2b731f3e..c7327c875363f 100644 --- a/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx +++ b/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx @@ -7,6 +7,7 @@ import { StoreState } from 'app/types'; import { PermissionLoader } from './PermissionLoader'; jest.mock('app/percona/settings/Settings.service'); +jest.mock('app/percona/shared/services/services/Services.service'); jest.mock('app/percona/shared/helpers/logger', () => { const originalModule = jest.requireActual('app/percona/shared/helpers/logger'); return { diff --git a/public/app/percona/shared/services/services/__mocks__/Services.service.ts b/public/app/percona/shared/services/services/__mocks__/Services.service.ts index de3e38be28ddd..89c268b241d30 100644 --- a/public/app/percona/shared/services/services/__mocks__/Services.service.ts +++ b/public/app/percona/shared/services/services/__mocks__/Services.service.ts @@ -9,4 +9,9 @@ ServicesService.getActive = () => service_types: [], }); +ServicesService.getServices = () => + Promise.resolve({ + services: [], + }); + ServicesService.removeService = () => Promise.resolve({}); From fde7d7a6aa370830da6335b4f6f5b996859ece60 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Thu, 2 Oct 2025 15:42:22 +0200 Subject: [PATCH 05/12] PMM-14348 Remove unused import --- .../app/percona/settings/components/Advanced/Advanced.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/percona/settings/components/Advanced/Advanced.test.tsx b/public/app/percona/settings/components/Advanced/Advanced.test.tsx index 205d1b636b8ce..bbb97ca9a4caa 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.test.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.test.tsx @@ -12,7 +12,6 @@ import { StoreState } from 'app/types'; import { Advanced } from './Advanced'; import { PMM_SERVER_AGENT_NODE_ID, PMM_SERVER_AGENT_SERVICE_NAME } from './Advanced.constants'; -import { act } from 'react'; jest.mock('app/percona/settings/Settings.service'); jest.mock('app/percona/shared/services/services/Services.service'); From 20c270fb4c360a0270b164196d1e407482c32947 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Fri, 3 Oct 2025 14:26:15 +0200 Subject: [PATCH 06/12] PMM-14348 Refactor UpdateAgentBody --- .../app/percona/inventory/Inventory.types.ts | 105 ++++-------------- 1 file changed, 21 insertions(+), 84 deletions(-) diff --git a/public/app/percona/inventory/Inventory.types.ts b/public/app/percona/inventory/Inventory.types.ts index 6b6e96b97765c..4312afd7d1dda 100644 --- a/public/app/percona/inventory/Inventory.types.ts +++ b/public/app/percona/inventory/Inventory.types.ts @@ -189,91 +189,28 @@ export interface AgentsOption { label: string; } +export interface Agent { + enable?: boolean; + custom_labels?: Record; + enable_push_metrics?: boolean; + metrics_resolutions?: MetricsResolutions; +} + export interface UpdateAgentBody { - node_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - mysqld_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - mongodb_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - postgres_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - proxysql_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - external_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - rds_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - azure_database_exporter?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_mysql_perfschema_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_mysql_slowlog_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_mongodb_profiler_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_mongodb_mongolog_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_postgresql_pgstatements_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; - qan_postgresql_pgstatmonitor_agent?: { - enable?: boolean; - custom_labels?: Record; - enable_push_metrics?: boolean; - metrics_resolutions?: MetricsResolutions; - }; + node_exporter?: Agent; + mysqld_exporter?: Agent; + mongodb_exporter?: Agent; + postgres_exporter?: Agent; + proxysql_exporter?: Agent; + external_exporter?: Agent; + rds_exporter?: Agent; + azure_database_exporter?: Agent; + qan_mysql_perfschema_agent?: Agent; + qan_mysql_slowlog_agent?: Agent; + qan_mongodb_profiler_agent?: Agent; + qan_mongodb_mongolog_agent?: Agent; + qan_postgresql_pgstatements_agent?: Agent; + qan_postgresql_pgstatmonitor_agent?: Agent; nomad_agent?: { enable?: boolean; }; From b47f0af75d2a588f6a2c4f7ed04837e2db2de598 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Fri, 3 Oct 2025 15:05:45 +0200 Subject: [PATCH 07/12] PMM-14248 Update type for agent item in update --- .../app/percona/inventory/Inventory.types.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/public/app/percona/inventory/Inventory.types.ts b/public/app/percona/inventory/Inventory.types.ts index 4312afd7d1dda..5b312ff027fd0 100644 --- a/public/app/percona/inventory/Inventory.types.ts +++ b/public/app/percona/inventory/Inventory.types.ts @@ -189,7 +189,7 @@ export interface AgentsOption { label: string; } -export interface Agent { +export interface UpdateAgentItem { enable?: boolean; custom_labels?: Record; enable_push_metrics?: boolean; @@ -197,20 +197,20 @@ export interface Agent { } export interface UpdateAgentBody { - node_exporter?: Agent; - mysqld_exporter?: Agent; - mongodb_exporter?: Agent; - postgres_exporter?: Agent; - proxysql_exporter?: Agent; - external_exporter?: Agent; - rds_exporter?: Agent; - azure_database_exporter?: Agent; - qan_mysql_perfschema_agent?: Agent; - qan_mysql_slowlog_agent?: Agent; - qan_mongodb_profiler_agent?: Agent; - qan_mongodb_mongolog_agent?: Agent; - qan_postgresql_pgstatements_agent?: Agent; - qan_postgresql_pgstatmonitor_agent?: Agent; + node_exporter?: UpdateAgentItem; + mysqld_exporter?: UpdateAgentItem; + mongodb_exporter?: UpdateAgentItem; + postgres_exporter?: UpdateAgentItem; + proxysql_exporter?: UpdateAgentItem; + external_exporter?: UpdateAgentItem; + rds_exporter?: UpdateAgentItem; + azure_database_exporter?: UpdateAgentItem; + qan_mysql_perfschema_agent?: UpdateAgentItem; + qan_mysql_slowlog_agent?: UpdateAgentItem; + qan_mongodb_profiler_agent?: UpdateAgentItem; + qan_mongodb_mongolog_agent?: UpdateAgentItem; + qan_postgresql_pgstatements_agent?: UpdateAgentItem; + qan_postgresql_pgstatmonitor_agent?: UpdateAgentItem; nomad_agent?: { enable?: boolean; }; From d430c8dfdd140370c5cc1f1cc852b675c0591c0e Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Fri, 3 Oct 2025 15:24:32 +0200 Subject: [PATCH 08/12] PMM-14248 Update wording for QAN toggle --- public/app/percona/settings/Settings.messages.ts | 5 +++-- .../percona/settings/components/Advanced/Advanced.tsx | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/public/app/percona/settings/Settings.messages.ts b/public/app/percona/settings/Settings.messages.ts index 563041e3c7e0a..76f156e2a06c0 100644 --- a/public/app/percona/settings/Settings.messages.ts +++ b/public/app/percona/settings/Settings.messages.ts @@ -39,8 +39,9 @@ export const Messages = { backupTooltip: 'Option to enable/disable Backup Management features.', backupLink: `https://per.co.na/backup_management`, pmmServerMonitoringLabel: 'QAN for PMM Server', - pmmServerMonitoringTooltip: 'Enable/disable QAN for PMM Server', - pmmServerMonitoringLink: '', + pmmServerMonitoringTooltip: + "Displays queries from PMM Server's internal PostgreSQL database in Query Analytics (QAN). Enable to troubleshoot PMM Server's database performance alongside your monitored instances.", + pmmServerMonitoringLink: 'https://per.co.na/qan-pmm-server', technicalPreviewLegend: 'Technical preview features', technicalPreviewDescription: 'These are technical preview features, not recommended to be used in production environments. Read more\n' + diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index 8bd385f012a0c..e2070d866a203 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -103,6 +103,9 @@ export const Advanced: FC = () => { backupLabel, backupLink, backupTooltip, + pmmServerMonitoringLabel, + pmmServerMonitoringLink, + pmmServerMonitoringTooltip, }, tooltipLinkText, } = Messages; @@ -292,10 +295,10 @@ export const Advanced: FC = () => { From 3983c5c8f0557d130fad5d6aa8681dd5d9a5640f Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Wed, 8 Oct 2025 14:22:17 +0200 Subject: [PATCH 09/12] PMM-14348 Refactor to use settings property --- .../AddInstance/AddInstance.test.tsx | 1 - .../DeleteServiceModal.test.tsx | 1 - .../DeleteServicesModal.test.tsx | 1 - .../OptionsCell/OptionsCell.test.tsx | 1 - .../app/percona/settings/Settings.messages.ts | 6 +- .../app/percona/settings/Settings.service.ts | 2 + public/app/percona/settings/Settings.types.ts | 3 + .../components/Advanced/Advanced.constants.ts | 3 - .../components/Advanced/Advanced.test.tsx | 94 +++++------- .../settings/components/Advanced/Advanced.tsx | 139 ++++++++---------- .../components/Advanced/Advanced.types.ts | 3 +- .../components/Advanced/Advanced.utils.ts | 8 - .../Platform/Connected/Connected.test.tsx | 2 +- .../components/SSHKey/SSHKey.test.tsx | 1 - .../CheckPermissions.test.tsx | 1 - .../PermissionLoader.test.tsx | 1 - .../app/percona/shared/core/reducers/index.ts | 6 +- 17 files changed, 105 insertions(+), 168 deletions(-) diff --git a/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx b/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx index 53d50ace73ab5..4a6bce2e02c9e 100644 --- a/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx +++ b/public/app/percona/add-instance/components/AddInstance/AddInstance.test.tsx @@ -12,7 +12,6 @@ import { AddInstance } from './AddInstance'; import { instanceList } from './AddInstance.constants'; jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); const selectedInstanceType: InstanceAvailable = { type: '' }; diff --git a/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx b/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx index e7e3c517ea2d9..87e3a372bc8d8 100644 --- a/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx +++ b/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx @@ -11,7 +11,6 @@ const successFn = jest.fn(); const removeServiceActionSpy = jest.spyOn(ServicesReducer, 'removeServiceAction'); jest.mock('app/percona/inventory/Inventory.service'); -jest.mock('app/percona/shared/services/services/Services.service'); const renderDefaults = (isOpen = true) => render( diff --git a/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx b/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx index 9ed56f0407d9a..fa7685563e956 100644 --- a/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx +++ b/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx @@ -15,7 +15,6 @@ const successFn = jest.fn(); const removeServicesActionSpy = jest.spyOn(ServicesReducer, 'removeServicesAction'); jest.mock('app/percona/inventory/Inventory.service'); -jest.mock('app/percona/shared/services/services/Services.service'); const serviceStub: FlattenService = { nodeId: 'Node #1', diff --git a/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx b/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx index cc73cd688b172..1d81ebaca828d 100644 --- a/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx +++ b/public/app/percona/rbac/AccessRoles/components/OptionsCell/OptionsCell.test.tsx @@ -14,7 +14,6 @@ import OptionsCell from './OptionsCell'; jest.mock('app/percona/shared/services/roles/Roles.service'); jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); const wrapWithProvider = (children: ReactElement) => ( ({ isConnectedToPortal: response.connected_to_platform, defaultRoleId: response.default_role_id, enableAccessControl: response.enable_access_control, + enableInternalPgQan: response.enable_internal_pg_qan, }); const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({ @@ -116,4 +117,5 @@ const toReadonlyModel = (response: ReadonlySettingsPayload): Settings => ({ isConnectedToPortal: false, defaultRoleId: -1, enableAccessControl: response.enable_access_control, + enableInternalPgQan: false, }); diff --git a/public/app/percona/settings/Settings.types.ts b/public/app/percona/settings/Settings.types.ts index b1556bf5e90cb..e749c6c198fb4 100644 --- a/public/app/percona/settings/Settings.types.ts +++ b/public/app/percona/settings/Settings.types.ts @@ -63,6 +63,7 @@ export interface AdvancedChangePayload extends AdvancedPayload { enable_azurediscover?: boolean; enable_updates?: boolean; enable_access_control?: boolean; + enable_internal_pg_qan?: boolean; } export interface MetricsResolutionsPayload { @@ -122,6 +123,7 @@ export interface SettingsPayload telemetry_summaries: string[]; default_role_id: number; enable_access_control: boolean; + enable_internal_pg_qan: boolean; } export interface SettingsPayload @@ -180,6 +182,7 @@ export interface Settings extends ReadonlySettings { isConnectedToPortal?: boolean; telemetrySummaries: string[]; defaultRoleId: number; + enableInternalPgQan: boolean; } export interface MetricsResolutions { diff --git a/public/app/percona/settings/components/Advanced/Advanced.constants.ts b/public/app/percona/settings/components/Advanced/Advanced.constants.ts index 6fb18eef8032b..f7fc1d92dac00 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.constants.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.constants.ts @@ -36,6 +36,3 @@ export const TECHNICAL_PREVIEW_DOC_URL = 'https://per.co.na/pmm-feature-status'; // all feature flags export const FEATURE_KEYS: Array = ['alerting', 'backup', 'stt', 'azureDiscover']; - -export const PMM_SERVER_AGENT_SERVICE_NAME = 'pmm-server-postgresql'; -export const PMM_SERVER_AGENT_NODE_ID = 'pmm-server'; diff --git a/public/app/percona/settings/components/Advanced/Advanced.test.tsx b/public/app/percona/settings/components/Advanced/Advanced.test.tsx index bbb97ca9a4caa..0a95152f23512 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.test.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.test.tsx @@ -1,51 +1,23 @@ import { render, screen, fireEvent, waitForElementToBeRemoved, waitFor } from '@testing-library/react'; import { Provider } from 'react-redux'; -import { InventoryService } from 'app/percona/inventory/Inventory.service'; -import { AgentType } from 'app/percona/inventory/Inventory.types'; -import { Databases } from 'app/percona/shared/core'; import * as reducers from 'app/percona/shared/core/reducers'; import { wrapWithGrafanaContextMock } from 'app/percona/shared/helpers/testUtils'; -import { Service, ServiceStatus } from 'app/percona/shared/services/services/Services.types'; import { configureStore } from 'app/store/configureStore'; import { StoreState } from 'app/types'; import { Advanced } from './Advanced'; -import { PMM_SERVER_AGENT_NODE_ID, PMM_SERVER_AGENT_SERVICE_NAME } from './Advanced.constants'; jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); - -const updateAgentSpy = jest.spyOn(InventoryService, 'updateAgent').mockImplementation(() => Promise.resolve({})); - -const setup = (pmmMonitoringEnabled = true) => { - const pmmServerService: Service = { - type: Databases.postgresql, - params: { - serviceId: 'service-id', - nodeName: 'node-name', - status: ServiceStatus.UP, - serviceName: PMM_SERVER_AGENT_SERVICE_NAME, - nodeId: PMM_SERVER_AGENT_NODE_ID, - agents: [ - { - disabled: !pmmMonitoringEnabled, - agentId: 'agent-id', - agentType: AgentType.qanPostgresql_pgstatements_agent, - }, - ], - }, - }; - return render( +const updateSettingsSpy = jest.spyOn(reducers, 'updateSettingsAction'); + +const setup = (pmmMonitoringEnabled = true) => + render( { azureDiscoverEnabled: true, publicAddress: 'localhost', alertingEnabled: true, + enableInternalPgQan: pmmMonitoringEnabled, }, }, }, @@ -72,11 +45,10 @@ const setup = (pmmMonitoringEnabled = true) => { {wrapWithGrafanaContextMock()} ); -}; describe('Advanced::', () => { beforeEach(() => { - updateAgentSpy.mockClear(); + updateSettingsSpy.mockClear(); }); it('renders correctly with props', async () => { @@ -116,7 +88,6 @@ describe('Advanced::', () => { }); it('calls apply changes', async () => { - const spy = jest.spyOn(reducers, 'updateSettingsAction'); render( { await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); - expect(spy).toHaveBeenCalled(); + expect(updateSettingsSpy).toHaveBeenCalled(); }); it('sets correct URL from browser', async () => { @@ -204,8 +175,6 @@ describe('Advanced::', () => { }); it('does not include STT check intervals in the change request if STT checks are disabled', async () => { - const spy = jest.spyOn(reducers, 'updateSettingsAction'); - render( { await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); - // expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeUndefined(); - expect(spy).toHaveBeenLastCalledWith( + expect(updateSettingsSpy).toHaveBeenLastCalledWith( expect.objectContaining({ body: expect.objectContaining({ advisor_run_intervals: undefined, @@ -253,8 +221,6 @@ describe('Advanced::', () => { }); it('Includes STT check intervals in the change request if STT checks are enabled', async () => { - const spy = jest.spyOn(reducers, 'updateSettingsAction'); - render( { await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); // expect(spy.calls.mostRecent().args[0].body.stt_check_intervals).toBeDefined(); - expect(spy).toHaveBeenLastCalledWith( + expect(updateSettingsSpy).toHaveBeenLastCalledWith( expect.objectContaining({ body: expect.objectContaining({ advisor_run_intervals: { @@ -305,11 +271,11 @@ describe('Advanced::', () => { ); }); - it('updates agent when pmm server monitoring is turned on', async () => { + it('updates internal monitoring when pmm server monitoring is turned on', async () => { const { container } = setup(); const monitoringSwitch = container.querySelector( - '[data-testid="pmm-server-monitoring"] [name="pmmServerMonitoringEnabled"]' + '[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]' ); expect(monitoringSwitch).toBeInTheDocument(); @@ -320,18 +286,20 @@ describe('Advanced::', () => { await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); - expect(updateAgentSpy).toHaveBeenCalledWith('agent-id', { - qan_postgresql_pgstatements_agent: { - enable: false, - }, - }); + expect(updateSettingsSpy).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + enable_internal_pg_qan: false, + }), + }) + ); }); - it('updates agent when pmm server monitoring is turned off', async () => { + it('updates internal monitoring when pmm server monitoring is turned off', async () => { const { container } = setup(false); const monitoringSwitch = container.querySelector( - '[data-testid="pmm-server-monitoring"] [name="pmmServerMonitoringEnabled"]' + '[data-testid="enable-internal-pg-qan"] [name="enableInternalPgQan"]' ); expect(monitoringSwitch).toBeInTheDocument(); @@ -342,20 +310,28 @@ describe('Advanced::', () => { await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); - expect(updateAgentSpy).toHaveBeenCalledWith('agent-id', { - qan_postgresql_pgstatements_agent: { - enable: true, - }, - }); + expect(updateSettingsSpy).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + enable_internal_pg_qan: true, + }), + }) + ); }); - it("doesn't updates agent when pmm server monitoring doesn't change", async () => { + it("doesn't update internal monitoring when pmm server monitoring doesn't change", async () => { setup(); fireEvent.submit(screen.getByTestId('advanced-button')); await waitForElementToBeRemoved(() => screen.getByTestId('Spinner')); - expect(updateAgentSpy).not.toHaveBeenCalled(); + expect(updateSettingsSpy).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + enable_internal_pg_qan: true, + }), + }) + ); }); }); diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index e2070d866a203..aa338f636c51f 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -1,10 +1,9 @@ import { cx } from '@emotion/css'; -import { FC, useEffect, useMemo, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { Field, withTypes } from 'react-final-form'; import { Button, Icon, Spinner, useStyles2 } from '@grafana/ui'; import { GET_SERVICES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; -import { InventoryService } from 'app/percona/inventory/Inventory.service'; import { Messages } from 'app/percona/settings/Settings.messages'; import { getSettingsStyles } from 'app/percona/settings/Settings.styles'; import { FeatureLoader } from 'app/percona/shared/components/Elements/FeatureLoader'; @@ -34,25 +33,59 @@ import { } from './Advanced.constants'; import { getStyles } from './Advanced.styles'; import { AdvancedFormProps } from './Advanced.types'; -import { - convertCheckIntervalsToHours, - convertHoursStringToSeconds, - convertSecondsToDays, - getMonitoringAgent, -} from './Advanced.utils'; +import { convertCheckIntervalsToHours, convertHoursStringToSeconds, convertSecondsToDays } from './Advanced.utils'; import { SwitchRow } from './SwitchRow'; const { - advanced: { sttCheckIntervalsLabel, sttCheckIntervalTooltip, sttCheckIntervalUnit }, + tooltipLinkText, + advanced: { + action, + retentionLabel, + retentionTooltip, + retentionUnits, + telemetryLabel, + telemetryLink, + telemetryTooltip, + telemetrySummaryTitle, + telemetryDisclaimer, + updatesLabel, + updatesLink, + updatesTooltip, + advisorsLabel, + advisorsLink, + advisorsTooltip, + publicAddressLabel, + publicAddressTooltip, + publicAddressButton, + accessControl, + accessControlTooltip, + accessControlLink, + alertingLabel, + alertingTooltip, + alertingLink, + azureDiscoverLabel, + azureDiscoverTooltip, + azureDiscoverLink, + technicalPreviewLegend, + technicalPreviewDescription, + technicalPreviewLinkText, + backupLabel, + backupLink, + backupTooltip, + enableInternalPgQanLabel, + enableInternalPgQanLink, + enableInternalPgQanTooltip, + sttCheckIntervalsLabel, + sttCheckIntervalTooltip, + sttCheckIntervalUnit, + }, } = Messages; export const Advanced: FC = () => { const styles = useStyles2(getStyles); const [generateToken] = useCancelToken(); const { result: settings } = useSelector(getPerconaSettings); - const { services } = useSelector(getServices); const dispatch = useAppDispatch(); - const agent = useMemo(() => getMonitoringAgent(services), [services]); const { advisorRunIntervals: sttCheckIntervals, dataRetention, @@ -65,50 +98,10 @@ export const Advanced: FC = () => { alertingEnabled, telemetrySummaries, enableAccessControl, + enableInternalPgQan, } = settings!; const settingsStyles = useStyles2(getSettingsStyles); const { rareInterval, standardInterval, frequentInterval } = convertCheckIntervalsToHours(sttCheckIntervals); - const { - advanced: { - action, - retentionLabel, - retentionTooltip, - retentionUnits, - telemetryLabel, - telemetryLink, - telemetryTooltip, - telemetrySummaryTitle, - telemetryDisclaimer, - updatesLabel, - updatesLink, - updatesTooltip, - advisorsLabel, - advisorsLink, - advisorsTooltip, - publicAddressLabel, - publicAddressTooltip, - publicAddressButton, - accessControl, - accessControlTooltip, - accessControlLink, - alertingLabel, - alertingTooltip, - alertingLink, - azureDiscoverLabel, - azureDiscoverTooltip, - azureDiscoverLink, - technicalPreviewLegend, - technicalPreviewDescription, - technicalPreviewLinkText, - backupLabel, - backupLink, - backupTooltip, - pmmServerMonitoringLabel, - pmmServerMonitoringLink, - pmmServerMonitoringTooltip, - }, - tooltipLinkText, - } = Messages; const initialValues: AdvancedFormProps = { retention: convertSecondsToDays(dataRetention), @@ -124,8 +117,7 @@ export const Advanced: FC = () => { frequentInterval, telemetrySummaries, accessControl: enableAccessControl, - pmmServerMonitoringAgentId: agent?.agentId, - pmmServerMonitoringEnabled: !agent?.disabled, + enableInternalPgQan, }; const [loading, setLoading] = useState(false); @@ -143,8 +135,7 @@ export const Advanced: FC = () => { frequentInterval, updates, accessControl, - pmmServerMonitoringEnabled, - pmmServerMonitoringAgentId, + enableInternalPgQan, } = values; const sttCheckIntervals = { rare_interval: `${convertHoursStringToSeconds(rareInterval)}s`, @@ -163,30 +154,16 @@ export const Advanced: FC = () => { enable_backup_management: backup, enable_updates: updates, enable_access_control: accessControl, + enable_internal_pg_qan: enableInternalPgQan, }; setLoading(true); - const promises: Array> = []; - - if (pmmServerMonitoringAgentId && initialValues.pmmServerMonitoringEnabled !== pmmServerMonitoringEnabled) { - promises.push( - InventoryService.updateAgent(pmmServerMonitoringAgentId, { - qan_postgresql_pgstatements_agent: { - enable: pmmServerMonitoringEnabled, - }, - }) - ); - } - - promises.push( - dispatch( - updateSettingsAction({ - body, - token: generateToken(SET_SETTINGS_CANCEL_TOKEN), - }) - ) + await dispatch( + updateSettingsAction({ + body, + token: generateToken(SET_SETTINGS_CANCEL_TOKEN), + }) ); - await Promise.all(promises); setLoading(false); }; const { Form } = withTypes(); @@ -293,13 +270,13 @@ export const Advanced: FC = () => { component={SwitchRow} />
diff --git a/public/app/percona/settings/components/Advanced/Advanced.types.ts b/public/app/percona/settings/components/Advanced/Advanced.types.ts index 65d80b5b46052..5df711ea6e25f 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.types.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.types.ts @@ -12,6 +12,5 @@ export interface AdvancedFormProps { frequentInterval: string; telemetrySummaries: string[]; accessControl?: boolean; - pmmServerMonitoringEnabled?: boolean; - pmmServerMonitoringAgentId?: string; + enableInternalPgQan?: boolean; } diff --git a/public/app/percona/settings/components/Advanced/Advanced.utils.ts b/public/app/percona/settings/components/Advanced/Advanced.utils.ts index 1b56d659991ac..e43d46400f121 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.utils.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.utils.ts @@ -44,11 +44,3 @@ export const convertCheckIntervalsToHours = (sttCheckIntervals: AdvisorRunInterv frequentInterval: `${convertSecondsStringToHour(rawFrequentInterval)}`, }; }; - -export const getMonitoringAgent = (services: Service[]) => { - const service = services.find( - (s) => s.params.serviceName === PMM_SERVER_AGENT_SERVICE_NAME && s.params.nodeId === PMM_SERVER_AGENT_NODE_ID - ); - - return service?.params.agents?.find((a) => a.agentType === AgentType.qanPostgresql_pgstatements_agent); -}; diff --git a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx index 8110154c1714d..b7b098ca81293 100644 --- a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx +++ b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx @@ -10,7 +10,7 @@ import { Connected } from './Connected'; import { Messages } from './Connected.messages'; jest.mock('app/percona/settings/components/Platform/Platform.service'); -jest.mock('app/percona/shared/services/services/Services.service'); + jest.mock('app/percona/settings/Settings.service'); describe('Connected:', () => { diff --git a/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx b/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx index 480af03545fc0..b1dbfc3b98dfd 100644 --- a/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx +++ b/public/app/percona/settings/components/SSHKey/SSHKey.test.tsx @@ -9,7 +9,6 @@ import { StoreState } from 'app/types'; import { SSHKey } from './SSHKey'; jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); describe('SSHKey::', () => { it('Renders correctly', () => { diff --git a/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx b/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx index d64cac3ad299e..32fffeacf47a7 100644 --- a/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx +++ b/public/app/percona/shared/components/Elements/CheckPermissions/CheckPermissions.test.tsx @@ -5,7 +5,6 @@ import { SettingsService } from 'app/percona/settings/Settings.service'; import { CheckPermissions } from './CheckPermissions'; jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); jest.mock('app/percona/shared/helpers/logger', () => { const originalModule = jest.requireActual('app/percona/shared/helpers/logger'); return { diff --git a/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx b/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx index c7327c875363f..b454e2b731f3e 100644 --- a/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx +++ b/public/app/percona/shared/components/Elements/PermissionLoader/PermissionLoader.test.tsx @@ -7,7 +7,6 @@ import { StoreState } from 'app/types'; import { PermissionLoader } from './PermissionLoader'; jest.mock('app/percona/settings/Settings.service'); -jest.mock('app/percona/shared/services/services/Services.service'); jest.mock('app/percona/shared/helpers/logger', () => { const originalModule = jest.requireActual('app/percona/shared/helpers/logger'); return { diff --git a/public/app/percona/shared/core/reducers/index.ts b/public/app/percona/shared/core/reducers/index.ts index 359d159304777..cb3673500648f 100644 --- a/public/app/percona/shared/core/reducers/index.ts +++ b/public/app/percona/shared/core/reducers/index.ts @@ -21,7 +21,7 @@ import navigationReducer from './navigation'; import nodesReducer from './nodes'; import pmmDumpsReducers from './pmmDump/pmmDump'; import rolesReducers from './roles/roles'; -import servicesReducer, { fetchServicesAction } from './services'; +import servicesReducer from './services'; import tourReducer from './tour/tour'; import updatesReducers from './updates'; import perconaUserReducers from './user/user'; @@ -63,6 +63,7 @@ const initialSettingsState: Settings = { isConnectedToPortal: false, defaultRoleId: 1, enableAccessControl: false, + enableInternalPgQan: false, }; export const fetchSettingsAction = createAsyncThunk( @@ -113,9 +114,6 @@ export const updateSettingsAction = createAsyncThunk( } const settings = await SettingsService.setSettings(args.body, args.token, true); await thunkAPI.dispatch(fetchSettingsAction({ usedPassword: password, testEmail })); - // Refetch services for QAN for PMM Server field - // @ts-ignore - await thunkAPI.dispatch(fetchServicesAction({})); return settings; })() ), From d9a26cad2b5373ef17d9f1d113f5a24c07163b78 Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Wed, 8 Oct 2025 14:31:24 +0200 Subject: [PATCH 10/12] PMM-14348 Revert unnecessary changes --- .../DeleteServiceModal/DeleteServiceModal.test.tsx | 1 + .../DeleteServicesModal/DeleteServicesModal.test.tsx | 1 + .../settings/components/Advanced/Advanced.utils.ts | 10 +--------- .../components/Platform/Connected/Connected.test.tsx | 1 - 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx b/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx index 87e3a372bc8d8..e7e3c517ea2d9 100644 --- a/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx +++ b/public/app/percona/inventory/components/DeleteServiceModal/DeleteServiceModal.test.tsx @@ -11,6 +11,7 @@ const successFn = jest.fn(); const removeServiceActionSpy = jest.spyOn(ServicesReducer, 'removeServiceAction'); jest.mock('app/percona/inventory/Inventory.service'); +jest.mock('app/percona/shared/services/services/Services.service'); const renderDefaults = (isOpen = true) => render( diff --git a/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx b/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx index fa7685563e956..9ed56f0407d9a 100644 --- a/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx +++ b/public/app/percona/inventory/components/DeleteServicesModal/DeleteServicesModal.test.tsx @@ -15,6 +15,7 @@ const successFn = jest.fn(); const removeServicesActionSpy = jest.spyOn(ServicesReducer, 'removeServicesAction'); jest.mock('app/percona/inventory/Inventory.service'); +jest.mock('app/percona/shared/services/services/Services.service'); const serviceStub: FlattenService = { nodeId: 'Node #1', diff --git a/public/app/percona/settings/components/Advanced/Advanced.utils.ts b/public/app/percona/settings/components/Advanced/Advanced.utils.ts index e43d46400f121..e52d923e83f25 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.utils.ts +++ b/public/app/percona/settings/components/Advanced/Advanced.utils.ts @@ -1,16 +1,8 @@ import moment from 'moment/moment'; -import { AgentType } from 'app/percona/inventory/Inventory.types'; import { AdvisorRunIntervalsSettings } from 'app/percona/settings/Settings.types'; -import { Service } from 'app/percona/shared/services/services/Services.types'; -import { - HOURS, - MINUTES_IN_HOUR, - PMM_SERVER_AGENT_NODE_ID, - PMM_SERVER_AGENT_SERVICE_NAME, - SECONDS_IN_DAY, -} from './Advanced.constants'; +import { HOURS, MINUTES_IN_HOUR, SECONDS_IN_DAY } from './Advanced.constants'; export const convertSecondsToDays = (dataRetention: string) => { const [count, units] = [+dataRetention.slice(0, -1), dataRetention.slice(-1)]; diff --git a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx index b7b098ca81293..700297b6c3b18 100644 --- a/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx +++ b/public/app/percona/settings/components/Platform/Connected/Connected.test.tsx @@ -10,7 +10,6 @@ import { Connected } from './Connected'; import { Messages } from './Connected.messages'; jest.mock('app/percona/settings/components/Platform/Platform.service'); - jest.mock('app/percona/settings/Settings.service'); describe('Connected:', () => { From 89f8c2e0b445b868663953034335cb221d8b555c Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Wed, 8 Oct 2025 14:33:10 +0200 Subject: [PATCH 11/12] PMM-14348 Remove fetching of services --- .../percona/settings/components/Advanced/Advanced.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/public/app/percona/settings/components/Advanced/Advanced.tsx b/public/app/percona/settings/components/Advanced/Advanced.tsx index aa338f636c51f..450fe355b5f1f 100644 --- a/public/app/percona/settings/components/Advanced/Advanced.tsx +++ b/public/app/percona/settings/components/Advanced/Advanced.tsx @@ -1,9 +1,8 @@ import { cx } from '@emotion/css'; -import { FC, useEffect, useState } from 'react'; +import { FC, useState } from 'react'; import { Field, withTypes } from 'react-final-form'; import { Button, Icon, Spinner, useStyles2 } from '@grafana/ui'; -import { GET_SERVICES_CANCEL_TOKEN } from 'app/percona/inventory/Inventory.constants'; import { Messages } from 'app/percona/settings/Settings.messages'; import { getSettingsStyles } from 'app/percona/settings/Settings.styles'; import { FeatureLoader } from 'app/percona/shared/components/Elements/FeatureLoader'; @@ -13,8 +12,7 @@ import { TextInputField } from 'app/percona/shared/components/Form/TextInput'; import { TabbedPage, TabbedPageContents } from 'app/percona/shared/components/TabbedPage'; import { useCancelToken } from 'app/percona/shared/components/hooks/cancelToken.hook'; import { updateSettingsAction } from 'app/percona/shared/core/reducers'; -import { fetchServicesAction } from 'app/percona/shared/core/reducers/services'; -import { getPerconaSettings, getServices } from 'app/percona/shared/core/selectors'; +import { getPerconaSettings } from 'app/percona/shared/core/selectors'; import validators from 'app/percona/shared/helpers/validators'; import { useAppDispatch } from 'app/store/store'; import { useSelector } from 'app/types'; @@ -168,10 +166,6 @@ export const Advanced: FC = () => { }; const { Form } = withTypes(); - useEffect(() => { - dispatch(fetchServicesAction({ token: generateToken(GET_SERVICES_CANCEL_TOKEN) })); - }, [dispatch, generateToken]); - return ( From b050529e08760b4b4c36d67d2a31221c1730f89d Mon Sep 17 00:00:00 2001 From: Matej Kubinec Date: Wed, 8 Oct 2025 15:00:04 +0200 Subject: [PATCH 12/12] PMM-14348 Add in missing settings stub --- public/app/percona/settings/__mocks__/Settings.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/percona/settings/__mocks__/Settings.service.ts b/public/app/percona/settings/__mocks__/Settings.service.ts index 7deef0502a503..ac7412a5a50c4 100644 --- a/public/app/percona/settings/__mocks__/Settings.service.ts +++ b/public/app/percona/settings/__mocks__/Settings.service.ts @@ -37,6 +37,7 @@ export const stub: Settings = { frequentInterval: '10s', }, defaultRoleId: 1, + enableInternalPgQan: false, }; SettingsService.getSettings = () => Promise.resolve(stub);