Skip to content

Commit

Permalink
upcoming: [DI-20348] - Changes for Adding CloudPulseCustomSelect comp…
Browse files Browse the repository at this point in the history
…onent and Integration (#10807)

* upcoming: [DI-20348] - Changes for CloudPulseCustomSelect components and its Integration

* upcoming: [DI-20348] - Added changeset

* upcoming: [DI-20348] - Code refactoring changes

* upcoming: [DI-20348] - Code refactoring and method optimisations

* upcoming: [DI-20348] - Simplify code logic

* upcoming: [DI-20348] - More refactoring

* upcoming: [DI-20348] - Error message fix

* upcoming: [DI-20348] - Simplify checks

* upcoming: [DI-20348] - UT's for cloudpulsecustom select

* upcoming: [DI-20348] - UT updates

* upcoming: [DI-20348] - PR comments changes

* upcoming: [DI-20348] - Simple names for functions

* upcoming: [DI-20348] - JSDoc comments fix

* upcoming: [DI-20348] - Lint issue fix

* upcoming: [DI-20348] - code simplification

* upcoming: [DI-20348] - PR comments and changeset updates

* upcoming: [DI-20348] - ESlint issue fixes across files

* upcoming: [DI-20348] - ESlint issue fixes across files

* upcoming: [DI-20348] - formatting serverhandler

* upcoming: [DI-20348] - Test names updates

---------

Co-authored-by: vmangalr <vmangalr@akamai.com>
  • Loading branch information
venkymano-akamai and vmangalr authored Aug 27, 2024
1 parent e893f16 commit f26a99e
Show file tree
Hide file tree
Showing 14 changed files with 1,003 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add new CloudPulseCustomSelect component and integrate with the global filter builder ([#10807](https://github.com/linode/manager/pull/10807))
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import {
getIntervalIndex,
} from '../Widget/components/CloudPulseIntervalSelect';

import type { CloudPulseWidgetProperties } from '../Widget/CloudPulseWidget';
import type {
CloudPulseMetricsAdditionalFilters,
CloudPulseWidgetProperties,
} from '../Widget/CloudPulseWidget';
import type {
AvailableMetrics,
Dashboard,
Expand All @@ -31,6 +34,11 @@ import type {
} from '@linode/api-v4';

export interface DashboardProperties {
/**
* Apart from above explicit filters, any additional filters for metrics endpoint will go here
*/
additionalFilters?: CloudPulseMetricsAdditionalFilters[];

/**
* Id of the selected dashboard
*/
Expand Down Expand Up @@ -64,6 +72,7 @@ export interface DashboardProperties {

export const CloudPulseDashboard = (props: DashboardProperties) => {
const {
additionalFilters,
dashboardId,
duration,
manualRefreshTimeStamp,
Expand All @@ -81,6 +90,7 @@ export const CloudPulseDashboard = (props: DashboardProperties) => {
widget: Widgets
): CloudPulseWidgetProperties => {
const graphProp: CloudPulseWidgetProperties = {
additionalFilters,
ariaLabel: widget.label,
authToken: '',
availableMetrics: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { StyledPlaceholder } from 'src/features/StackScripts/StackScriptBase/Sta

import { GlobalFilters } from '../Overview/GlobalFilters';
import { REGION, RESOURCE_ID } from '../Utils/constants';
import { checkIfAllMandatoryFiltersAreSelected } from '../Utils/FilterBuilder';
import {
checkIfAllMandatoryFiltersAreSelected,
getMetricsCallCustomFilters,
} from '../Utils/FilterBuilder';
import { FILTER_CONFIG } from '../Utils/FilterConfig';
import { useLoadUserPreferences } from '../Utils/UserPreference';
import { CloudPulseDashboard } from './CloudPulseDashboard';
Expand Down Expand Up @@ -97,6 +100,10 @@ export const CloudPulseDashboardLanding = () => {

return (
<CloudPulseDashboard
additionalFilters={getMetricsCallCustomFilters(
filterValue,
dashboard.service_type
)}
region={
typeof filterValue[REGION] === 'string'
? (filterValue[REGION] as string)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { dashboardFactory } from 'src/factories';
import { databaseQueries } from 'src/queries/databases/databases';

import { RESOURCES } from './constants';
import {
buildXFilter,
checkIfAllMandatoryFiltersAreSelected,
checkIfWeNeedToDisableFilterByFilterKey,
constructAdditionalRequestFilters,
getCustomSelectProperties,
getMetricsCallCustomFilters,
getRegionProperties,
getResourcesProperties,
getTimeDurationProperties,
} from './FilterBuilder';
import { FILTER_CONFIG } from './FilterConfig';
import { CloudPulseSelectTypes } from './models';

const mockDashboard = dashboardFactory.build();

const linodeConfig = FILTER_CONFIG.get('linode');

const dbaasConfig = FILTER_CONFIG.get('dbaas');

it('test getRegionProperties method', () => {
const regionConfig = linodeConfig?.filters.find(
(filterObj) => filterObj.name === 'Region'
Expand Down Expand Up @@ -183,3 +191,76 @@ it('test checkIfAllMandatoryFiltersAreSelected method', () => {

expect(result).toEqual(false);
});

it('test getCustomSelectProperties method', () => {
const customSelectEngineConfig = dbaasConfig?.filters.find(
(filterObj) => filterObj.name === 'DB Engine'
);

expect(customSelectEngineConfig).toBeDefined();

if (customSelectEngineConfig) {
let result = getCustomSelectProperties(
{
config: customSelectEngineConfig,
dashboard: { ...mockDashboard, service_type: 'dbaas' },
isServiceAnalyticsIntegration: true,
},
vi.fn()
);

expect(result.options).toBeDefined();
expect(result.options?.length).toEqual(2);
expect(result.savePreferences).toEqual(false);
expect(result.isMultiSelect).toEqual(false);
expect(result.disabled).toEqual(false);
expect(result.clearDependentSelections).toBeDefined();
expect(result.clearDependentSelections?.includes(RESOURCES)).toBe(true);

customSelectEngineConfig.configuration.type = CloudPulseSelectTypes.dynamic;
customSelectEngineConfig.configuration.apiV4QueryKey =
databaseQueries.engines;
customSelectEngineConfig.configuration.isMultiSelect = true;
customSelectEngineConfig.configuration.options = undefined;

result = getCustomSelectProperties(
{
config: customSelectEngineConfig,
dashboard: mockDashboard,
isServiceAnalyticsIntegration: true,
},
vi.fn()
);

expect(result.apiV4QueryKey).toEqual(databaseQueries.engines);
expect(result.type).toEqual(CloudPulseSelectTypes.dynamic);
expect(result.savePreferences).toEqual(false);
expect(result.isMultiSelect).toEqual(true);
}
});

it('test getFiltersForMetricsCallFromCustomSelect method', () => {
const result = getMetricsCallCustomFilters(
{
resource_id: [1, 2, 3],
},
'linode'
);

expect(result).toBeDefined();
expect(result.length).toEqual(0);
});

it('test constructAdditionalRequestFilters method', () => {
const result = constructAdditionalRequestFilters(
getMetricsCallCustomFilters(
{
resource_id: [1, 2, 3],
},
'linode'
)
);

expect(result).toBeDefined();
expect(result.length).toEqual(0);
});
132 changes: 130 additions & 2 deletions packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { RELATIVE_TIME_DURATION } from './constants';
import { RELATIVE_TIME_DURATION, RESOURCE_ID, RESOURCES } from './constants';
import { FILTER_CONFIG } from './FilterConfig';
import { CloudPulseSelectTypes } from './models';

import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding';
import type { CloudPulseCustomSelectProps } from '../shared/CloudPulseCustomSelect';
import type { CloudPulseRegionSelectProps } from '../shared/CloudPulseRegionSelect';
import type {
CloudPulseResources,
CloudPulseResourcesSelectProps,
} from '../shared/CloudPulseResourcesSelect';
import type { CloudPulseTimeRangeSelectProps } from '../shared/CloudPulseTimeRangeSelect';
import type { CloudPulseMetricsAdditionalFilters } from '../Widget/CloudPulseWidget';
import type { CloudPulseServiceTypeFilters } from './models';
import type { Dashboard, Filter, TimeDuration } from '@linode/api-v4';
import type { Dashboard, Filter, Filters, TimeDuration } from '@linode/api-v4';

interface CloudPulseFilterProperties {
config: CloudPulseServiceTypeFilters;
Expand Down Expand Up @@ -86,6 +89,54 @@ export const getResourcesProperties = (
};
};

/**
* @param props The cloudpulse filter properties selected so far
* @param handleCustomSelectChange The callback function when a filter change happens
* @returns {CloudPulseCustomSelectProps} Returns a property compatible for CloudPulseCustomSelect Component
*/
export const getCustomSelectProperties = (
props: CloudPulseFilterProperties,
handleCustomSelectChange: (filterKey: string, value: FilterValueType) => void
): CloudPulseCustomSelectProps => {
const {
apiIdField,
apiLabelField,
apiV4QueryKey,
filterKey,
filterType,
isMultiSelect,
maxSelections,
options,
placeholder,
} = props.config.configuration;
const { dashboard, dependentFilters, isServiceAnalyticsIntegration } = props;
return {
apiResponseIdField: apiIdField,
apiResponseLabelField: apiLabelField,
apiV4QueryKey,
clearDependentSelections: getDependentFiltersByFilterKey(
filterKey,
dashboard
),
disabled: checkIfWeNeedToDisableFilterByFilterKey(
filterKey,
dependentFilters ?? {},
dashboard
),
filterKey,
filterType,
handleSelectionChange: handleCustomSelectChange,
isMultiSelect,
maxSelections,
options,
placeholder,
savePreferences: !isServiceAnalyticsIntegration,
type: options
? CloudPulseSelectTypes.static
: CloudPulseSelectTypes.dynamic,
};
};

/**
* This function helps in building the properties needed for time duration filter
*
Expand Down Expand Up @@ -206,3 +257,80 @@ export const checkIfAllMandatoryFiltersAreSelected = (
return value !== undefined && (!Array.isArray(value) || value.length > 0);
});
};

/**
* @param selectedFilters The selected filters from the global filters view from custom select component
* @param serviceType The serviceType assosicated with the dashboard like linode, dbaas etc.,
* @returns Constructs and returns the metrics call filters based on selected filters and service type
*/
export const getMetricsCallCustomFilters = (
selectedFilters: {
[key: string]: FilterValueType;
},
serviceType: string
): CloudPulseMetricsAdditionalFilters[] => {
const serviceTypeConfig = FILTER_CONFIG.get(serviceType);

// If configuration exists, filter and map it to the desired CloudPulseMetricsAdditionalFilters format
return (
serviceTypeConfig?.filters
.filter(
({ configuration }) =>
configuration.isFilterable &&
!configuration.isMetricsFilter &&
selectedFilters[configuration.filterKey]
)
.map(({ configuration }) => ({
filterKey: configuration.filterKey,
filterValue: selectedFilters[configuration.filterKey],
})) ?? []
);
};

/**
* @param additionalFilters The additional filters selected from custom select components
* @returns The list of filters for the metric API call, based the additional custom select components
*/
export const constructAdditionalRequestFilters = (
additionalFilters: CloudPulseMetricsAdditionalFilters[]
): Filters[] => {
const filters: Filters[] = [];
for (const filter of additionalFilters) {
if (filter) {
// push to the filters
filters.push({
key: filter.filterKey,
operator: Array.isArray(filter.filterValue) ? 'in' : 'eq',
value: Array.isArray(filter.filterValue)
? Array.of(filter.filterValue).join(',')
: String(filter.filterValue),
});
}
}
return filters;
};

/**
*
* @param filterKey The filterKey of the actual filter
* @param dashboard The selected dashboard from the global filter view
* @returns The filterKeys that needs to be removed from the preferences
*/
const getDependentFiltersByFilterKey = (
filterKey: string,
dashboard: Dashboard
): string[] => {
const serviceTypeConfig = FILTER_CONFIG.get(dashboard.service_type);

if (!serviceTypeConfig) {
return [];
}

return serviceTypeConfig.filters
.filter((filter) => filter?.configuration?.dependency?.includes(filterKey))
.map(({ configuration }) =>
configuration.filterKey === RESOURCE_ID
? RESOURCES
: configuration.filterKey
);
};
Loading

0 comments on commit f26a99e

Please sign in to comment.