Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [DI-20348] - Changes for Adding CloudPulseCustomSelect component and Integration #10807

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
22ed724
upcoming: [DI-20348] - Changes for CloudPulseCustomSelect components …
venkat199501 Aug 21, 2024
f73f2a6
upcoming: [DI-20348] - Added changeset
venkat199501 Aug 21, 2024
145983a
upcoming: [DI-20348] - Code refactoring changes
venkat199501 Aug 21, 2024
bed370c
upcoming: [DI-20348] - Code refactoring and method optimisations
venkat199501 Aug 21, 2024
8507623
upcoming: [DI-20348] - Simplify code logic
venkat199501 Aug 21, 2024
34278aa
upcoming: [DI-20348] - More refactoring
venkat199501 Aug 21, 2024
d6b4d1a
upcoming: [DI-20348] - Error message fix
venkat199501 Aug 21, 2024
dfbcaa7
upcoming: [DI-20348] - Simplify checks
venkat199501 Aug 21, 2024
0718c68
upcoming: [DI-20348] - UT's for cloudpulsecustom select
venkat199501 Aug 21, 2024
ab7030e
upcoming: [DI-20348] - UT updates
venkat199501 Aug 21, 2024
4c337b3
upcoming: [DI-20348] - PR comments changes
venkat199501 Aug 22, 2024
4ab7449
upcoming: [DI-20348] - Simple names for functions
venkat199501 Aug 22, 2024
3713ff8
upcoming: [DI-20348] - JSDoc comments fix
venkat199501 Aug 22, 2024
f8021f1
upcoming: [DI-20348] - Lint issue fix
venkat199501 Aug 22, 2024
88509a1
upcoming: [DI-20348] - code simplification
venkat199501 Aug 23, 2024
a8973b8
upcoming: [DI-20348] - PR comments and changeset updates
venkat199501 Aug 24, 2024
7d3e1b3
upcoming: [DI-20348] - ESlint issue fixes across files
venkat199501 Aug 24, 2024
70a787d
upcoming: [DI-20348] - ESlint issue fixes across files
venkat199501 Aug 26, 2024
4dd3c6b
upcoming: [DI-20348] - formatting serverhandler
venkat199501 Aug 26, 2024
89bbded
upcoming: [DI-20348] - Test names updates
venkat199501 Aug 26, 2024
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
---

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
dwiley-akamai marked this conversation as resolved.
Show resolved Hide resolved
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
Loading