From ae045fb696a0915d6fe9c684de375368324456a5 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Mon, 2 Dec 2024 19:04:03 +0530 Subject: [PATCH 01/20] upcoming: [DI-20595] - ACLP Supported regions per service type --- packages/manager/src/featureFlags.ts | 5 +- .../CloudPulse/Utils/CloudPulseWidgetUtils.ts | 8 +-- .../features/CloudPulse/Utils/FilterConfig.ts | 12 +++- .../src/features/CloudPulse/Utils/models.ts | 11 ++- .../shared/CloudPulseRegionSelect.test.tsx | 72 +++++++++++++++++-- .../shared/CloudPulseRegionSelect.tsx | 44 +++++++++++- .../shared/CloudPulseResourcesSelect.tsx | 4 +- 7 files changed, 137 insertions(+), 19 deletions(-) diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 5c1fc1f2d83..7ae5c5d2e09 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -69,10 +69,11 @@ interface LkeEnterpriseFlag extends BaseFeatureFlag { la: boolean; } -export interface CloudPulseResourceTypeMapFlag { +export interface CloudPulseServiceTypeMapFlag { dimensionKey: string; maxResourceSelections?: number; serviceType: string; + supportedRegionIds?: string; } interface gpuV2 { @@ -101,7 +102,7 @@ export interface Flags { aclp: AclpFlag; aclpAlerting: AclpAlerting; aclpReadEndpoint: string; - aclpResourceTypeMap: CloudPulseResourceTypeMapFlag[]; + aclpServiceTypeMap: CloudPulseServiceTypeMapFlag[]; apiMaintenance: APIMaintenance; apicliButtonCopy: string; apl: boolean; diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts index f4066808b3d..b1166834b05 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts @@ -25,7 +25,7 @@ import type { Theme } from '@mui/material'; import type { DataSet } from 'src/components/AreaChart/AreaChart'; import type { AreaProps } from 'src/components/AreaChart/AreaChart'; import type { MetricsDisplayRow } from 'src/components/LineGraph/MetricsDisplay'; -import type { CloudPulseResourceTypeMapFlag, FlagSet } from 'src/featureFlags'; +import type { CloudPulseServiceTypeMapFlag, FlagSet } from 'src/featureFlags'; interface LabelNameOptionsProps { /** @@ -122,7 +122,7 @@ interface DimensionNameProperties { /** * flag dimension key mapping for service type */ - flag: CloudPulseResourceTypeMapFlag | undefined; + flag: CloudPulseServiceTypeMapFlag | undefined; /** * metric key-value to generate dimension name @@ -315,8 +315,8 @@ const getLabelName = (props: LabelNameOptionsProps): string => { return `${label} (${unit})`; } - const flag = flags?.aclpResourceTypeMap?.find( - (obj: CloudPulseResourceTypeMapFlag) => obj.serviceType === serviceType + const flag = flags?.aclpServiceTypeMap?.find( + (obj: CloudPulseServiceTypeMapFlag) => obj.serviceType === serviceType ); return getDimensionName({ flag, metric, resources }); diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts index 55c96678749..3cdacc9d1de 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts @@ -3,11 +3,14 @@ import { CloudPulseSelectTypes } from './models'; import type { CloudPulseServiceTypeFilterMap } from './models'; const TIME_DURATION = 'Time Range'; +const DBAAS_CAPABILITY = 'Managed Databases'; +const LINODE_CAPABILITY = 'Linodes'; export const LINODE_CONFIG: Readonly = { filters: [ { configuration: { + capability: LINODE_CAPABILITY, filterKey: 'region', filterType: 'string', isFilterable: false, @@ -20,6 +23,7 @@ export const LINODE_CONFIG: Readonly = { }, { configuration: { + capability: LINODE_CAPABILITY, dependency: ['region'], filterKey: 'resource_id', filterType: 'string', @@ -35,6 +39,7 @@ export const LINODE_CONFIG: Readonly = { }, { configuration: { + capability: LINODE_CAPABILITY, filterKey: 'relative_time_duration', filterType: 'string', isFilterable: true, @@ -55,6 +60,7 @@ export const DBAAS_CONFIG: Readonly = { filters: [ { configuration: { + capability: DBAAS_CAPABILITY, filterKey: 'engine', filterType: 'string', isFilterable: false, // isFilterable -- this determines whethere you need to pass it metrics api @@ -80,6 +86,7 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { + capability: DBAAS_CAPABILITY, filterKey: 'region', filterType: 'string', isFilterable: false, @@ -92,6 +99,7 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { + capability: DBAAS_CAPABILITY, dependency: ['region', 'engine'], filterKey: 'resource_id', filterType: 'string', @@ -107,6 +115,7 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { + capability: DBAAS_CAPABILITY, filterKey: 'relative_time_duration', filterType: 'string', isFilterable: true, @@ -121,6 +130,7 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { + capability: DBAAS_CAPABILITY, filterKey: 'node_type', filterType: 'string', isFilterable: true, // isFilterable -- this determines whether you need to pass it metrics api @@ -149,7 +159,7 @@ export const DBAAS_CONFIG: Readonly = { }; export const FILTER_CONFIG: Readonly< - Map + Map > = new Map([ ['dbaas', DBAAS_CONFIG], ['linode', LINODE_CONFIG], diff --git a/packages/manager/src/features/CloudPulse/Utils/models.ts b/packages/manager/src/features/CloudPulse/Utils/models.ts index f51e945074e..02d58fbc570 100644 --- a/packages/manager/src/features/CloudPulse/Utils/models.ts +++ b/packages/manager/src/features/CloudPulse/Utils/models.ts @@ -1,4 +1,8 @@ -import type { DatabaseEngine, DatabaseType } from '@linode/api-v4'; +import type { + Capabilities, + DatabaseEngine, + DatabaseType, +} from '@linode/api-v4'; import type { QueryFunction, QueryKey } from '@tanstack/react-query'; /** @@ -83,6 +87,11 @@ export interface CloudPulseServiceTypeFiltersConfiguration { */ apiV4QueryKey?: QueryFunctionAndKey; + /** + * This is the current capability corresponding to the service type + */ + capability: Capabilities; + /** * This is an optional field, it is used to disable a certain filter, untill of the dependent filters are selected */ diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 9372ec32ead..9aaf2799528 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -1,12 +1,15 @@ +import userEvent from '@testing-library/user-event'; import * as React from 'react'; -import * as regions from 'src/queries/regions/regions'; +import { dashboardFactory, regionFactory } from 'src/factories'; import { renderWithTheme } from 'src/utilities/testHelpers'; import { CloudPulseRegionSelect } from './CloudPulseRegionSelect'; import type { CloudPulseRegionSelectProps } from './CloudPulseRegionSelect'; import type { Region } from '@linode/api-v4'; +import type { CloudPulseServiceTypeMapFlag, Flags } from 'src/featureFlags'; +import type * as regions from 'src/queries/regions/regions'; const props: CloudPulseRegionSelectProps = { handleRegionChange: vi.fn(), @@ -14,11 +17,32 @@ const props: CloudPulseRegionSelectProps = { selectedDashboard: undefined, }; -describe('CloudPulseRegionSelect', () => { - vi.spyOn(regions, 'useRegionsQuery').mockReturnValue({ - data: Array(), - } as ReturnType); +const queryMocks = vi.hoisted(() => ({ + useRegionsQuery: vi.fn().mockReturnValue({}), +})); + +const flags: Partial = { + aclpServiceTypeMap: [ + { + serviceType: 'dbaas', + supportedRegionIds: 'us-west', + }, + { + serviceType: 'linode', + supportedRegionIds: 'us-east', + }, + ] as CloudPulseServiceTypeMapFlag[], +}; +vi.mock('src/queries/regions/regions', async () => { + const actual = await vi.importActual('src/queries/regions/regions'); + return { + ...actual, + useRegionsQuery: queryMocks.useRegionsQuery, + }; +}); + +describe('CloudPulseRegionSelect', () => { it('should render a Region Select component', () => { const { getByLabelText, getByTestId } = renderWithTheme( @@ -29,7 +53,7 @@ describe('CloudPulseRegionSelect', () => { }); it('should render a Region Select component with proper error message on api call failure', () => { - vi.spyOn(regions, 'useRegionsQuery').mockReturnValue({ + queryMocks.useRegionsQuery.mockReturnValue({ data: undefined, isError: true, isLoading: false, @@ -40,4 +64,40 @@ describe('CloudPulseRegionSelect', () => { expect(getByText('Failed to fetch Region.')); }); + + it('should render a Region Select component with capability specific and launchDarkly based supported regions', async () => { + const user = userEvent.setup(); + + const allRegions: Region[] = [ + regionFactory.build({ capabilities: ['Linodes'], id: 'us-east' }), + regionFactory.build({ + capabilities: ['Managed Databases'], + id: 'us-west', + label: 'US, Fremont, CA', + }), + ]; + + queryMocks.useRegionsQuery.mockReturnValue({ + data: allRegions, + isError: false, + isLoading: false, + }); + + const { getByRole } = renderWithTheme( + , + { flags } + ); + + await user.click(getByRole('button', { name: 'Open' })); + // example: region id => 'us-west' belongs to service type - 'dbass', capability -'Managed Databases', and is supported via launchDarkly + expect( + getByRole('option', { + name: 'US, Fremont, CA (us-west)', + }) + ).toBeInTheDocument(); }); +}); \ No newline at end of file diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index bb8a1c25f03..6e0cacd6a9a 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -1,9 +1,13 @@ import * as React from 'react'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; +import { useFlags } from 'src/hooks/useFlags'; import { useRegionsQuery } from 'src/queries/regions/regions'; -import type { Dashboard, FilterValue } from '@linode/api-v4'; +import { FILTER_CONFIG } from '../Utils/FilterConfig'; + +import type { CloudPulseServiceTypeFilters } from '../Utils/models'; +import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; export interface CloudPulseRegionSelectProps { defaultValue?: FilterValue; @@ -18,6 +22,8 @@ export const CloudPulseRegionSelect = React.memo( (props: CloudPulseRegionSelectProps) => { const { data: regions, isError, isLoading } = useRegionsQuery(); + const flags = useFlags(); + const { defaultValue, handleRegionChange, @@ -27,6 +33,16 @@ export const CloudPulseRegionSelect = React.memo( selectedDashboard, } = props; + const currentFilterConfig: + | CloudPulseServiceTypeFilters + | undefined = FILTER_CONFIG.get( + selectedDashboard?.service_type + )?.filters.filter( + (config) => config.configuration.filterKey === 'region' + )[0]; + // Capture the current capability from corresponding filter config + const capability = currentFilterConfig?.configuration.capability; + const [selectedRegion, setSelectedRegion] = React.useState(); // Once the data is loaded, set the state variable with value stored in preferences React.useEffect(() => { @@ -40,13 +56,35 @@ export const CloudPulseRegionSelect = React.memo( // eslint-disable-next-line react-hooks/exhaustive-deps }, [regions]); + // validate launchDrakly region_ids with the ids from the fetched 'all-regions' + const supportedRegions = React.useMemo(() => { + const serviceTypeFlag = flags.aclpServiceTypeMap?.find( + (item) => item.serviceType === selectedDashboard?.service_type + ); + + const supportedRegionsIdList = serviceTypeFlag?.supportedRegionIds + ?.split(',') + .map((regionId) => regionId.trim()); + + if (!supportedRegionsIdList?.length) { + return regions; + } + + const validatedRegions = regions?.filter((region) => + supportedRegionsIdList?.includes(region.id) + ); + + return validatedRegions ? validatedRegions : regions; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [flags.aclpServiceTypeMap, regions, selectedDashboard?.service_type]); + return ( { setSelectedRegion(region?.id); handleRegionChange(region?.id, savePreferences); }} - currentCapability={undefined} + currentCapability={capability} data-testid="region-select" disableClearable={false} disabled={!selectedDashboard || !regions} @@ -56,7 +94,7 @@ export const CloudPulseRegionSelect = React.memo( loading={isLoading} noMarginTop placeholder={placeholder ?? 'Select a Region'} - regions={regions ? regions : []} + regions={supportedRegions ? supportedRegions : []} value={selectedRegion} /> ); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index c14d8afe38f..6e306b13264 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -87,11 +87,11 @@ export const CloudPulseResourcesSelect = React.memo( // Maximum resource selection limit is fetched from launchdarkly const maxResourceSelectionLimit = React.useMemo(() => { - const obj = flags.aclpResourceTypeMap?.find( + const obj = flags.aclpServiceTypeMap?.find( (item) => item.serviceType === resourceType ); return obj?.maxResourceSelections || 10; - }, [resourceType, flags.aclpResourceTypeMap]); + }, [resourceType, flags.aclpServiceTypeMap]); const resourcesLimitReached = React.useMemo(() => { return getResourcesList.length > maxResourceSelectionLimit; From b576975592a6e7b08b3b88a00b0b84ee22be812e Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Mon, 2 Dec 2024 19:37:34 +0530 Subject: [PATCH 02/20] upcoming: [DI-20595] - small fix --- .../features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 6e0cacd6a9a..274d67ba7ec 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -70,11 +70,9 @@ export const CloudPulseRegionSelect = React.memo( return regions; } - const validatedRegions = regions?.filter((region) => + return regions?.filter((region) => supportedRegionsIdList?.includes(region.id) ); - - return validatedRegions ? validatedRegions : regions; // eslint-disable-next-line react-hooks/exhaustive-deps }, [flags.aclpServiceTypeMap, regions, selectedDashboard?.service_type]); @@ -94,7 +92,7 @@ export const CloudPulseRegionSelect = React.memo( loading={isLoading} noMarginTop placeholder={placeholder ?? 'Select a Region'} - regions={supportedRegions ? supportedRegions : []} + regions={supportedRegions ? supportedRegions : regions ? regions : []} value={selectedRegion} /> ); From 1832509260263f47321616532dc2fe69d06e29d5 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 10:40:33 +0530 Subject: [PATCH 03/20] upcoming: [DI-20595] - improved test quality --- .../shared/CloudPulseRegionSelect.test.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 9aaf2799528..a306c6173d4 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -29,7 +29,7 @@ const flags: Partial = { }, { serviceType: 'linode', - supportedRegionIds: 'us-east', + supportedRegionIds: 'us-lax', }, ] as CloudPulseServiceTypeMapFlag[], }; @@ -69,7 +69,11 @@ describe('CloudPulseRegionSelect', () => { const user = userEvent.setup(); const allRegions: Region[] = [ - regionFactory.build({ capabilities: ['Linodes'], id: 'us-east' }), + regionFactory.build({ + capabilities: ['Linodes'], + id: 'us-lax', + label: 'US, Los Angeles, CA', + }), regionFactory.build({ capabilities: ['Managed Databases'], id: 'us-west', @@ -83,7 +87,7 @@ describe('CloudPulseRegionSelect', () => { isLoading: false, }); - const { getByRole } = renderWithTheme( + const { getByRole, queryByRole } = renderWithTheme( { name: 'US, Fremont, CA (us-west)', }) ).toBeInTheDocument(); + expect( + queryByRole('option', { + name: 'US, Los Angeles, CA (us-lax)', + }) + ).toBeNull(); + }); }); -}); \ No newline at end of file From 78a1489e7c03237b013116876e451b318ca04abd Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 16:30:44 +0530 Subject: [PATCH 04/20] upcoming: [DI-20595] - PR Comments --- packages/manager/src/featureFlags.ts | 4 +- .../CloudPulse/Utils/CloudPulseWidgetUtils.ts | 8 ++-- .../features/CloudPulse/Utils/FilterConfig.ts | 12 ++--- .../src/features/CloudPulse/Utils/models.ts | 9 ++-- .../shared/CloudPulseRegionSelect.test.tsx | 30 ++++++++++--- .../shared/CloudPulseRegionSelect.tsx | 45 ++++++++++--------- .../shared/CloudPulseResourcesSelect.tsx | 4 +- 7 files changed, 65 insertions(+), 47 deletions(-) diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 7ae5c5d2e09..493dbc39767 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -69,7 +69,7 @@ interface LkeEnterpriseFlag extends BaseFeatureFlag { la: boolean; } -export interface CloudPulseServiceTypeMapFlag { +export interface CloudPulseResourceTypeMapFlag { dimensionKey: string; maxResourceSelections?: number; serviceType: string; @@ -102,7 +102,7 @@ export interface Flags { aclp: AclpFlag; aclpAlerting: AclpAlerting; aclpReadEndpoint: string; - aclpServiceTypeMap: CloudPulseServiceTypeMapFlag[]; + aclpResourceTypeMap: CloudPulseResourceTypeMapFlag[]; apiMaintenance: APIMaintenance; apicliButtonCopy: string; apl: boolean; diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts index b1166834b05..f4066808b3d 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts @@ -25,7 +25,7 @@ import type { Theme } from '@mui/material'; import type { DataSet } from 'src/components/AreaChart/AreaChart'; import type { AreaProps } from 'src/components/AreaChart/AreaChart'; import type { MetricsDisplayRow } from 'src/components/LineGraph/MetricsDisplay'; -import type { CloudPulseServiceTypeMapFlag, FlagSet } from 'src/featureFlags'; +import type { CloudPulseResourceTypeMapFlag, FlagSet } from 'src/featureFlags'; interface LabelNameOptionsProps { /** @@ -122,7 +122,7 @@ interface DimensionNameProperties { /** * flag dimension key mapping for service type */ - flag: CloudPulseServiceTypeMapFlag | undefined; + flag: CloudPulseResourceTypeMapFlag | undefined; /** * metric key-value to generate dimension name @@ -315,8 +315,8 @@ const getLabelName = (props: LabelNameOptionsProps): string => { return `${label} (${unit})`; } - const flag = flags?.aclpServiceTypeMap?.find( - (obj: CloudPulseServiceTypeMapFlag) => obj.serviceType === serviceType + const flag = flags?.aclpResourceTypeMap?.find( + (obj: CloudPulseResourceTypeMapFlag) => obj.serviceType === serviceType ); return getDimensionName({ flag, metric, resources }); diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts index 3cdacc9d1de..c6313bbb1f4 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts @@ -7,10 +7,10 @@ const DBAAS_CAPABILITY = 'Managed Databases'; const LINODE_CAPABILITY = 'Linodes'; export const LINODE_CONFIG: Readonly = { + capability: LINODE_CAPABILITY, filters: [ { configuration: { - capability: LINODE_CAPABILITY, filterKey: 'region', filterType: 'string', isFilterable: false, @@ -23,7 +23,6 @@ export const LINODE_CONFIG: Readonly = { }, { configuration: { - capability: LINODE_CAPABILITY, dependency: ['region'], filterKey: 'resource_id', filterType: 'string', @@ -39,7 +38,6 @@ export const LINODE_CONFIG: Readonly = { }, { configuration: { - capability: LINODE_CAPABILITY, filterKey: 'relative_time_duration', filterType: 'string', isFilterable: true, @@ -57,10 +55,10 @@ export const LINODE_CONFIG: Readonly = { }; export const DBAAS_CONFIG: Readonly = { + capability: DBAAS_CAPABILITY, filters: [ { configuration: { - capability: DBAAS_CAPABILITY, filterKey: 'engine', filterType: 'string', isFilterable: false, // isFilterable -- this determines whethere you need to pass it metrics api @@ -86,7 +84,6 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { - capability: DBAAS_CAPABILITY, filterKey: 'region', filterType: 'string', isFilterable: false, @@ -99,7 +96,6 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { - capability: DBAAS_CAPABILITY, dependency: ['region', 'engine'], filterKey: 'resource_id', filterType: 'string', @@ -115,7 +111,6 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { - capability: DBAAS_CAPABILITY, filterKey: 'relative_time_duration', filterType: 'string', isFilterable: true, @@ -130,7 +125,6 @@ export const DBAAS_CONFIG: Readonly = { }, { configuration: { - capability: DBAAS_CAPABILITY, filterKey: 'node_type', filterType: 'string', isFilterable: true, // isFilterable -- this determines whether you need to pass it metrics api @@ -159,7 +153,7 @@ export const DBAAS_CONFIG: Readonly = { }; export const FILTER_CONFIG: Readonly< - Map + Map > = new Map([ ['dbaas', DBAAS_CONFIG], ['linode', LINODE_CONFIG], diff --git a/packages/manager/src/features/CloudPulse/Utils/models.ts b/packages/manager/src/features/CloudPulse/Utils/models.ts index 02d58fbc570..05d4c7fc926 100644 --- a/packages/manager/src/features/CloudPulse/Utils/models.ts +++ b/packages/manager/src/features/CloudPulse/Utils/models.ts @@ -9,6 +9,10 @@ import type { QueryFunction, QueryKey } from '@tanstack/react-query'; * The CloudPulseServiceTypeMap has list of filters to be built for different service types like dbaas, linode etc.,The properties here are readonly as it is only for reading and can't be modified in code */ export interface CloudPulseServiceTypeFilterMap { + /** + * Current capability corresponding to a service type + */ + readonly capability: Capabilities; /** * The list of filters for a service type */ @@ -87,11 +91,6 @@ export interface CloudPulseServiceTypeFiltersConfiguration { */ apiV4QueryKey?: QueryFunctionAndKey; - /** - * This is the current capability corresponding to the service type - */ - capability: Capabilities; - /** * This is an optional field, it is used to disable a certain filter, untill of the dependent filters are selected */ diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index a306c6173d4..198ca48098d 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -8,7 +8,7 @@ import { CloudPulseRegionSelect } from './CloudPulseRegionSelect'; import type { CloudPulseRegionSelectProps } from './CloudPulseRegionSelect'; import type { Region } from '@linode/api-v4'; -import type { CloudPulseServiceTypeMapFlag, Flags } from 'src/featureFlags'; +import type { CloudPulseResourceTypeMapFlag, Flags } from 'src/featureFlags'; import type * as regions from 'src/queries/regions/regions'; const props: CloudPulseRegionSelectProps = { @@ -22,16 +22,16 @@ const queryMocks = vi.hoisted(() => ({ })); const flags: Partial = { - aclpServiceTypeMap: [ + aclpResourceTypeMap: [ { serviceType: 'dbaas', - supportedRegionIds: 'us-west', + supportedRegionIds: 'us-west, us-east', }, { serviceType: 'linode', - supportedRegionIds: 'us-lax', + supportedRegionIds: 'us-lax, us-mia', }, - ] as CloudPulseServiceTypeMapFlag[], + ] as CloudPulseResourceTypeMapFlag[], }; vi.mock('src/queries/regions/regions', async () => { @@ -74,11 +74,21 @@ describe('CloudPulseRegionSelect', () => { id: 'us-lax', label: 'US, Los Angeles, CA', }), + regionFactory.build({ + capabilities: ['Linodes'], + id: 'us-mia', + label: 'US, Miami, FL', + }), regionFactory.build({ capabilities: ['Managed Databases'], id: 'us-west', label: 'US, Fremont, CA', }), + regionFactory.build({ + capabilities: ['Managed Databases'], + id: 'us-east', + label: 'US, Newark, NJ', + }), ]; queryMocks.useRegionsQuery.mockReturnValue({ @@ -103,10 +113,20 @@ describe('CloudPulseRegionSelect', () => { name: 'US, Fremont, CA (us-west)', }) ).toBeInTheDocument(); + expect( + getByRole('option', { + name: 'US, Newark, NJ (us-east)', + }) + ).toBeInTheDocument(); expect( queryByRole('option', { name: 'US, Los Angeles, CA (us-lax)', }) ).toBeNull(); + expect( + queryByRole('option', { + name: 'US, Miami, FL (us-mia)', + }) + ).toBeNull(); }); }); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 274d67ba7ec..8089c07348d 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -6,8 +6,13 @@ import { useRegionsQuery } from 'src/queries/regions/regions'; import { FILTER_CONFIG } from '../Utils/FilterConfig'; -import type { CloudPulseServiceTypeFilters } from '../Utils/models'; -import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; +import type { + Capabilities, + Dashboard, + FilterValue, + Region, +} from '@linode/api-v4'; +import type { CloudPulseResourceTypeMapFlag } from 'src/featureFlags'; export interface CloudPulseRegionSelectProps { defaultValue?: FilterValue; @@ -33,15 +38,12 @@ export const CloudPulseRegionSelect = React.memo( selectedDashboard, } = props; - const currentFilterConfig: - | CloudPulseServiceTypeFilters - | undefined = FILTER_CONFIG.get( - selectedDashboard?.service_type - )?.filters.filter( - (config) => config.configuration.filterKey === 'region' - )[0]; - // Capture the current capability from corresponding filter config - const capability = currentFilterConfig?.configuration.capability; + const serviceType: string | undefined = selectedDashboard?.service_type; + const capability = React.useMemo(() => { + return serviceType + ? FILTER_CONFIG.get(serviceType)?.capability + : undefined; + }, [serviceType]); const [selectedRegion, setSelectedRegion] = React.useState(); // Once the data is loaded, set the state variable with value stored in preferences @@ -58,23 +60,26 @@ export const CloudPulseRegionSelect = React.memo( // validate launchDrakly region_ids with the ids from the fetched 'all-regions' const supportedRegions = React.useMemo(() => { - const serviceTypeFlag = flags.aclpServiceTypeMap?.find( - (item) => item.serviceType === selectedDashboard?.service_type + const serviceTypeFlag: + | CloudPulseResourceTypeMapFlag + | undefined = flags.aclpResourceTypeMap?.find( + (item: CloudPulseResourceTypeMapFlag) => + item.serviceType === serviceType ); - const supportedRegionsIdList = serviceTypeFlag?.supportedRegionIds - ?.split(',') - .map((regionId) => regionId.trim()); + const supportedRegionsIdList: string[] = + serviceTypeFlag?.supportedRegionIds + ?.split(',') + .map((regionId: string) => regionId.trim()) || []; - if (!supportedRegionsIdList?.length) { + if (!supportedRegionsIdList.length) { return regions; } return regions?.filter((region) => supportedRegionsIdList?.includes(region.id) ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [flags.aclpServiceTypeMap, regions, selectedDashboard?.service_type]); + }, [flags.aclpResourceTypeMap, regions, serviceType]); return ( ); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index 6e306b13264..c14d8afe38f 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -87,11 +87,11 @@ export const CloudPulseResourcesSelect = React.memo( // Maximum resource selection limit is fetched from launchdarkly const maxResourceSelectionLimit = React.useMemo(() => { - const obj = flags.aclpServiceTypeMap?.find( + const obj = flags.aclpResourceTypeMap?.find( (item) => item.serviceType === resourceType ); return obj?.maxResourceSelections || 10; - }, [resourceType, flags.aclpServiceTypeMap]); + }, [resourceType, flags.aclpResourceTypeMap]); const resourcesLimitReached = React.useMemo(() => { return getResourcesList.length > maxResourceSelectionLimit; From 615c573fdf3966d1abffa897451ccfbddb240592 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 16:34:49 +0530 Subject: [PATCH 05/20] upcoming: [DI-20595] - small fix --- .../src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 8089c07348d..f981d0c29af 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -60,7 +60,7 @@ export const CloudPulseRegionSelect = React.memo( // validate launchDrakly region_ids with the ids from the fetched 'all-regions' const supportedRegions = React.useMemo(() => { - const serviceTypeFlag: + const resourceTypeFlag: | CloudPulseResourceTypeMapFlag | undefined = flags.aclpResourceTypeMap?.find( (item: CloudPulseResourceTypeMapFlag) => @@ -68,7 +68,7 @@ export const CloudPulseRegionSelect = React.memo( ); const supportedRegionsIdList: string[] = - serviceTypeFlag?.supportedRegionIds + resourceTypeFlag?.supportedRegionIds ?.split(',') .map((regionId: string) => regionId.trim()) || []; From 19db96e92015d7f1f16a2e4d110920c6d3db2cc8 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 16:44:06 +0530 Subject: [PATCH 06/20] upcoming: [DI-20595] - updated UT --- .../src/features/CloudPulse/Utils/FilterConfig.ts | 4 ++-- .../shared/CloudPulseRegionSelect.test.tsx | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts index c6313bbb1f4..e5bc12d7e3b 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts @@ -3,8 +3,8 @@ import { CloudPulseSelectTypes } from './models'; import type { CloudPulseServiceTypeFilterMap } from './models'; const TIME_DURATION = 'Time Range'; -const DBAAS_CAPABILITY = 'Managed Databases'; -const LINODE_CAPABILITY = 'Linodes'; +export const DBAAS_CAPABILITY = 'Managed Databases'; +export const LINODE_CAPABILITY = 'Linodes'; export const LINODE_CONFIG: Readonly = { capability: LINODE_CAPABILITY, diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 198ca48098d..f775dbcb406 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { dashboardFactory, regionFactory } from 'src/factories'; import { renderWithTheme } from 'src/utilities/testHelpers'; +import { DBAAS_CAPABILITY, LINODE_CAPABILITY } from '../Utils/FilterConfig'; import { CloudPulseRegionSelect } from './CloudPulseRegionSelect'; import type { CloudPulseRegionSelectProps } from './CloudPulseRegionSelect'; @@ -70,25 +71,30 @@ describe('CloudPulseRegionSelect', () => { const allRegions: Region[] = [ regionFactory.build({ - capabilities: ['Linodes'], + capabilities: [LINODE_CAPABILITY], id: 'us-lax', label: 'US, Los Angeles, CA', }), regionFactory.build({ - capabilities: ['Linodes'], + capabilities: [LINODE_CAPABILITY], id: 'us-mia', label: 'US, Miami, FL', }), regionFactory.build({ - capabilities: ['Managed Databases'], + capabilities: [DBAAS_CAPABILITY], id: 'us-west', label: 'US, Fremont, CA', }), regionFactory.build({ - capabilities: ['Managed Databases'], + capabilities: [DBAAS_CAPABILITY], id: 'us-east', label: 'US, Newark, NJ', }), + regionFactory.build({ + capabilities: [DBAAS_CAPABILITY], + id: 'us-central', + label: 'US, Dallas, TX', + }), ]; queryMocks.useRegionsQuery.mockReturnValue({ From 00d89ed5ff32553db6e5ee209d47a7bc95943873 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 16:55:51 +0530 Subject: [PATCH 07/20] upcoming: [DI-20595] - updated UT --- .../CloudPulse/shared/CloudPulseRegionSelect.test.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index f775dbcb406..6a1ed09de3e 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -124,6 +124,11 @@ describe('CloudPulseRegionSelect', () => { name: 'US, Newark, NJ (us-east)', }) ).toBeInTheDocument(); + expect( + queryByRole('option', { + name: 'US, Dallas, TX (us-central)', + }) + ).toBeNull(); expect( queryByRole('option', { name: 'US, Los Angeles, CA (us-lax)', From 5614b9ba4d1c87d93a9490ca6b78ad981fd17759 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 17:21:30 +0530 Subject: [PATCH 08/20] upcoming: [DI-20595] - updated types --- .../CloudPulse/shared/CloudPulseRegionSelect.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index f981d0c29af..09d58673744 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -39,11 +39,9 @@ export const CloudPulseRegionSelect = React.memo( } = props; const serviceType: string | undefined = selectedDashboard?.service_type; - const capability = React.useMemo(() => { - return serviceType - ? FILTER_CONFIG.get(serviceType)?.capability - : undefined; - }, [serviceType]); + const capability = serviceType + ? FILTER_CONFIG.get(serviceType)?.capability + : undefined; const [selectedRegion, setSelectedRegion] = React.useState(); // Once the data is loaded, set the state variable with value stored in preferences @@ -60,14 +58,12 @@ export const CloudPulseRegionSelect = React.memo( // validate launchDrakly region_ids with the ids from the fetched 'all-regions' const supportedRegions = React.useMemo(() => { - const resourceTypeFlag: - | CloudPulseResourceTypeMapFlag - | undefined = flags.aclpResourceTypeMap?.find( + const resourceTypeFlag = flags.aclpResourceTypeMap?.find( (item: CloudPulseResourceTypeMapFlag) => item.serviceType === serviceType ); - const supportedRegionsIdList: string[] = + const supportedRegionsIdList = resourceTypeFlag?.supportedRegionIds ?.split(',') .map((regionId: string) => regionId.trim()) || []; From 1dcb4af2fe58c7ae1a7472fd2450864db957b259 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 3 Dec 2024 18:47:38 +0530 Subject: [PATCH 09/20] upcoming: [DI-20595] - small linting fix --- .../features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 09d58673744..3ad0acda4ae 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -6,12 +6,7 @@ import { useRegionsQuery } from 'src/queries/regions/regions'; import { FILTER_CONFIG } from '../Utils/FilterConfig'; -import type { - Capabilities, - Dashboard, - FilterValue, - Region, -} from '@linode/api-v4'; +import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; import type { CloudPulseResourceTypeMapFlag } from 'src/featureFlags'; export interface CloudPulseRegionSelectProps { From a6cb6337f0385552d612d0bb4ba1386e74663ab9 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Thu, 5 Dec 2024 13:41:01 +0530 Subject: [PATCH 10/20] upcoming: [DI-20595] - handle empty regions --- .../CloudPulse/shared/CloudPulseRegionSelect.test.tsx | 2 +- .../features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 6a1ed09de3e..3a47701db75 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -26,7 +26,7 @@ const flags: Partial = { aclpResourceTypeMap: [ { serviceType: 'dbaas', - supportedRegionIds: 'us-west, us-east', + supportedRegionIds: 'us-west, us-east, ,', }, { serviceType: 'linode', diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 3ad0acda4ae..6625b443dc1 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -57,16 +57,15 @@ export const CloudPulseRegionSelect = React.memo( (item: CloudPulseResourceTypeMapFlag) => item.serviceType === serviceType ); - const supportedRegionsIdList = resourceTypeFlag?.supportedRegionIds ?.split(',') - .map((regionId: string) => regionId.trim()) || []; + .map((regionId: string) => regionId.trim()) + .filter((regionId: string) => regionId.length > 0) || []; if (!supportedRegionsIdList.length) { return regions; } - return regions?.filter((region) => supportedRegionsIdList?.includes(region.id) ); From d748c7da912a5e3fa393fd0b79724b9754aa51ef Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Fri, 6 Dec 2024 15:38:40 +0530 Subject: [PATCH 11/20] upcoming: [DI-20595] - Added changeset --- .../.changeset/pr-11381-upcoming-features-1733479690692.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md diff --git a/packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md b/packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md new file mode 100644 index 00000000000..a78f9e4aea0 --- /dev/null +++ b/packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Show ACLP supported regions per service type in region select ([#11381](https://github.com/linode/manager/pull/11381)) From 2854e2d88a3ace91b4bf6bc81dc67e796a13c964 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Fri, 6 Dec 2024 17:08:48 +0530 Subject: [PATCH 12/20] upcoming: [DI-20595] - Added changeset --- ...479690692.md => pr-11382-upcoming-features-1733485035913.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/manager/.changeset/{pr-11381-upcoming-features-1733479690692.md => pr-11382-upcoming-features-1733485035913.md} (66%) diff --git a/packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md b/packages/manager/.changeset/pr-11382-upcoming-features-1733485035913.md similarity index 66% rename from packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md rename to packages/manager/.changeset/pr-11382-upcoming-features-1733485035913.md index a78f9e4aea0..4f3f3c27984 100644 --- a/packages/manager/.changeset/pr-11381-upcoming-features-1733479690692.md +++ b/packages/manager/.changeset/pr-11382-upcoming-features-1733485035913.md @@ -2,4 +2,4 @@ "@linode/manager": Upcoming Features --- -Show ACLP supported regions per service type in region select ([#11381](https://github.com/linode/manager/pull/11381)) +Show ACLP supported regions per service type in region select ([#11382](https://github.com/linode/manager/pull/11382)) From d22480963d0e9a29642f2cd628819a8da07905fb Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Mon, 9 Dec 2024 14:30:25 +0530 Subject: [PATCH 13/20] upcoming: [DI-20595] - edge case fix --- .../CloudPulse/shared/CloudPulseRegionSelect.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 6625b443dc1..5e323c67c3f 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -8,6 +8,7 @@ import { FILTER_CONFIG } from '../Utils/FilterConfig'; import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; import type { CloudPulseResourceTypeMapFlag } from 'src/featureFlags'; +import { isNil } from 'ramda'; export interface CloudPulseRegionSelectProps { defaultValue?: FilterValue; @@ -57,17 +58,17 @@ export const CloudPulseRegionSelect = React.memo( (item: CloudPulseResourceTypeMapFlag) => item.serviceType === serviceType ); - const supportedRegionsIdList = - resourceTypeFlag?.supportedRegionIds - ?.split(',') - .map((regionId: string) => regionId.trim()) - .filter((regionId: string) => regionId.length > 0) || []; - if (!supportedRegionsIdList.length) { + if (isNil(resourceTypeFlag?.supportedRegionIds)) { return regions; } + + const supportedRegionsIdList = resourceTypeFlag.supportedRegionIds + .split(',') + .map((regionId: string) => regionId.trim()); + return regions?.filter((region) => - supportedRegionsIdList?.includes(region.id) + supportedRegionsIdList.includes(region.id) ); }, [flags.aclpResourceTypeMap, regions, serviceType]); From 9cef42688e3ab252af4037a254abc5dd76fdc33e Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Mon, 9 Dec 2024 18:20:20 +0530 Subject: [PATCH 14/20] upcoming: [DI-20595] - linting fix --- .../src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 5e323c67c3f..e4d0b325605 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -1,3 +1,4 @@ +import { isNil } from 'ramda'; import * as React from 'react'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; @@ -8,7 +9,6 @@ import { FILTER_CONFIG } from '../Utils/FilterConfig'; import type { Dashboard, FilterValue, Region } from '@linode/api-v4'; import type { CloudPulseResourceTypeMapFlag } from 'src/featureFlags'; -import { isNil } from 'ramda'; export interface CloudPulseRegionSelectProps { defaultValue?: FilterValue; From 0e3f89ce173410df9d67dcfde01749aa7055cf5e Mon Sep 17 00:00:00 2001 From: agorthi Date: Mon, 9 Dec 2024 19:14:23 +0530 Subject: [PATCH 15/20] Test[DI-20595] -add region select testcases --- .../cloudpulse-dashboard-errors.spec.ts | 26 ++++- .../dbaas-widgets-verification.spec.ts | 101 +++++++++++++----- .../linode-widget-verification.spec.ts | 83 ++++++++++---- 3 files changed, 153 insertions(+), 57 deletions(-) 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 f2fbcdc66a7..7a142a0a6bd 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 @@ -35,6 +35,7 @@ import { } from 'support/intercepts/databases'; import { Database } from '@linode/api-v4'; import { mockGetAccount } from 'support/intercepts/account'; +import { Flags } from 'src/featureFlags'; /** * Verifies the presence and values of specific properties within the aclpPreference object @@ -44,6 +45,24 @@ import { mockGetAccount } from 'support/intercepts/account'; * @param requestPayload - The payload received from the request, containing the aclpPreference object. * @param expectedValues - An object containing the expected values for properties to validate against the requestPayload. */ + +const flags: Partial = { + aclp: { enabled: true, beta: true }, + aclpResourceTypeMap: [ + { + dimensionKey: 'LINODE_ID', + maxResourceSelections: 10, + serviceType: 'linode', + supportedRegionIds: 'us-ord', + }, + { + dimensionKey: 'cluster_id', + maxResourceSelections: 10, + serviceType: 'dbaas', + supportedRegionIds: 'us-ord', + }, + ], +}; const { metrics, id, @@ -78,10 +97,9 @@ const metricDefinitions = { }; const mockRegion = regionFactory.build({ - capabilities: ['Linodes'], + capabilities: ['Managed Databases'], id: 'us-ord', label: 'Chicago, IL', - country: 'us', }); const databaseMock: Database = databaseFactory.build({ @@ -97,9 +115,7 @@ const mockAccount = accountFactory.build(); describe('Tests for API error handling', () => { beforeEach(() => { - mockAppendFeatureFlags({ - aclp: { beta: true, enabled: true }, - }); + mockAppendFeatureFlags(flags); mockGetAccount(mockAccount); mockGetCloudPulseMetricDefinitions(serviceType, metricDefinitions); mockGetCloudPulseDashboards(serviceType, [dashboard]).as('fetchDashboard'); diff --git a/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts index 1c63c435993..6f5f367e895 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/dbaas-widgets-verification.spec.ts @@ -27,7 +27,6 @@ import { mockGetAccount } from 'support/intercepts/account'; import { mockGetLinodes } from 'support/intercepts/linodes'; import { mockGetUserPreferences } from 'support/intercepts/profile'; import { mockGetRegions } from 'support/intercepts/regions'; -import { extendRegion } from 'support/util/regions'; import { CloudPulseMetricsResponse, Database } from '@linode/api-v4'; import { Interception } from 'cypress/types/net-stubbing'; import { generateRandomMetricsData } from 'support/util/cloudpulse'; @@ -49,14 +48,29 @@ import { formatToolTip } from 'src/features/CloudPulse/Utils/unitConversion'; const expectedGranularityArray = ['Auto', '1 day', '1 hr', '5 min']; const timeDurationToSelect = 'Last 24 Hours'; -const flags: Partial = { aclp: { enabled: true, beta: true } }; +const flags: Partial = { + aclp: { enabled: true, beta: true }, + aclpResourceTypeMap: [ + { + dimensionKey: 'LINODE_ID', + maxResourceSelections: 10, + serviceType: 'linode', + supportedRegionIds: '', + }, + { + dimensionKey: 'cluster_id', + maxResourceSelections: 10, + serviceType: 'dbaas', + supportedRegionIds: 'us-ord', + }, + ], +}; const { metrics, id, serviceType, dashboardName, - region, engine, clusterName, nodeType, @@ -91,14 +105,18 @@ const mockLinode = linodeFactory.build({ }); const mockAccount = accountFactory.build(); -const mockRegion = extendRegion( - regionFactory.build({ - capabilities: ['Linodes'], - id: 'us-ord', - label: 'Chicago, IL', - country: 'us', - }) -); + +const mockRegion = regionFactory.build({ + capabilities: ['Managed Databases'], + id: 'us-ord', + label: 'Chicago, IL', +}); + +const extendedMockRegion = regionFactory.build({ + capabilities: ['Managed Databases'], + id: 'us-east', + label: 'Newark,NL', +}); const metricsAPIResponsePayload = cloudPulseMetricsResponseFactory.build({ data: generateRandomMetricsData(timeDurationToSelect, '5 min'), }); @@ -151,9 +169,9 @@ const getWidgetLegendRowValuesFromResponse = ( }; const databaseMock: Database = databaseFactory.build({ - label: widgetDetails.dbaas.clusterName, - type: widgetDetails.dbaas.engine, - region: widgetDetails.dbaas.region, + label: clusterName, + type: engine, + region: mockRegion.label, version: '1', status: 'provisioning', cluster_size: 1, @@ -177,7 +195,7 @@ describe('Integration Tests for DBaaS Dashboard ', () => { mockCreateCloudPulseMetrics(serviceType, metricsAPIResponsePayload).as( 'getMetrics' ); - mockGetRegions([mockRegion]); + mockGetRegions([mockRegion, extendedMockRegion]); mockGetUserPreferences({}); mockGetDatabases([databaseMock]).as('getDatabases'); @@ -191,35 +209,60 @@ describe('Integration Tests for DBaaS Dashboard ', () => { ui.autocomplete .findByLabel('Dashboard') .should('be.visible') - .type(`${dashboardName}{enter}`) - .should('be.visible'); + .type(dashboardName); + + ui.autocompletePopper + .findByTitle(dashboardName) + .should('be.visible') + .click(); // Select a time duration from the autocomplete input. ui.autocomplete .findByLabel('Time Range') .should('be.visible') - .type(`${timeDurationToSelect}{enter}`) - .should('be.visible'); + .type(timeDurationToSelect); + + ui.autocompletePopper + .findByTitle(timeDurationToSelect) + .should('be.visible') + .click(); - //Select a Engine from the autocomplete input. + //Select a Database Engine from the autocomplete input. ui.autocomplete .findByLabel('Database Engine') .should('be.visible') - .type(`${engine}{enter}`) - .should('be.visible'); + .type(engine); + + ui.autocompletePopper.findByTitle(engine).should('be.visible').click(); + + // Select a region from the dropdown. + ui.regionSelect.find().click(); - // Select a region from the dropdown. - ui.regionSelect.find().click().type(`${region}{enter}`); + ui.regionSelect.find().type(extendedMockRegion.label); + + // Since DBaaS does not support this region, we expect it to not be in the dropdown. + + ui.autocompletePopper.find().within(() => { + cy.findByText( + `${extendedMockRegion.label} (${extendedMockRegion.id})` + ).should('not.exist'); + }); - // Select a resource from the autocomplete input. + ui.regionSelect.find().click().clear(); + ui.regionSelect + .findItemByRegionId(mockRegion.id, [mockRegion]) + .should('be.visible') + .click(); + + // Select a resource (Database Clusters) from the autocomplete input. ui.autocomplete .findByLabel('Database Clusters') .should('be.visible') - .type(`${clusterName}{enter}`) - .click(); - cy.findByText(clusterName).should('be.visible'); + .type(clusterName); + + ui.autocompletePopper.findByTitle(clusterName).should('be.visible').click(); - //Select a Node from the autocomplete input. + // Select a Node from the autocomplete input. ui.autocomplete .findByLabel('Node Type') .should('be.visible') diff --git a/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts b/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts index 303748e957a..7e49463a19d 100644 --- a/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts +++ b/packages/manager/cypress/e2e/core/cloudpulse/linode-widget-verification.spec.ts @@ -26,7 +26,6 @@ import { mockGetAccount } from 'support/intercepts/account'; import { mockGetLinodes } from 'support/intercepts/linodes'; import { mockGetUserPreferences } from 'support/intercepts/profile'; import { mockGetRegions } from 'support/intercepts/regions'; -import { extendRegion } from 'support/util/regions'; import { CloudPulseMetricsResponse } from '@linode/api-v4'; import { generateRandomMetricsData } from 'support/util/cloudpulse'; import { Interception } from 'cypress/types/net-stubbing'; @@ -46,15 +45,25 @@ import { formatToolTip } from 'src/features/CloudPulse/Utils/unitConversion'; */ const expectedGranularityArray = ['Auto', '1 day', '1 hr', '5 min']; const timeDurationToSelect = 'Last 24 Hours'; -const flags: Partial = { aclp: { enabled: true, beta: true } }; -const { - metrics, - id, - serviceType, - dashboardName, - region, - resource, -} = widgetDetails.linode; +const flags: Partial = { + aclp: { enabled: true, beta: true }, + aclpResourceTypeMap: [ + { + dimensionKey: 'LINODE_ID', + maxResourceSelections: 10, + serviceType: 'linode', + supportedRegionIds: 'us-ord', + }, + { + dimensionKey: 'cluster_id', + maxResourceSelections: 10, + serviceType: 'dbaas', + supportedRegionIds: '', + }, + ], +}; +const { metrics, id, serviceType, dashboardName, region, resource } = + widgetDetails.linode; const dashboard = dashboardFactory.build({ label: dashboardName, @@ -85,14 +94,18 @@ const mockLinode = linodeFactory.build({ }); const mockAccount = accountFactory.build(); -const mockRegion = extendRegion( - regionFactory.build({ - capabilities: ['Linodes'], - id: 'us-ord', - label: 'Chicago, IL', - country: 'us', - }) -); + +const mockRegion = regionFactory.build({ + capabilities: ['Linodes'], + id: 'us-ord', + label: 'Chicago, IL', +}); + +const extendedMockRegion = regionFactory.build({ + capabilities: ['Managed Databases'], + id: 'us-east', + label: 'Newark,NL', +}); const metricsAPIResponsePayload = cloudPulseMetricsResponseFactory.build({ data: generateRandomMetricsData(timeDurationToSelect, '5 min'), }); @@ -170,18 +183,41 @@ describe('Integration Tests for Linode Dashboard ', () => { ui.autocomplete .findByLabel('Dashboard') .should('be.visible') - .type(`${dashboardName}{enter}`) - .should('be.visible'); + .type(dashboardName); + + ui.autocompletePopper + .findByTitle(dashboardName) + .should('be.visible') + .click(); // Select a time duration from the autocomplete input. ui.autocomplete .findByLabel('Time Range') .should('be.visible') - .type(`${timeDurationToSelect}{enter}`) - .should('be.visible'); + .type(timeDurationToSelect); + + ui.autocompletePopper + .findByTitle(timeDurationToSelect) + .should('be.visible') + .click(); + + ui.regionSelect.find().click(); + + // Select a region from the dropdown. + ui.regionSelect.find().click(); + + ui.regionSelect.find().type(extendedMockRegion.label); + + // Since Linode does not support this region, we expect it to not be in the dropdown. + + ui.autocompletePopper.find().within(() => { + cy.findByText( + `${extendedMockRegion.label} (${extendedMockRegion.id})` + ).should('not.exist'); + }); // Select a region from the dropdown. - ui.regionSelect.find().click().type(`${region}{enter}`); + ui.regionSelect.find().click().clear().type(`${region}{enter}`); // Select a resource from the autocomplete input. ui.autocomplete @@ -191,6 +227,7 @@ describe('Integration Tests for Linode Dashboard ', () => { .click(); cy.findByText(resource).should('be.visible'); + // Wait for all metrics query requests to resolve. cy.wait(['@getMetrics', '@getMetrics', '@getMetrics', '@getMetrics']); }); From f5cfdb22bf3cdf308c6e32b55a65313a97a697d2 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 10 Dec 2024 11:09:45 +0530 Subject: [PATCH 16/20] upcoming: [DI-20595] - PR Comments --- .../features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 3a47701db75..6a1ed09de3e 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -26,7 +26,7 @@ const flags: Partial = { aclpResourceTypeMap: [ { serviceType: 'dbaas', - supportedRegionIds: 'us-west, us-east, ,', + supportedRegionIds: 'us-west, us-east', }, { serviceType: 'linode', From c94801d78259411d3fe74427c0e99d0d4648e986 Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Tue, 10 Dec 2024 11:10:10 +0530 Subject: [PATCH 17/20] upcoming: [DI-20595] - PR Comments --- .../features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index e4d0b325605..c35dc04272b 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -59,7 +59,10 @@ export const CloudPulseRegionSelect = React.memo( item.serviceType === serviceType ); - if (isNil(resourceTypeFlag?.supportedRegionIds)) { + if ( + resourceTypeFlag?.supportedRegionIds === null || + resourceTypeFlag?.supportedRegionIds === undefined + ) { return regions; } @@ -88,7 +91,7 @@ export const CloudPulseRegionSelect = React.memo( loading={isLoading} noMarginTop placeholder={placeholder ?? 'Select a Region'} - regions={supportedRegions ? supportedRegions : []} + regions={supportedRegions ?? []} value={selectedRegion} /> ); From e74d09f1bf2702d204d53d9b62f16b3a3d129a12 Mon Sep 17 00:00:00 2001 From: Ankita Date: Tue, 10 Dec 2024 19:34:08 +0530 Subject: [PATCH 18/20] upcoming: [DI-20595] - linting fix Co-authored-by: Purvesh Makode --- .../src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index c35dc04272b..5d6030d7741 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -1,4 +1,3 @@ -import { isNil } from 'ramda'; import * as React from 'react'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; From 9edb7995ccd7eeffe290170e900c27112d1f1c74 Mon Sep 17 00:00:00 2001 From: Ankita Date: Tue, 10 Dec 2024 19:38:03 +0530 Subject: [PATCH 19/20] upcoming: [DI-20595] - linting fix Co-authored-by: Purvesh Makode --- .../src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx index 5d6030d7741..e3ff3325c92 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.tsx @@ -51,7 +51,7 @@ export const CloudPulseRegionSelect = React.memo( // eslint-disable-next-line react-hooks/exhaustive-deps }, [regions]); - // validate launchDrakly region_ids with the ids from the fetched 'all-regions' + // validate launchDarkly region_ids with the ids from the fetched 'all-regions' const supportedRegions = React.useMemo(() => { const resourceTypeFlag = flags.aclpResourceTypeMap?.find( (item: CloudPulseResourceTypeMapFlag) => From 1cb3c4b717f9898940e7f170ae44c945800127fa Mon Sep 17 00:00:00 2001 From: Ankita Date: Tue, 10 Dec 2024 19:38:33 +0530 Subject: [PATCH 20/20] upcoming: [DI-20595] - linting fix Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com> --- .../features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx index 6a1ed09de3e..8e3a69b9696 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseRegionSelect.test.tsx @@ -113,7 +113,7 @@ describe('CloudPulseRegionSelect', () => { ); await user.click(getByRole('button', { name: 'Open' })); - // example: region id => 'us-west' belongs to service type - 'dbass', capability -'Managed Databases', and is supported via launchDarkly + // example: region id => 'us-west' belongs to service type - 'dbaas', capability -'Managed Databases', and is supported via launchDarkly expect( getByRole('option', { name: 'US, Fremont, CA (us-west)',