From 761ebc11698f72f601f6117328990220e70b73e3 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Fri, 28 Feb 2025 15:29:14 -0500 Subject: [PATCH 1/6] filter out linodes in distributed regions from the linode clone and backups tab --- .../shared/LinodeSelectTable.test.tsx | 11 +++++++- .../LinodeCreate/shared/LinodeSelectTable.tsx | 28 +++++++++++++------ packages/manager/src/hooks/useOrder.ts | 6 +++- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx index c2feffafb1c..2d0f1937aa1 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx @@ -9,11 +9,20 @@ import { renderWithThemeAndHookFormContext, } from 'src/utilities/testHelpers'; -import { LinodeSelectTable } from './LinodeSelectTable'; +import { LinodeSelectTable, getLinodeXFilter } from './LinodeSelectTable'; beforeAll(() => mockMatchMedia()); describe('Linode Select Table', () => { + it('should filter out Linodes in distributed regions', async () => { + const xFilter = getLinodeXFilter(''); + + expect(xFilter).toStrictEqual({ + '+or': [{ label: { '+contains': '' } }], + site_type: 'core', + }); + }); + it('should render Linodes from the API', async () => { const linodes = linodeFactory.buildList(10); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx index 61bbfad27cb..7af96d2eab5 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx @@ -34,6 +34,7 @@ import { SelectLinodeCard } from './SelectLinodeCard'; import type { LinodeCreateFormValues } from '../utilities'; import type { Linode } from '@linode/api-v4'; import type { Theme } from '@mui/material'; +import type { UseOrder } from 'src/hooks/useOrder'; interface Props { /** @@ -81,15 +82,7 @@ export const LinodeSelectTable = (props: Props) => { const filter = preselectedLinodeId ? { id: preselectedLinodeId } - : { - '+or': [ - { label: { '+contains': query } }, - ...(isNumeric(query) ? [{ id: Number(query) }] : []), // let users filter by Linode id - ], - '+order': order.order, - '+order_by': order.orderBy, - // backups: { enabled: true }, womp womp! We can't filter on values within objects - }; + : getLinodeXFilter(query, order); const { data, error, isFetching, isLoading } = useLinodesQuery( { @@ -240,3 +233,20 @@ export const LinodeSelectTable = (props: Props) => { ); }; + +export const getLinodeXFilter = (query: string, order?: UseOrder) => { + const filter = { + '+or': [ + { label: { '+contains': query } }, + ...(isNumeric(query) ? [{ id: Number(query) }] : []), // let users filter by Linode id + ], + site_type: 'core', // backups and cloning are not supported for distributed regions + // backups: { enabled: true }, womp womp! We can't filter on values within objects + }; + + if (order) { + return { ...filter, '+order': order.order, '+order_by': order.orderBy }; + } + + return filter; +}; diff --git a/packages/manager/src/hooks/useOrder.ts b/packages/manager/src/hooks/useOrder.ts index f5e9d41abc8..ab28f230f3d 100644 --- a/packages/manager/src/hooks/useOrder.ts +++ b/packages/manager/src/hooks/useOrder.ts @@ -14,6 +14,10 @@ import type { BaseQueryParams } from 'src/utilities/queryParams'; export type Order = 'asc' | 'desc'; +export interface UseOrder extends OrderSet { + handleOrderChange: (newOrderBy: string, newOrder: Order) => void; +} + /** * useOrder is a hook that allows you to handle ordering tables. It takes into account * the following items when determining initial order @@ -31,7 +35,7 @@ export const useOrder = ( initial?: OrderSet, preferenceKey?: string, prefix?: string -) => { +): UseOrder => { const { data: sortPreferences } = usePreferences( (preferences) => preferences?.sortKeys ); From 028caf7ddb0b0c78cd31fc4dbc22995c2f4cd5f2 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Fri, 28 Feb 2025 15:47:06 -0500 Subject: [PATCH 2/6] Added changeset: Filter out Linodes in distributed regions from Create flow Backups & Clone tab --- packages/manager/.changeset/pr-11767-fixed-1740775625877.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-11767-fixed-1740775625877.md diff --git a/packages/manager/.changeset/pr-11767-fixed-1740775625877.md b/packages/manager/.changeset/pr-11767-fixed-1740775625877.md new file mode 100644 index 00000000000..46dab168c42 --- /dev/null +++ b/packages/manager/.changeset/pr-11767-fixed-1740775625877.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Filter out Linodes in distributed regions from Create flow Backups & Clone tab ([#11767](https://github.com/linode/manager/pull/11767)) From 5ada6205b32ed0f35fb28b8b1c027ca68608c0a3 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Fri, 28 Feb 2025 15:47:52 -0500 Subject: [PATCH 3/6] clean up --- .../Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx index 2d0f1937aa1..12f122bff16 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx @@ -14,7 +14,7 @@ import { LinodeSelectTable, getLinodeXFilter } from './LinodeSelectTable'; beforeAll(() => mockMatchMedia()); describe('Linode Select Table', () => { - it('should filter out Linodes in distributed regions', async () => { + it('should filter out Linodes in distributed regions', () => { const xFilter = getLinodeXFilter(''); expect(xFilter).toStrictEqual({ From 76c1a77acaef17fb7b536ccf9895c23762380c4d Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Mon, 3 Mar 2025 14:04:56 -0500 Subject: [PATCH 4/6] address feedback --- .../LinodeCreate/shared/LinodeSelectTable.test.tsx | 13 +++++++++++-- .../LinodeCreate/shared/LinodeSelectTable.tsx | 11 ++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx index 12f122bff16..a864dc6a354 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx @@ -17,8 +17,17 @@ describe('Linode Select Table', () => { it('should filter out Linodes in distributed regions', () => { const xFilter = getLinodeXFilter(''); - expect(xFilter).toStrictEqual({ - '+or': [{ label: { '+contains': '' } }], + expect(xFilter).toHaveProperty('site_type', 'core'); + }); + + it('should search for both label and id', () => { + const xFilter = getLinodeXFilter('12345678'); + + expect(xFilter).toContain({ + '+or': [ + { label: { '+contains': '12345678' } }, + { id: { '+contains': '12345678' } }, + ], site_type: 'core', }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx index 7af96d2eab5..f823a954cdd 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx @@ -1,3 +1,4 @@ +import { getAPIFilterFromQuery } from '@linode/search'; import { Box, Notice, Stack, Typography } from '@linode/ui'; import Grid from '@mui/material/Grid2'; import useMediaQuery from '@mui/material/useMediaQuery'; @@ -22,7 +23,6 @@ import { usePagination } from 'src/hooks/usePagination'; import { useLinodesQuery } from 'src/queries/linodes/linodes'; import { sendLinodePowerOffEvent } from 'src/utilities/analytics/customEventAnalytics'; import { isPrivateIP } from 'src/utilities/ipUtils'; -import { isNumeric } from 'src/utilities/stringUtils'; import { getGeneratedLinodeLabel, @@ -235,11 +235,12 @@ export const LinodeSelectTable = (props: Props) => { }; export const getLinodeXFilter = (query: string, order?: UseOrder) => { + const { filter: apiFilter } = getAPIFilterFromQuery(query, { + searchableFieldsWithoutOperator: ['label', 'id'], + }); + const filter = { - '+or': [ - { label: { '+contains': query } }, - ...(isNumeric(query) ? [{ id: Number(query) }] : []), // let users filter by Linode id - ], + ...apiFilter, site_type: 'core', // backups and cloning are not supported for distributed regions // backups: { enabled: true }, womp womp! We can't filter on values within objects }; From 76b756d3b8a8555b5bcda91b8b3821a40226ba4c Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Mon, 3 Mar 2025 14:51:10 -0500 Subject: [PATCH 5/6] fix unit test --- .../Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx index a864dc6a354..c5f1e987232 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx @@ -23,7 +23,7 @@ describe('Linode Select Table', () => { it('should search for both label and id', () => { const xFilter = getLinodeXFilter('12345678'); - expect(xFilter).toContain({ + expect(xFilter).toStrictEqual({ '+or': [ { label: { '+contains': '12345678' } }, { id: { '+contains': '12345678' } }, From 9376b496f2eca36c8de6faade37bd9b2f6ee9b18 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 4 Mar 2025 10:42:33 -0500 Subject: [PATCH 6/6] feedback --- .../shared/LinodeSelectTable.test.tsx | 21 ++++++++--- .../LinodeCreate/shared/LinodeSelectTable.tsx | 37 ++++++++++++++----- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx index c5f1e987232..7755d89ffb5 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.test.tsx @@ -15,23 +15,34 @@ beforeAll(() => mockMatchMedia()); describe('Linode Select Table', () => { it('should filter out Linodes in distributed regions', () => { - const xFilter = getLinodeXFilter(''); + const { filter } = getLinodeXFilter(undefined, ''); - expect(xFilter).toHaveProperty('site_type', 'core'); + expect(filter).toHaveProperty('site_type', 'core'); }); - it('should search for both label and id', () => { - const xFilter = getLinodeXFilter('12345678'); + it('should search for label, id, ipv4, tags', () => { + const { filter } = getLinodeXFilter(undefined, '12345678'); - expect(xFilter).toStrictEqual({ + expect(filter).toStrictEqual({ '+or': [ { label: { '+contains': '12345678' } }, { id: { '+contains': '12345678' } }, + { ipv4: { '+contains': '12345678' } }, + { tags: { '+contains': '12345678' } }, ], site_type: 'core', }); }); + it('should return an error if the x-filter is invalid', () => { + const { filterError } = getLinodeXFilter(undefined, '123 456'); + + expect(filterError).toHaveProperty( + 'message', + `Expected "!=", "<", "<=", "=", ">", ">=", [:~], or whitespace but "4" found.` + ); + }); + it('should render Linodes from the API', async () => { const linodes = linodeFactory.buildList(10); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx index f823a954cdd..0c7e7ec9811 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/shared/LinodeSelectTable.tsx @@ -80,9 +80,11 @@ export const LinodeSelectTable = (props: Props) => { const pagination = usePagination(); const order = useOrder(); - const filter = preselectedLinodeId - ? { id: preselectedLinodeId } - : getLinodeXFilter(query, order); + const { filter, filterError } = getLinodeXFilter( + preselectedLinodeId, + query, + order + ); const { data, error, isFetching, isLoading } = useLinodesQuery( { @@ -139,6 +141,7 @@ export const LinodeSelectTable = (props: Props) => { }} clearable debounceTime={250} + errorText={filterError?.message} hideLabel isSearching={isFetching} label="Search" @@ -234,10 +237,23 @@ export const LinodeSelectTable = (props: Props) => { ); }; -export const getLinodeXFilter = (query: string, order?: UseOrder) => { - const { filter: apiFilter } = getAPIFilterFromQuery(query, { - searchableFieldsWithoutOperator: ['label', 'id'], - }); +export const getLinodeXFilter = ( + preselectedLinodeId: number | undefined, + query: string, + order?: UseOrder +) => { + if (preselectedLinodeId) { + return { + id: preselectedLinodeId, + }; + } + + const { error: filterError, filter: apiFilter } = getAPIFilterFromQuery( + query, + { + searchableFieldsWithoutOperator: ['label', 'id', 'ipv4', 'tags'], + } + ); const filter = { ...apiFilter, @@ -246,8 +262,11 @@ export const getLinodeXFilter = (query: string, order?: UseOrder) => { }; if (order) { - return { ...filter, '+order': order.order, '+order_by': order.orderBy }; + return { + filter: { ...filter, '+order': order.order, '+order_by': order.orderBy }, + filterError, + }; } - return filter; + return { filter, filterError }; };