diff --git a/packages/manager/.changeset/pr-12678-changed-1754995292006.md b/packages/manager/.changeset/pr-12678-changed-1754995292006.md new file mode 100644 index 00000000000..55ff0fdc05d --- /dev/null +++ b/packages/manager/.changeset/pr-12678-changed-1754995292006.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +Update logic in metrics filters to use the resources from `useResources` useQuery cache in CloudPulse metrics ([#12678](https://github.com/linode/manager/pull/12678)) diff --git a/packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-dashboard-errors.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-dashboard-errors.spec.ts index 1ced6eaa60d..d852296fd3d 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-dashboard-errors.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/cloudpulse-dashboard-errors.spec.ts @@ -389,6 +389,11 @@ describe('Tests for API error handling', () => { // Wait for the API calls . cy.wait(['@fetchServices', '@fetchDashboard']); + // simulate an error on instances call before changing the region again + mockGetDatabasesError('Internal Server Error').as( + 'getDatabaseInstancesError' + ); + // Select a dashboard from the autocomplete input ui.autocomplete .findByLabel('Dashboard') @@ -400,33 +405,6 @@ describe('Tests for API error handling', () => { .should('be.visible') .click(); - // Select a Database Engine from the autocomplete input. - ui.autocomplete - .findByLabel('Database Engine') - .should('be.visible') - .type(engine); - - ui.autocompletePopper.findByTitle(engine).should('be.visible').click(); - - // Select a region from the dropdown. - ui.regionSelect.find().click(); - ui.regionSelect - .findItemByRegionId(mockRegions[0].id, mockRegions) - .should('be.visible') - .click(); - - // simulate an error on instances call before changing the region again - mockGetDatabasesError('Internal Server Error').as( - 'getDatabaseInstancesError' - ); - - // Select a region from the dropdown. - ui.regionSelect.find().click(); - ui.regionSelect - .findItemByRegionId(mockRegions[1].id, mockRegions) - .should('be.visible') - .click(); - // Wait for the intercepted request to complete cy.wait('@getDatabaseInstancesError'); diff --git a/packages/manager/src/components/GenerateFirewallDialog/useCreateFirewallFromTemplate.ts b/packages/manager/src/components/GenerateFirewallDialog/useCreateFirewallFromTemplate.ts index 625b4887a56..4239ce7ec0f 100644 --- a/packages/manager/src/components/GenerateFirewallDialog/useCreateFirewallFromTemplate.ts +++ b/packages/manager/src/components/GenerateFirewallDialog/useCreateFirewallFromTemplate.ts @@ -65,7 +65,7 @@ export const createFirewallFromTemplate = async (options: { // Get firewalls and firewall template in parallel const [{ rules, slug }, firewalls] = await Promise.all([ queryClient.ensureQueryData(firewallQueries.template(templateSlug)), - queryClient.fetchQuery(firewallQueries.firewalls._ctx.all), // must fetch fresh data if generating more than one firewall + queryClient.fetchQuery(firewallQueries.firewalls._ctx.all()), // must fetch fresh data if generating more than one firewall ]); if (updateProgress) { diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertRegions/AlertRegions.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertRegions/AlertRegions.tsx index 5feb5123fb2..c15a160a41b 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertRegions/AlertRegions.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertRegions/AlertRegions.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField'; import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; +import { RESOURCE_FILTER_MAP } from '../../Utils/constants'; import { type AlertFormMode, REGION_GROUP_INFO_MESSAGE, @@ -17,7 +18,7 @@ import { getFilteredRegions } from '../Utils/utils'; import { DisplayAlertRegions } from './DisplayAlertRegions'; import type { AlertRegion } from './DisplayAlertRegions'; -import type { CloudPulseServiceType, Filter } from '@linode/api-v4'; +import type { CloudPulseServiceType } from '@linode/api-v4'; interface AlertRegionsProps { /** @@ -48,19 +49,11 @@ export const AlertRegions = React.memo((props: AlertRegionsProps) => { const { data: regions, isLoading: isRegionsLoading } = useRegionsQuery(); const [selectedRegions, setSelectedRegions] = React.useState(value); const [showSelected, setShowSelected] = React.useState(false); - - const resourceFilterMap: Record = { - dbaas: { - platform: 'rdbms-default', - }, - }; const { data: resources, isLoading: isResourcesLoading } = useResourcesQuery( Boolean(serviceType && regions?.length), serviceType === null ? undefined : serviceType, {}, - { - ...(resourceFilterMap[serviceType ?? ''] ?? {}), - } + { ...(RESOURCE_FILTER_MAP[serviceType ?? ''] ?? {}) } ); const handleSelectionChange = React.useCallback( diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.tsx index 70dcf79ce53..45271682aa6 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertsResources/AlertsResources.tsx @@ -132,6 +132,10 @@ export const AlertResources = React.memo((props: AlertResourcesProp) => { const supportedRegionIds = getSupportedRegionIds(regions, serviceType); const xFilterToBeApplied: Filter | undefined = React.useMemo(() => { + if (serviceType === 'firewall') { + return undefined; + } + const regionFilter: Filter = supportedRegionIds ? { '+or': supportedRegionIds.map((regionId) => ({ diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx index 5932d5ed919..d87ae94a1b1 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx @@ -9,6 +9,7 @@ import { useGetCloudPulseMetricDefinitionsByServiceType, } from 'src/queries/cloudpulse/services'; +import { RESOURCE_FILTER_MAP } from '../Utils/constants'; import { useAclpPreference } from '../Utils/UserPreference'; import { renderPlaceHolder, @@ -92,7 +93,7 @@ export const CloudPulseDashboard = (props: DashboardProperties) => { Boolean(dashboard?.service_type), dashboard?.service_type, {}, - dashboard?.service_type === 'dbaas' ? { platform: 'rdbms-default' } : {} + RESOURCE_FILTER_MAP[dashboard?.service_type ?? ''] ?? {} ); const { @@ -131,7 +132,13 @@ export const CloudPulseDashboard = (props: DashboardProperties) => { } if (isMetricDefinitionLoading || isDashboardLoading || isResourcesLoading) { - return ; + return ( + ({ + padding: theme.spacingFunction(16), + })} + /> + ); } if (!dashboard) { diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardLanding.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardLanding.tsx index b11e7197842..0f7ef75ea14 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardLanding.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardLanding.tsx @@ -18,11 +18,12 @@ export interface FilterData { id: { [filterKey: string]: FilterValueType }; label: { [filterKey: string]: string[] }; } +export interface CloudPulseMetricsFilter { + [key: string]: FilterValueType; +} export interface DashboardProp { dashboard?: Dashboard; - filterValue: { - [key: string]: FilterValueType; - }; + filterValue: CloudPulseMetricsFilter; timeDuration?: DateTimeWithPreset; } diff --git a/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.test.tsx b/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.test.tsx index 1c10b05f6b2..79a911c5c22 100644 --- a/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.test.tsx +++ b/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.test.tsx @@ -1,6 +1,7 @@ import { screen } from '@testing-library/react'; import React from 'react'; +import { databaseInstanceFactory } from 'src/factories'; import { renderWithTheme } from 'src/utilities/testHelpers'; import { GlobalFilters } from './GlobalFilters'; @@ -19,6 +20,19 @@ const setup = () => { /> ); }; + +const queryMocks = vi.hoisted(() => ({ + useResourcesQuery: vi.fn().mockReturnValue({}), +})); + +vi.mock('src/queries/cloudpulse/resources', async () => { + const actual = await vi.importActual('src/queries/cloudpulse/resources'); + return { + ...actual, + useResourcesQuery: queryMocks.useResourcesQuery, + }; +}); + describe('Global filters component test', () => { it('Should render refresh button', () => { setup(); @@ -40,4 +54,16 @@ describe('Global filters component test', () => { expect(timeRangeSelect).toBeInTheDocument(); }); + + it('Should show circle progress if resources call is loading', async () => { + queryMocks.useResourcesQuery.mockReturnValue({ + data: [{ ...databaseInstanceFactory.build(), clusterSize: 1 }], + isLoading: true, + }); + + setup(); + + const progress = await screen.findByTestId('circle-progress'); + expect(progress).toBeInTheDocument(); + }); }); diff --git a/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.tsx b/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.tsx index 1c236ed2e97..69663ae83a4 100644 --- a/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.tsx +++ b/packages/manager/src/features/CloudPulse/Overview/GlobalFilters.tsx @@ -4,13 +4,19 @@ import { GridLegacy } from '@mui/material'; import * as React from 'react'; import Reload from 'src/assets/icons/refresh.svg'; +import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; import { CloudPulseDashboardFilterBuilder } from '../shared/CloudPulseDashboardFilterBuilder'; import { CloudPulseDashboardSelect } from '../shared/CloudPulseDashboardSelect'; import { CloudPulseDateTimeRangePicker } from '../shared/CloudPulseDateTimeRangePicker'; import { CloudPulseTooltip } from '../shared/CloudPulseTooltip'; import { convertToGmt } from '../Utils/CloudPulseDateTimePickerUtils'; -import { DASHBOARD_ID, REFRESH, TIME_DURATION } from '../Utils/constants'; +import { + DASHBOARD_ID, + REFRESH, + RESOURCE_FILTER_MAP, + TIME_DURATION, +} from '../Utils/constants'; import { useAclpPreference } from '../Utils/UserPreference'; import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding'; @@ -88,6 +94,14 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => { handleAnyFilterChange(REFRESH, Date.now(), []); }, []); + const { isLoading, isError } = useResourcesQuery( + selectedDashboard !== undefined, + selectedDashboard?.service_type ?? '', + {}, + + RESOURCE_FILTER_MAP[selectedDashboard?.service_type ?? ''] ?? {} + ); + return ( @@ -150,6 +164,8 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => { dashboard={selectedDashboard} emitFilterChange={emitFilterChange} handleToggleAppliedFilter={handleToggleAppliedFilter} + isError={isError} + isLoading={isLoading} isServiceAnalyticsIntegration={false} preferences={preferences} /> diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts index 0a42100a08b..217a73b1222 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts @@ -1,16 +1,17 @@ import { databaseQueries } from '@linode/queries'; import { DateTime } from 'luxon'; -import { dashboardFactory } from 'src/factories'; +import { dashboardFactory, databaseInstanceFactory } from 'src/factories'; import { RESOURCE_ID, RESOURCES } from './constants'; import { deepEqual, + filterBasedOnConfig, + filterUsingDependentFilters, getFilters, getTextFilterProperties, } from './FilterBuilder'; import { - buildXFilter, checkIfAllMandatoryFiltersAreSelected, constructAdditionalRequestFilters, getCustomSelectProperties, @@ -22,7 +23,10 @@ import { shouldDisableFilterByFilterKey, } from './FilterBuilder'; import { FILTER_CONFIG } from './FilterConfig'; -import { CloudPulseSelectTypes } from './models'; +import { CloudPulseAvailableViews, CloudPulseSelectTypes } from './models'; + +import type { CloudPulseResources } from '../shared/CloudPulseResourcesSelect'; +import type { CloudPulseServiceTypeFilters } from './models'; const mockDashboard = dashboardFactory.build(); @@ -111,7 +115,7 @@ it('test getResourceSelectionProperties method', () => { expect(handleResourcesSelection).toBeDefined(); expect(savePreferences).toEqual(false); expect(disabled).toEqual(false); - expect(JSON.stringify(xFilter)).toEqual('{"+and":[{"region":"us-east"}]}'); + expect(JSON.stringify(xFilter)).toEqual('{"region":"us-east"}'); expect(label).toEqual(name); } }); @@ -143,7 +147,7 @@ it('test getResourceSelectionProperties method with disabled true', () => { expect(handleResourcesSelection).toBeDefined(); expect(savePreferences).toEqual(false); expect(disabled).toEqual(true); - expect(JSON.stringify(xFilter)).toEqual('{"+and":[]}'); + expect(JSON.stringify(xFilter)).toEqual('{}'); expect(label).toEqual(name); } }); @@ -247,26 +251,6 @@ it('test getNodeTypeProperties with disabled true', () => { } }); -it('test buildXfilter method', () => { - const resourceSelectionConfig = linodeConfig?.filters.find( - (filterObj) => filterObj.name === 'Resources' - ); - - expect(resourceSelectionConfig).toBeDefined(); // fails if resources selection in not defined - - if (resourceSelectionConfig) { - let result = buildXFilter(resourceSelectionConfig, { - region: 'us-east', - }); - - expect(JSON.stringify(result)).toEqual('{"+and":[{"region":"us-east"}]}'); - - result = buildXFilter(resourceSelectionConfig, {}); - - expect(JSON.stringify(result)).toEqual('{"+and":[]}'); - } -}); - it('test checkIfAllMandatoryFiltersAreSelected method', () => { const resourceSelectionConfig = linodeConfig?.filters.find( (filterObj) => filterObj.name === 'Resources' @@ -501,3 +485,139 @@ it('should return the filters based on dashboard', () => { expect(filters?.length).toBe(1); }); + +describe('filterUsingDependentFilters', () => { + const mockData: CloudPulseResources[] = [ + { + ...databaseInstanceFactory.build(), + region: 'us-east', + engineType: 'mysql', + id: '1', + tags: ['test'], + }, + { + ...databaseInstanceFactory.build(), + region: 'us-west', + engineType: 'postgresql', + id: '2', + tags: ['test', 'test2'], + }, + ]; + it('should return the data passed if data or dependentFilters are undefined', () => { + expect(filterUsingDependentFilters(undefined, undefined)).toBeUndefined(); + expect(filterUsingDependentFilters(mockData, undefined)).toBe(mockData); + expect(filterUsingDependentFilters(undefined, {})).toBeUndefined(); + }); + + it('should filter based on a single key-value match', () => { + const filters = { engineType: 'mysql' }; + const result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([mockData[0]]); + }); + + it('should filter when both resource and filter value are arrays', () => { + const filters = { tags: ['test', 'test2'] }; + const result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([mockData[0], mockData[1]]); + }); + + it('should return empty array if no resource matches', () => { + const filters = { region: 'us-central' }; + const result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([]); + }); + + it('should apply multiple filters simultaneously', () => { + let filters = { + engineType: 'postgresql', + region: 'us-east', + tags: 'test', + }; + let result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([]); + + filters = { + engineType: 'postgresql', + region: 'us-east', + tags: 'test', + }; + + result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([]); + + filters = { + engineType: 'postgresql', + region: 'us-west', + tags: 'test', + }; + + result = filterUsingDependentFilters(mockData, filters); + expect(result).toEqual([mockData[1]]); + }); +}); + +describe('filterBasedOnConfig', () => { + const config: CloudPulseServiceTypeFilters = { + configuration: { + dependency: [], // empty dependency + filterKey: 'resource_id', + filterType: 'string', + isFilterable: true, + isMetricsFilter: true, + isMultiSelect: true, + name: 'Database Clusters', + neededInViews: [CloudPulseAvailableViews.central], + placeholder: 'Select Database Clusters', + priority: 3, + }, + name: 'Resources', + }; + it('should return empty object if config has no dependencies', () => { + const dependentFilters = { engine: 'mysql', region: 'us-east' }; + const result = filterBasedOnConfig(config, dependentFilters); + expect(result).toEqual({}); + }); + + it('should return filtered values based on dependency keys', () => { + const dependentFilters = { + engine: 'mysql', + region: 'us-east', + status: 'running', + }; + const result = filterBasedOnConfig( + { + ...config, + configuration: { + ...config.configuration, + dependency: ['engine', 'status'], + }, + }, + dependentFilters + ); + expect(result).toEqual({ + engineType: 'mysql', + status: 'running', + }); + }); + + it('should work with array values in filters', () => { + const dependentFilters = { + engine: 'mysql', + tags: ['db', 'prod'], + }; + const result = filterBasedOnConfig( + { + ...config, + configuration: { + ...config.configuration, + dependency: ['engine', 'tags'], + }, + }, + dependentFilters + ); + expect(result).toEqual({ + engineType: 'mysql', + tags: ['db', 'prod'], + }); + }); +}); diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts index f05ae918ec6..484cb68ea55 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts @@ -9,7 +9,10 @@ import { import { FILTER_CONFIG } from './FilterConfig'; import { CloudPulseAvailableViews, CloudPulseSelectTypes } from './models'; -import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding'; +import type { + CloudPulseMetricsFilter, + FilterValueType, +} from '../Dashboard/CloudPulseDashboardLanding'; import type { CloudPulseCustomSelectProps } from '../shared/CloudPulseCustomSelect'; import type { CloudPulseNodeTypeFilterProps } from '../shared/CloudPulseNodeTypeFilter'; import type { CloudPulseRegionSelectProps } from '../shared/CloudPulseRegionSelect'; @@ -29,7 +32,6 @@ import type { AclpConfig, Dashboard, DateTimeWithPreset, - Filter, Filters, TimeDuration, } from '@linode/api-v4'; @@ -37,21 +39,19 @@ import type { interface CloudPulseFilterProperties { config: CloudPulseServiceTypeFilters; dashboard: Dashboard; - dependentFilters?: { - [key: string]: FilterValueType; - }; + dependentFilters?: CloudPulseMetricsFilter; isServiceAnalyticsIntegration: boolean; preferences?: AclpConfig; resource_ids?: number[] | undefined; + shouldDisable?: boolean; } interface CloudPulseMandatoryFilterCheckProps { dashboard: Dashboard; - filterValue: { - [key: string]: FilterValueType; - }; + filterValue: CloudPulseMetricsFilter; timeDuration: DateTimeWithPreset | undefined; } + /** * This function helps in building the properties needed for tags selection component * @@ -115,6 +115,7 @@ export const getRegionProperties = ( preferences, dependentFilters, config, + shouldDisable, } = props; return { defaultValue: preferences?.[REGION], @@ -123,12 +124,14 @@ export const getRegionProperties = ( placeholder, savePreferences: !isServiceAnalyticsIntegration, selectedDashboard: dashboard, - disabled: shouldDisableFilterByFilterKey( - filterKey, - dependentFilters ?? {}, - dashboard - ), - xFilter: buildXFilter(config, dependentFilters ?? {}), + disabled: + shouldDisable || + shouldDisableFilterByFilterKey( + filterKey, + dependentFilters ?? {}, + dashboard + ), + xFilter: filterBasedOnConfig(config, dependentFilters ?? {}), }; }; @@ -156,21 +159,24 @@ export const getResourcesProperties = ( dependentFilters, isServiceAnalyticsIntegration, preferences, + shouldDisable, } = props; return { defaultValue: preferences?.[RESOURCES], - disabled: shouldDisableFilterByFilterKey( - filterKey, - dependentFilters ?? {}, - dashboard, - preferences - ), + disabled: + shouldDisable || + shouldDisableFilterByFilterKey( + filterKey, + dependentFilters ?? {}, + dashboard, + preferences + ), handleResourcesSelection: handleResourceChange, label, placeholder, resourceType: dashboard.service_type, savePreferences: !isServiceAnalyticsIntegration, - xFilter: buildXFilter(config, dependentFilters ?? {}), + xFilter: filterBasedOnConfig(config, dependentFilters ?? {}), }; }; @@ -189,15 +195,18 @@ export const getNodeTypeProperties = ( isServiceAnalyticsIntegration, preferences, resource_ids, + shouldDisable, } = props; return { database_ids: resource_ids, defaultValue: preferences?.[NODE_TYPE], - disabled: shouldDisableFilterByFilterKey( - filterKey, - dependentFilters ?? {}, - dashboard - ), + disabled: + shouldDisable || + shouldDisableFilterByFilterKey( + filterKey, + dependentFilters ?? {}, + dashboard + ), handleNodeTypeChange, label, placeholder, @@ -238,6 +247,7 @@ export const getCustomSelectProperties = ( dependentFilters, isServiceAnalyticsIntegration, preferences, + shouldDisable, } = props; return { apiResponseIdField: apiIdField, @@ -248,11 +258,13 @@ export const getCustomSelectProperties = ( dashboard ), defaultValue: preferences?.[filterKey], - disabled: shouldDisableFilterByFilterKey( - filterKey, - dependentFilters ?? {}, - dashboard - ), + disabled: + shouldDisable || + shouldDisableFilterByFilterKey( + filterKey, + dependentFilters ?? {}, + dashboard + ), filterKey, filterType, isOptional, @@ -323,14 +335,17 @@ export const getTextFilterProperties = ( isServiceAnalyticsIntegration, preferences, dependentFilters, + shouldDisable, } = props; return { - disabled: shouldDisableFilterByFilterKey( - props.config.configuration.filterKey, - dependentFilters ?? {}, - dashboard - ), + disabled: + shouldDisable || + shouldDisableFilterByFilterKey( + props.config.configuration.filterKey, + dependentFilters ?? {}, + dashboard + ), defaultValue: preferences?.[props.config.configuration.filterKey], handleTextFilterChange, label, @@ -346,34 +361,29 @@ export const getTextFilterProperties = ( * * @param config - any cloudpulse service type filter config * @param dependentFilters - the filters that are selected so far - * @returns - a xFilter type of apiV4 + * @returns - filtered dependencies based on the provided config */ -export const buildXFilter = ( - config: CloudPulseServiceTypeFilters, - dependentFilters: { - [key: string]: FilterValueType | TimeDuration; +export const filterBasedOnConfig = ( + config: CloudPulseServiceTypeFilters | undefined, + dependentFilters: CloudPulseMetricsFilter +): CloudPulseMetricsFilter => { + if (!config) { + return {}; } -): Filter => { - const filters: Filter[] = []; - let orCondition: Filter[] = []; const { dependency } = config.configuration; + const filtered: CloudPulseMetricsFilter = {}; if (dependency) { dependency.forEach((key) => { const value = dependentFilters[key]; if (value !== undefined) { - if (Array.isArray(value)) { - orCondition = value.map((val) => ({ [key]: val })); - } else { - filters.push({ [key]: value }); - } + filtered[key === 'engine' ? 'engineType' : key] = value; } }); + return filtered; } - if (orCondition.length) { - return { '+and': filters, '+or': orCondition }; - } - return { '+and': filters }; + + return {}; }; /** @@ -471,9 +481,7 @@ export const checkIfAllMandatoryFiltersAreSelected = ( * @returns Constructs and returns the metrics call filters based on selected filters and service type */ export const getMetricsCallCustomFilters = ( - selectedFilters: { - [key: string]: FilterValueType; - }, + selectedFilters: CloudPulseMetricsFilter, dashboardId?: number ): CloudPulseMetricsAdditionalFilters[] => { const serviceTypeConfig = dashboardId @@ -626,10 +634,38 @@ export const getFilters = ( return FILTER_CONFIG.get(dashboard.id)?.filters.filter((config) => isServiceAnalyticsIntegration ? config.configuration.neededInViews.includes( - CloudPulseAvailableViews.service - ) + CloudPulseAvailableViews.service + ) : config.configuration.neededInViews.includes( - CloudPulseAvailableViews.central - ) + CloudPulseAvailableViews.central + ) ); }; + +/** + * @param data The resources for which the filter needs to be applied + * @param dependentFilters The selected dependent filters that will be used to filter the resources + * @returns The filtered resources + */ +export const filterUsingDependentFilters = ( + data?: CloudPulseResources[], + dependentFilters?: CloudPulseMetricsFilter +): CloudPulseResources[] | undefined => { + if (!dependentFilters || !data) { + return data; + } + + return data.filter((resource) => { + return Object.entries(dependentFilters).every(([key, filterValue]) => { + const resourceValue = resource[key as keyof CloudPulseResources]; + + if (Array.isArray(resourceValue) && Array.isArray(filterValue)) { + return filterValue.some((val) => resourceValue.includes(String(val))); + } else if (Array.isArray(resourceValue)) { + return resourceValue.includes(String(filterValue)); + } else { + return resourceValue === filterValue; + } + }); + }); +}; diff --git a/packages/manager/src/features/CloudPulse/Utils/ReusableDashboardFilterUtils.ts b/packages/manager/src/features/CloudPulse/Utils/ReusableDashboardFilterUtils.ts index 3cc7ed5d6f5..10c2c59258f 100644 --- a/packages/manager/src/features/CloudPulse/Utils/ReusableDashboardFilterUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/ReusableDashboardFilterUtils.ts @@ -3,7 +3,7 @@ import { FILTER_CONFIG } from './FilterConfig'; import { CloudPulseAvailableViews } from './models'; import type { DashboardProperties } from '../Dashboard/CloudPulseDashboard'; -import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding'; +import type { CloudPulseMetricsFilter } from '../Dashboard/CloudPulseDashboardLanding'; import type { CloudPulseMetricsAdditionalFilters } from '../Widget/CloudPulseWidget'; import type { Dashboard, DateTimeWithPreset } from '@linode/api-v4'; @@ -18,7 +18,7 @@ interface ReusableDashboardFilterUtilProps { /** * The selected filter values */ - filterValue: { [key: string]: FilterValueType }; + filterValue: CloudPulseMetricsFilter; /** * The selected resource id */ diff --git a/packages/manager/src/features/CloudPulse/Utils/constants.ts b/packages/manager/src/features/CloudPulse/Utils/constants.ts index aa154f7c086..e159fbbb11d 100644 --- a/packages/manager/src/features/CloudPulse/Utils/constants.ts +++ b/packages/manager/src/features/CloudPulse/Utils/constants.ts @@ -1,3 +1,5 @@ +import type { Filter } from '@linode/api-v4'; + export const DASHBOARD_ID = 'dashboardId'; export const PRIMARY_NODE = 'primary'; @@ -88,3 +90,27 @@ export const PLACEHOLDER_TEXT: Record = { [PORT]: PORTS_PLACEHOLDER_TEXT, [INTERFACE_ID]: INTERFACE_IDS_PLACEHOLDER_TEXT, }; + +export const ORDER_BY_LABLE_ASC = { + '+order': 'asc', + '+order_by': 'label', +}; + +export const RESOURCE_FILTER_MAP: Record = { + dbaas: { + platform: 'rdbms-default', + ...ORDER_BY_LABLE_ASC, + }, + linode: { + ...ORDER_BY_LABLE_ASC, + }, + nodebalancer: { + ...ORDER_BY_LABLE_ASC, + }, + firewall: { + ...ORDER_BY_LABLE_ASC, + }, + netloadbalancer: { + ...ORDER_BY_LABLE_ASC, + }, +}; diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx index ec7d18701d9..8c7555c12d3 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx @@ -165,7 +165,7 @@ export const CloudPulseCustomSelect = React.memo( }); React.useEffect(() => { - if (!selectedResource) { + if (!selectedResource && !disabled) { setResource( getInitialDefaultSelections({ defaultValue, @@ -180,7 +180,7 @@ export const CloudPulseCustomSelect = React.memo( ); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [savePreferences, options, apiV4QueryKey, queriedResources]); // only execute this use efffect one time or if savePreferences or options or dataApiUrl changes + }, [savePreferences, options, apiV4QueryKey, queriedResources, disabled]); // only execute this use efffect one time or if savePreferences or options or dataApiUrl changes const handleChange = ( _: React.SyntheticEvent, diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardFilterBuilder.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardFilterBuilder.tsx index b87b9bf0063..6a4498cb168 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardFilterBuilder.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardFilterBuilder.tsx @@ -1,4 +1,4 @@ -import { Button, ErrorState, Typography } from '@linode/ui'; +import { Button, CircleProgress, ErrorState, Typography } from '@linode/ui'; import { GridLegacy, useTheme } from '@mui/material'; import * as React from 'react'; @@ -30,7 +30,10 @@ import { import { FILTER_CONFIG } from '../Utils/FilterConfig'; import { type CloudPulseServiceTypeFilters } from '../Utils/models'; -import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding'; +import type { + CloudPulseMetricsFilter, + FilterValueType, +} from '../Dashboard/CloudPulseDashboardLanding'; import type { CloudPulseResources } from './CloudPulseResourcesSelect'; import type { CloudPulseTags } from './CloudPulseTagsFilter'; import type { AclpConfig, Dashboard } from '@linode/api-v4'; @@ -55,6 +58,16 @@ export interface CloudPulseDashboardFilterBuilderProps { handleToggleAppliedFilter: (isVisible: boolean) => void; + /** + * Is cluster Call + */ + isError?: boolean; + + /** + * Property to disable filters + */ + isLoading?: boolean; + /** * this will handle the restrictions, if the parent of the component is going to be integrated in service analytics page */ @@ -80,19 +93,18 @@ export const CloudPulseDashboardFilterBuilder = React.memo( isServiceAnalyticsIntegration, preferences, resource_ids, + isError = false, + isLoading = false, } = props; - const [, setDependentFilters] = React.useState<{ - [key: string]: FilterValueType; - }>({}); + const [, setDependentFilters] = React.useState({}); const [showFilter, setShowFilter] = React.useState(true); const theme = useTheme(); - const dependentFilterReference: React.MutableRefObject<{ - [key: string]: FilterValueType; - }> = React.useRef({}); + const dependentFilterReference: React.MutableRefObject = + React.useRef({}); const checkAndUpdateDependentFilters = React.useCallback( (filterKey: string, value: FilterValueType) => { @@ -261,6 +273,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( dependentFilters: dependentFilterReference.current, isServiceAnalyticsIntegration, preferences, + shouldDisable: isError || isLoading, }, handleTagsChange ); @@ -272,6 +285,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( isServiceAnalyticsIntegration, preferences, dependentFilters: dependentFilterReference.current, + shouldDisable: isError || isLoading, }, handleRegionChange ); @@ -283,6 +297,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( dependentFilters: dependentFilterReference.current, isServiceAnalyticsIntegration, preferences, + shouldDisable: isError || isLoading, }, handleResourceChange ); @@ -301,6 +316,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( : ( dependentFilterReference.current[RESOURCE_ID] as string[] )?.map((id: string) => Number(id)), + shouldDisable: isError || isLoading, }, handleNodeTypeChange ); @@ -317,6 +333,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( dependentFilters: resource_ids?.length ? { [RESOURCE_ID]: resource_ids } : dependentFilterReference.current, + shouldDisable: isError || isLoading, }, handleTextFilterChange ); @@ -330,6 +347,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo( : dependentFilterReference.current, isServiceAnalyticsIntegration, preferences, + shouldDisable: isError || isLoading, }, handleCustomSelectChange ); @@ -345,6 +363,8 @@ export const CloudPulseDashboardFilterBuilder = React.memo( handleCustomSelectChange, isServiceAnalyticsIntegration, preferences, + isError, + isLoading, ] ); @@ -438,21 +458,32 @@ export const CloudPulseDashboardFilterBuilder = React.memo( Filters - - - + {isLoading ? ( + + + + ) : ( + + + + )} ); }, @@ -466,6 +497,8 @@ function compareProps( return ( oldProps.dashboard?.id === newProps.dashboard?.id && oldProps.preferences?.[DASHBOARD_ID] === - newProps.preferences?.[DASHBOARD_ID] + newProps.preferences?.[DASHBOARD_ID] && + oldProps.isLoading === newProps.isLoading && + oldProps.isError === newProps.isError ); } diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.test.tsx index 26806cf2dcb..09323742ef8 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.test.tsx @@ -20,14 +20,14 @@ const props: CloudPulseNodeTypeFilterProps = { }; const queryMocks = vi.hoisted(() => ({ - useAllDatabasesQuery: vi.fn().mockReturnValue({}), + useResourcesQuery: vi.fn().mockReturnValue({}), })); -vi.mock('@linode/queries', async () => { - const actual = await vi.importActual('@linode/queries'); +vi.mock('src/queries/cloudpulse/resources', async () => { + const actual = await vi.importActual('src/queries/cloudpulse/resources'); return { ...actual, - useAllDatabasesQuery: queryMocks.useAllDatabasesQuery, + useResourcesQuery: queryMocks.useResourcesQuery, }; }); @@ -50,8 +50,8 @@ describe('CloudPulseNodeTypeFilter', () => { }); it('initializes with Primary as default value when no preferences are saved', async () => { - queryMocks.useAllDatabasesQuery.mockReturnValue({ - data: databaseInstanceFactory.buildList(2), + queryMocks.useResourcesQuery.mockReturnValue({ + data: [{ ...databaseInstanceFactory.build(), clusterSize: 1 }], isError: false, isLoading: false, }); @@ -67,10 +67,16 @@ describe('CloudPulseNodeTypeFilter', () => { }); it('displays correct options in dropdown in case of maximum cluster size one', async () => { - queryMocks.useAllDatabasesQuery.mockReturnValue({ + queryMocks.useResourcesQuery.mockReturnValue({ data: [ - databaseInstanceFactory.build({ cluster_size: 1, id: 1 }), - databaseInstanceFactory.build({ cluster_size: 1, id: 2 }), + { + ...databaseInstanceFactory.build({ id: 1 }), + clusterSize: 1, + }, + { + ...databaseInstanceFactory.build({ id: 2 }), + clusterSize: 1, + }, ], isError: false, isLoading: false, @@ -87,10 +93,16 @@ describe('CloudPulseNodeTypeFilter', () => { }); it('displays correct options in dropdown if maximum cluster size is greater than one', async () => { - queryMocks.useAllDatabasesQuery.mockReturnValue({ + queryMocks.useResourcesQuery.mockReturnValue({ data: [ - databaseInstanceFactory.build({ cluster_size: 2, id: 1 }), - databaseInstanceFactory.build({ cluster_size: 3, id: 2 }), + { + ...databaseInstanceFactory.build({ id: 1 }), + clusterSize: 2, + }, + { + ...databaseInstanceFactory.build({ id: 2 }), + clusterSize: 3, + }, ], isError: false, isLoading: false, @@ -116,10 +128,16 @@ describe('CloudPulseNodeTypeFilter', () => { }); it('maintains selected value in preferences after re-render', async () => { - queryMocks.useAllDatabasesQuery.mockReturnValue({ + queryMocks.useResourcesQuery.mockReturnValue({ data: [ - databaseInstanceFactory.build({ cluster_size: 1, id: 1 }), - databaseInstanceFactory.build({ cluster_size: 3, id: 2 }), + { + ...databaseInstanceFactory.build({ id: 1 }), + clusterSize: 1, + }, + { + ...databaseInstanceFactory.build({ id: 2 }), + clusterSize: 3, + }, ], isError: false, isLoading: false, diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.tsx index cda1d448278..9f26fc63a11 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseNodeTypeFilter.tsx @@ -1,10 +1,11 @@ -import { useAllDatabasesQuery } from '@linode/queries'; import { Autocomplete } from '@linode/ui'; import * as React from 'react'; -import { PRIMARY_NODE } from '../Utils/constants'; +import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; -import type { DatabaseInstance, FilterValue } from '@linode/api-v4'; +import { PRIMARY_NODE, RESOURCE_FILTER_MAP } from '../Utils/constants'; + +import type { FilterValue } from '@linode/api-v4'; export interface CloudPulseNodeType { id: string; @@ -88,7 +89,12 @@ export const CloudPulseNodeTypeFilter = React.memo( data: databaseClusters, isError, isLoading, - } = useAllDatabasesQuery(); // fetch all databases + } = useResourcesQuery( + !disabled, + 'dbaas', + {}, + RESOURCE_FILTER_MAP['dbaas'] ?? {} + ); // fetch all databases const isClusterSizeGreaterThanOne = React.useMemo< boolean | undefined @@ -98,8 +104,8 @@ export const CloudPulseNodeTypeFilter = React.memo( } // check if any cluster has a size greater than 1 for selected database ids return databaseClusters.some( - (cluster: DatabaseInstance) => - database_ids.includes(cluster.id) && cluster.cluster_size > 1 + ({ id, clusterSize }) => + database_ids.includes(Number(id)) && clusterSize && clusterSize > 1 ); }, [databaseClusters, database_ids]); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index b385b4cf223..560ac92afc9 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -7,11 +7,12 @@ import { useFlags } from 'src/hooks/useFlags'; import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; import { filterRegionByServiceType } from '../Alerts/Utils/utils'; -import { NO_REGION_MESSAGE } from '../Utils/constants'; -import { deepEqual } from '../Utils/FilterBuilder'; +import { NO_REGION_MESSAGE, RESOURCE_FILTER_MAP } from '../Utils/constants'; +import { deepEqual, filterUsingDependentFilters } from '../Utils/FilterBuilder'; import { FILTER_CONFIG } from '../Utils/FilterConfig'; -import type { Dashboard, Filter, FilterValue, Region } from '@linode/api-v4'; +import type { CloudPulseMetricsFilter } from '../Dashboard/CloudPulseDashboardLanding'; +import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; export interface CloudPulseRegionSelectProps { defaultValue?: FilterValue; @@ -25,7 +26,7 @@ export interface CloudPulseRegionSelectProps { placeholder?: string; savePreferences?: boolean; selectedDashboard: Dashboard | undefined; - xFilter?: Filter; + xFilter?: CloudPulseMetricsFilter; } export const CloudPulseRegionSelect = React.memo( @@ -41,24 +42,17 @@ export const CloudPulseRegionSelect = React.memo( xFilter, } = props; - const resourceFilterMap: Record = { - dbaas: { - platform: 'rdbms-default', - }, - }; - const { data: regions, isError, isLoading } = useRegionsQuery(); const { data: resources, isError: isResourcesError, isLoading: isResourcesLoading, } = useResourcesQuery( - selectedDashboard !== undefined && Boolean(regions?.length), + !disabled && selectedDashboard !== undefined && Boolean(regions?.length), selectedDashboard?.service_type, {}, { - ...(resourceFilterMap[selectedDashboard?.service_type ?? ''] ?? {}), - ...(xFilter ?? {}), // the usual xFilters + ...(RESOURCE_FILTER_MAP[selectedDashboard?.service_type ?? ''] ?? {}), } ); @@ -111,7 +105,9 @@ export const CloudPulseRegionSelect = React.memo( }, [regions, serviceType]); const supportedRegionsFromResources = supportedRegions?.filter(({ id }) => - resources?.some(({ region }) => region === id) + filterUsingDependentFilters(resources, xFilter)?.some( + ({ region }) => region === id + ) ); return ( @@ -128,7 +124,7 @@ export const CloudPulseRegionSelect = React.memo( fullWidth isGeckoLAEnabled={isGeckoLAEnabled} label={label || 'Region'} - loading={isLoading || isResourcesLoading} + loading={!disabled && (isLoading || isResourcesLoading)} noMarginTop noOptionsText={ NO_REGION_MESSAGE[selectedDashboard?.service_type ?? ''] ?? diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index 034f0aec38c..c95f20d2236 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -5,15 +5,14 @@ import React from 'react'; import { useFlags } from 'src/hooks/useFlags'; import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; -import { deepEqual } from '../Utils/FilterBuilder'; +import { RESOURCE_FILTER_MAP } from '../Utils/constants'; +import { deepEqual, filterUsingDependentFilters } from '../Utils/FilterBuilder'; -import type { - CloudPulseServiceType, - Filter, - FilterValue, -} from '@linode/api-v4'; +import type { CloudPulseMetricsFilter } from '../Dashboard/CloudPulseDashboardLanding'; +import type { CloudPulseServiceType, FilterValue } from '@linode/api-v4'; export interface CloudPulseResources { + clusterSize?: number; engineType?: string; entities?: Record; id: string; @@ -35,7 +34,7 @@ export interface CloudPulseResourcesSelectProps { resourceType: CloudPulseServiceType | undefined; savePreferences?: boolean; tags?: string[]; - xFilter?: Filter; + xFilter?: CloudPulseMetricsFilter; } export const CloudPulseResourcesSelect = React.memo( @@ -49,20 +48,11 @@ export const CloudPulseResourcesSelect = React.memo( region, resourceType, savePreferences, - tags, xFilter, } = props; const flags = useFlags(); - const resourceFilterMap: Record = { - dbaas: { - '+order': 'asc', - '+order_by': 'label', - platform: 'rdbms-default', - }, - }; - const { data: resources, isError, @@ -71,16 +61,8 @@ export const CloudPulseResourcesSelect = React.memo( disabled !== undefined ? !disabled : Boolean(region && resourceType), resourceType, {}, - xFilter - ? { - ...(resourceFilterMap[resourceType ?? ''] ?? {}), - ...xFilter, // the usual xFilters - } - : { - ...(resourceFilterMap[resourceType ?? ''] ?? {}), - region, - ...(tags ?? []), - } + + RESOURCE_FILTER_MAP[resourceType ?? ''] ?? {} ); const [selectedResources, setSelectedResources] = @@ -94,8 +76,8 @@ export const CloudPulseResourcesSelect = React.memo( const isAutocompleteOpen = React.useRef(false); // Ref to track the open state of Autocomplete const getResourcesList = React.useMemo(() => { - return resources && resources.length > 0 ? resources : []; - }, [resources]); + return filterUsingDependentFilters(resources, xFilter) ?? []; + }, [resources, xFilter]); // Maximum resource selection limit is fetched from launchdarkly const maxResourceSelectionLimit = React.useMemo(() => { diff --git a/packages/manager/src/queries/cloudpulse/queries.ts b/packages/manager/src/queries/cloudpulse/queries.ts index 9fb946629a7..d53fedbfb23 100644 --- a/packages/manager/src/queries/cloudpulse/queries.ts +++ b/packages/manager/src/queries/cloudpulse/queries.ts @@ -115,7 +115,7 @@ export const queryFactory = createQueryKeys(key, { case 'dbaas': return databaseQueries.databases._ctx.all(params, filters); case 'firewall': - return firewallQueries.firewalls._ctx.all; + return firewallQueries.firewalls._ctx.all(params, filters); case 'linode': return { queryFn: () => getAllLinodesRequest(params, filters), // since we don't have query factory implementation, in linodes.ts, once it is ready we will reuse that, untill then we will use same query keys diff --git a/packages/manager/src/queries/cloudpulse/resources.ts b/packages/manager/src/queries/cloudpulse/resources.ts index 2cd9b1d8164..a530fe8a6cb 100644 --- a/packages/manager/src/queries/cloudpulse/resources.ts +++ b/packages/manager/src/queries/cloudpulse/resources.ts @@ -36,6 +36,7 @@ export const useResourcesQuery = ( regions: resource.regions ? resource.regions : [], tags: resource.tags, entities, + clusterSize: resource.cluster_size, }; }); }, diff --git a/packages/queries/src/firewalls/firewalls.ts b/packages/queries/src/firewalls/firewalls.ts index ceed713cfc0..09ab77e9543 100644 --- a/packages/queries/src/firewalls/firewalls.ts +++ b/packages/queries/src/firewalls/firewalls.ts @@ -62,9 +62,15 @@ const getAllFirewallDevices = ( const getAllFirewallTemplates = () => getAll(getTemplates)().then((data) => data.data); -const getAllFirewallsRequest = () => - getAll((passedParams, passedFilter) => - getFirewalls(passedParams, passedFilter), +const getAllFirewallsRequest = ( + passedParams: Params = {}, + passedFilter: Filter = {}, +) => + getAll((params, filter) => + getFirewalls( + { ...params, ...passedParams }, + { ...filter, ...passedFilter }, + ), )().then((data) => data.data); export const firewallQueries = createQueryKeys('firewalls', { @@ -80,10 +86,10 @@ export const firewallQueries = createQueryKeys('firewalls', { }), firewalls: { contextQueries: { - all: { - queryFn: getAllFirewallsRequest, - queryKey: null, - }, + all: (params: Params = {}, filter: Filter = {}) => ({ + queryFn: () => getAllFirewallsRequest(params, filter), + queryKey: [params, filter], + }), infinite: (filter: Filter = {}) => ({ queryFn: ({ pageParam }) => getFirewalls({ page: pageParam as number }, filter), @@ -176,7 +182,7 @@ export const useAddFirewallDeviceMutation = () => { // Append the new entity to the Firewall object in the "all firewalls" store queryClient.setQueryData( - firewallQueries.firewalls._ctx.all.queryKey, + firewallQueries.firewalls._ctx.all().queryKey, (firewalls) => { if (!firewalls) { return undefined; @@ -305,9 +311,13 @@ export const useFirewallQuery = (id: number, enabled: boolean = true) => enabled, }); -export const useAllFirewallsQuery = (enabled: boolean = true) => { +export const useAllFirewallsQuery = ( + enabled: boolean = true, + params?: Params, + filter?: Filter, +) => { return useQuery({ - ...firewallQueries.firewalls._ctx.all, + ...firewallQueries.firewalls._ctx.all(params, filter), enabled, }); }; @@ -450,7 +460,7 @@ export const useUpdateFirewallRulesMutation = (firewallId: number) => { // Update the the Firewall object in the "all firewalls" store queryClient.setQueryData( - firewallQueries.firewalls._ctx.all.queryKey, + firewallQueries.firewalls._ctx.all().queryKey, (firewalls) => { if (!firewalls) { return undefined;