Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

CloudPulse-Metrics: Add optional-filter component at `CloudPulseFirewallNodebalancersSelect.tsx` and integrate it with existing firewall-nodebalancer filters ([#13029](https://github.com/linode/manager/pull/13029))
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {

import { RESOURCE_FILTER_MAP } from '../Utils/constants';
import { useAclpPreference } from '../Utils/UserPreference';
import { getResourcesFilterConfig } from '../Utils/utils';
import {
getAssociatedEntityType,
getResourcesFilterConfig,
} from '../Utils/utils';
import {
renderPlaceHolder,
RenderWidgets,
Expand Down Expand Up @@ -114,9 +117,9 @@ export const CloudPulseDashboard = (props: DashboardProperties) => {

// Get the resources filter configuration for the dashboard
const resourcesFilterConfig = getResourcesFilterConfig(dashboardId);
const associatedEntityType =
resourcesFilterConfig?.associatedEntityType ?? 'both';
const filterFn = resourcesFilterConfig?.filterFn;
// Get the associated entity type for the dashboard
const associatedEntityType = getAssociatedEntityType(dashboardId);

const {
data: resourceList,
Expand Down
124 changes: 121 additions & 3 deletions packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { databaseQueries } from '@linode/queries';
import { nodeBalancerFactory } from '@linode/utilities';
import { DateTime } from 'luxon';

import {
Expand All @@ -12,9 +13,11 @@
deepEqual,
filterBasedOnConfig,
filterEndpointsUsingRegion,
filterFirewallNodebalancers,
filterUsingDependentFilters,
getEndpointsProperties,
getFilters,
getFirewallNodebalancersProperties,
getTextFilterProperties,
} from './FilterBuilder';
import {
Expand Down Expand Up @@ -43,7 +46,9 @@

const nodeBalancerConfig = FILTER_CONFIG.get(3);

const firewallConfig = FILTER_CONFIG.get(4);
const linodeFirewallConfig = FILTER_CONFIG.get(4);

const nodebalancerFirewallConfig = FILTER_CONFIG.get(8);

const dbaasDashboard = dashboardFactory.build({ service_type: 'dbaas', id: 1 });

Expand Down Expand Up @@ -135,7 +140,7 @@
});

it('test getResourceSelectionProperties method for linode-firewall', () => {
const resourceSelectionConfig = firewallConfig?.filters.find(
const resourceSelectionConfig = linodeFirewallConfig?.filters.find(
(filterObj) => filterObj.name === 'Firewalls'
);

Expand Down Expand Up @@ -426,7 +431,7 @@
});

it('test getTextFilterProperties method for interface_id', () => {
const interfaceIdFilterConfig = firewallConfig?.filters.find(
const interfaceIdFilterConfig = linodeFirewallConfig?.filters.find(
(filterObj) => filterObj.name === 'Interface IDs'
);

Expand Down Expand Up @@ -488,6 +493,49 @@
expect(xFilter).toEqual({ region: 'us-east' });
}
});
it('test getFirewallNodebalancersProperties', () => {
const nodebalancersConfig = nodebalancerFirewallConfig?.filters.find(
(filterObj) => filterObj.name === 'NodeBalancers'
);

expect(nodebalancersConfig).toBeDefined();

if (nodebalancersConfig) {
const nodebalancersProperties = getFirewallNodebalancersProperties(
{
config: nodebalancersConfig,
dashboard: dashboardFactory.build({ service_type: 'firewall', id: 8 }),
dependentFilters: {
resource_id: '1',
associated_entity_region: 'us-east',
},
isServiceAnalyticsIntegration: false,
},
vi.fn()
);
const {
label,
disabled,
selectedDashboard,
savePreferences,
handleNodebalancersSelection,
defaultValue,
xFilter,
} = nodebalancersProperties;

expect(nodebalancersProperties).toBeDefined();
expect(label).toEqual(nodebalancersConfig.configuration.name);
expect(selectedDashboard.service_type).toEqual('firewall');
expect(savePreferences).toEqual(true);
expect(disabled).toEqual(false);
expect(handleNodebalancersSelection).toBeDefined();
expect(defaultValue).toEqual(undefined);
expect(xFilter).toEqual({
resource_id: '1',
associated_entity_region: 'us-east',
});
}
});

it('test getFiltersForMetricsCallFromCustomSelect method', () => {
const result = getMetricsCallCustomFilters(
Expand Down Expand Up @@ -669,6 +717,76 @@
});
});

describe('filterFirewallNodebalancers', () => {
const mockData = [
nodeBalancerFactory.build({
id: 1,
label: 'nodebalancer-1',

Check warning on line 724 in packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":724,"column":14,"nodeType":"Literal","endLine":724,"endColumn":30}
region: 'us-east',
}),
nodeBalancerFactory.build({
id: 2,
label: 'nodebalancer-2',
region: 'us-west',
}),
];
const mockFirewalls: CloudPulseResources[] = [
{
id: '1',
label: 'firewall-1',
entities: { '1': 'nodebalancer-1' },
},
];

it('should return undefined if data is undefined', () => {
expect(
filterFirewallNodebalancers(
undefined,
{ associated_entity_region: 'us-east', resource_id: '1' },
mockFirewalls
)
).toEqual(undefined);
});

it('should return undefined if xFilter/firewalls is empty or undefined', () => {
const result = filterFirewallNodebalancers(
mockData,
undefined,
mockFirewalls
);
const result2 = filterFirewallNodebalancers(mockData, {}, mockFirewalls);
const result3 = filterFirewallNodebalancers(
mockData,
{ associated_entity_region: 'us-east', resource_id: '1' },
[]
);
const result4 = filterFirewallNodebalancers(
mockData,
{ associated_entity_region: 'us-east', resource_id: '1' },
undefined
);
expect(result).toEqual(undefined);
expect(result2).toEqual(undefined);
expect(result3).toEqual(undefined);
expect(result4).toEqual(undefined);
});

it('should filter nodebalancers based on xFilter', () => {
const result = filterFirewallNodebalancers(
mockData,
{ associated_entity_region: 'us-east', resource_id: '1' },
mockFirewalls
);
expect(result).toEqual([
{
id: '1',
label: 'nodebalancer-1',
associated_entity_region: 'us-east',
},
]);
});
});

describe('filterBasedOnConfig', () => {
const config: CloudPulseServiceTypeFilters = {
configuration: {
Expand Down
91 changes: 90 additions & 1 deletion packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from './constants';
import { FILTER_CONFIG } from './FilterConfig';
import { CloudPulseAvailableViews, CloudPulseSelectTypes } from './models';
import { getAssociatedEntityType } from './utils';

import type {
CloudPulseMetricsFilter,
Expand All @@ -16,6 +17,10 @@ import type {
import type { CloudPulseCustomSelectProps } from '../shared/CloudPulseCustomSelect';
import type { CloudPulseEndpointsSelectProps } from '../shared/CloudPulseEndpointsSelect';
import type { CloudPulseEndpoints } from '../shared/CloudPulseEndpointsSelect';
import type {
CloudPulseFirewallNodebalancersSelectProps,
CloudPulseNodebalancers,
} from '../shared/CloudPulseFirewallNodebalancersSelect';
import type { CloudPulseNodeTypeFilterProps } from '../shared/CloudPulseNodeTypeFilter';
import type { CloudPulseRegionSelectProps } from '../shared/CloudPulseRegionSelect';
import type {
Expand All @@ -36,6 +41,7 @@ import type {
Dashboard,
DateTimeWithPreset,
Filters,
NodeBalancer,
TimeDuration,
} from '@linode/api-v4';

Expand Down Expand Up @@ -183,7 +189,7 @@ export const getResourcesProperties = (
resourceType: dashboard.service_type,
savePreferences: !isServiceAnalyticsIntegration,
xFilter: filterBasedOnConfig(config, dependentFilters ?? {}),
associatedEntityType: config.configuration.associatedEntityType ?? 'both',
associatedEntityType: getAssociatedEntityType(dashboard.id),
filterFn: config.configuration.filterFn,
};
};
Expand Down Expand Up @@ -408,6 +414,47 @@ export const getEndpointsProperties = (
};
};

/**
*
* @param props The cloudpulse filter properties selected so far
* @param handleFirewallNodebalancersChange The callback function when selection of nodebalancers changes
* @returns CloudPulseFirewallNodebalancersSelectProps
*/
export const getFirewallNodebalancersProperties = (
props: CloudPulseFilterProperties,
handleFirewallNodebalancersChange: (
nodebalancers: CloudPulseNodebalancers[],
savePref?: boolean
) => void
): CloudPulseFirewallNodebalancersSelectProps => {
const { filterKey, name: label, placeholder } = props.config.configuration;
const {
config,
dashboard,
dependentFilters,
isServiceAnalyticsIntegration,
preferences,
shouldDisable,
} = props;
return {
defaultValue: preferences?.[config.configuration.filterKey],
selectedDashboard: dashboard,
disabled:
shouldDisable ||
shouldDisableFilterByFilterKey(
filterKey,
dependentFilters ?? {},
dashboard,
preferences
),
handleNodebalancersSelection: handleFirewallNodebalancersChange,
label,
placeholder,
savePreferences: !isServiceAnalyticsIntegration,
xFilter: filterBasedOnConfig(config, dependentFilters ?? {}),
isOptional: config.configuration.isOptional,
};
};
/**
* This function helps in builder the xFilter needed to passed in a apiV4 call
*
Expand Down Expand Up @@ -769,3 +816,45 @@ export const filterEndpointsUsingRegion = (

return data.filter(({ region }) => region === regionFromFilter);
};

/**
*
* @param data The nodebalancers for which the filter needs to be applied
* @param xFilter The selected filters that will be used to filter the nodebalancers
* @param firewalls The firewalls for which the filter needs to be applied
* @returns The filtered nodebalancers
*/

export const filterFirewallNodebalancers = (
data?: NodeBalancer[],
xFilter?: CloudPulseMetricsFilter,
firewalls?: CloudPulseResources[]
): CloudPulseNodebalancers[] | undefined => {
// If data is undefined or xFilter/firewalls is undefined or empty, return undefined
if (!data || !xFilter || !Object.keys(xFilter).length || !firewalls?.length) {
return undefined;
}

// Map the nodebalancers to the CloudPulseNodebalancers interface
const nodebalancers: CloudPulseNodebalancers[] = data.map((nodebalancer) => ({
id: String(nodebalancer.id),
label: nodebalancer.label,
associated_entity_region: nodebalancer.region,
}));

const firewallObj = firewalls.find(
(firewall) => firewall.id === String(xFilter[RESOURCE_ID])
);

return nodebalancers.filter((nodebalancer) => {
return Object.entries(xFilter).every(([key, filterValue]) => {
// If the filter key is the resource id, check if the nodebalancer is associated with the selected firewall
if (key === RESOURCE_ID) {
return firewallObj?.entities?.[nodebalancer.id];
}
const nodebalancerValue =
nodebalancer[key as keyof CloudPulseNodebalancers];
return nodebalancerValue === filterValue;
});
});
};
22 changes: 22 additions & 0 deletions packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { queryFactory } from 'src/queries/cloudpulse/queries';
import {
ENDPOINT,
INTERFACE_IDS_PLACEHOLDER_TEXT,
NODEBALANCER_ID,
PARENT_ENTITY_REGION,
REGION,
RESOURCE_ID,
Expand Down Expand Up @@ -322,6 +323,7 @@ export const FIREWALL_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
},
],
serviceType: 'firewall',
associatedEntityType: 'linode',
};

export const FIREWALL_NODEBALANCER_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> =
Expand Down Expand Up @@ -362,8 +364,28 @@ export const FIREWALL_NODEBALANCER_CONFIG: Readonly<CloudPulseServiceTypeFilterM
},
name: 'NodeBalancer Region',
},
{
configuration: {
dependency: [PARENT_ENTITY_REGION, RESOURCE_ID],
filterKey: NODEBALANCER_ID,
filterType: 'string',
isFilterable: true,
isMetricsFilter: false,
isMultiSelect: true,
isOptional: true,
name: 'NodeBalancers',
neededInViews: [
CloudPulseAvailableViews.central,
CloudPulseAvailableViews.service,
],
placeholder: 'Select NodeBalancers',
priority: 3,
},
name: 'NodeBalancers',
},
],
serviceType: 'firewall',
associatedEntityType: 'nodebalancer',
};

export const OBJECTSTORAGE_CONFIG_BUCKET: Readonly<CloudPulseServiceTypeFilterMap> =
Expand Down
2 changes: 2 additions & 0 deletions packages/manager/src/features/CloudPulse/Utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const PARENT_ENTITY_REGION = 'associated_entity_region';

export const RESOURCES = 'resources';

export const NODEBALANCER_ID = 'nodebalancer_id';

export const INTERVAL = 'interval';

export const TIME_DURATION = 'dateTimeDuration';
Expand Down
Loading