diff --git a/packages/api-v4/.changeset/pr-9824-upcoming-features-1697827003419.md b/packages/api-v4/.changeset/pr-9824-upcoming-features-1697827003419.md new file mode 100644 index 00000000000..6de5d7bca99 --- /dev/null +++ b/packages/api-v4/.changeset/pr-9824-upcoming-features-1697827003419.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Upcoming Features +--- + +Update the `Subnet` and `Interface` interfaces to match new API spec ([#9824](https://github.com/linode/manager/pull/9824)) diff --git a/packages/api-v4/src/linodes/types.ts b/packages/api-v4/src/linodes/types.ts index a1021d2258d..454267e227d 100644 --- a/packages/api-v4/src/linodes/types.ts +++ b/packages/api-v4/src/linodes/types.ts @@ -165,6 +165,7 @@ export interface Interface { purpose: InterfacePurpose; ipam_address: string | null; primary?: boolean; + active: boolean; subnet_id?: number | null; vpc_id?: number | null; ipv4?: ConfigInterfaceIPv4; @@ -172,7 +173,7 @@ export interface Interface { ip_ranges?: string[]; } -export type InterfacePayload = Omit; +export type InterfacePayload = Omit; export interface ConfigInterfaceOrderPayload { ids: number[]; diff --git a/packages/api-v4/src/vpcs/types.ts b/packages/api-v4/src/vpcs/types.ts index 789c4b39776..5df0c4d1d88 100644 --- a/packages/api-v4/src/vpcs/types.ts +++ b/packages/api-v4/src/vpcs/types.ts @@ -1,3 +1,5 @@ +import { Interface } from 'src/linodes'; + export interface VPC { id: number; label: string; @@ -28,7 +30,7 @@ export interface CreateSubnetPayload { export interface Subnet extends CreateSubnetPayload { id: number; - linodes: number[]; + linodes: SubnetAssignedLinodeData[]; created: string; updated: string; } @@ -36,3 +38,10 @@ export interface Subnet extends CreateSubnetPayload { export interface ModifySubnetPayload { label: string; } + +export type SubnetLinodeInterfaceData = Pick; + +export interface SubnetAssignedLinodeData { + id: number; + interfaces: SubnetLinodeInterfaceData[]; +} diff --git a/packages/manager/.changeset/pr-9824-upcoming-features-1697827056392.md b/packages/manager/.changeset/pr-9824-upcoming-features-1697827056392.md new file mode 100644 index 00000000000..18137e220a7 --- /dev/null +++ b/packages/manager/.changeset/pr-9824-upcoming-features-1697827056392.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Properly support new shapes of `Subnet` and `Interface` return objects ([#9824](https://github.com/linode/manager/pull/9824)) diff --git a/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts b/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts index ec8bb0bf6ad..d91f38034de 100644 --- a/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts +++ b/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts @@ -128,6 +128,7 @@ describe('VPC details page', () => { const mockSubnet = subnetFactory.build({ id: randomNumber(), label: randomLabel(), + linodes: [], }); const mockVPC = vpcFactory.build({ id: randomNumber(), diff --git a/packages/manager/src/factories/linodeConfigInterfaceFactory.ts b/packages/manager/src/factories/linodeConfigInterfaceFactory.ts index 99daac28cfd..7d1a0301f8e 100644 --- a/packages/manager/src/factories/linodeConfigInterfaceFactory.ts +++ b/packages/manager/src/factories/linodeConfigInterfaceFactory.ts @@ -3,6 +3,7 @@ import * as Factory from 'factory.ts'; export const LinodeConfigInterfaceFactory = Factory.Sync.makeFactory( { + active: false, id: Factory.each((i) => i), ipam_address: '10.0.0.1/24', label: Factory.each((i) => `interface-${i}`), @@ -12,19 +13,20 @@ export const LinodeConfigInterfaceFactory = Factory.Sync.makeFactory( export const LinodeConfigInterfaceFactoryWithVPC = Factory.Sync.makeFactory( { + active: false, id: Factory.each((i) => i), + ip_ranges: ['192.0.2.0/24'], ipam_address: '10.0.0.1/24', - label: Factory.each((i) => `interface-${i}`), - purpose: 'vpc', ipv4: { - vpc: '10.0.0.0', nat_1_1: 'some nat', + vpc: '10.0.0.0', }, ipv6: { vpc: '2001:0db8:85a3:0000:0000:8a2e:0370:7334', }, - ip_ranges: ['192.0.2.0/24'], - vpc_id: Factory.each((i) => i + 1), + label: Factory.each((i) => `interface-${i}`), + purpose: 'vpc', subnet_id: Factory.each((i) => i), + vpc_id: Factory.each((i) => i + 1), } ); diff --git a/packages/manager/src/factories/subnets.ts b/packages/manager/src/factories/subnets.ts index 5ff31fb7b0c..4e8bc5c5796 100644 --- a/packages/manager/src/factories/subnets.ts +++ b/packages/manager/src/factories/subnets.ts @@ -1,13 +1,31 @@ -import { Subnet } from '@linode/api-v4/lib/vpcs/types'; +import { + Subnet, + SubnetAssignedLinodeData, +} from '@linode/api-v4/lib/vpcs/types'; import * as Factory from 'factory.ts'; +// NOTE: Changing to fixed array length for the interfaces and linodes fields of the +// subnetAssignedLinodeDataFactory and subnetFactory respectively -- see [M3-7227] for more details + +export const subnetAssignedLinodeDataFactory = Factory.Sync.makeFactory( + { + id: Factory.each((i) => i), + interfaces: Array.from({ length: 5 }, () => ({ + active: false, + id: Math.floor(Math.random() * 100), + })), + } +); + export const subnetFactory = Factory.Sync.makeFactory({ created: '2023-07-12T16:08:53', id: Factory.each((i) => i), ipv4: '0.0.0.0/0', label: Factory.each((i) => `subnet-${i}`), - linodes: Factory.each((i) => - Array.from({ length: i }, () => Math.floor(Math.random() * 100)) + linodes: Array.from({ length: 5 }, () => + subnetAssignedLinodeDataFactory.build({ + id: Math.floor(Math.random() * 100), + }) ), updated: '2023-07-12T16:08:53', }); diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetail.test.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetail.test.tsx index 7e7832162d6..7ae27f80e17 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetail.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetail.test.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { accountFactory, linodeFactory, + subnetAssignedLinodeDataFactory, subnetFactory, vpcFactory, } from 'src/factories'; @@ -40,7 +41,7 @@ describe('Linode Entity Detail', () => { const subnet = subnetFactory.build({ id: 2, - linodes: [5], + linodes: [subnetAssignedLinodeDataFactory.build({ id: 5 })], }); const vpc = vpcFactory.build({ @@ -81,7 +82,7 @@ describe('Linode Entity Detail', () => { const subnet = subnetFactory.build({ id: 4, - linodes: [85], + linodes: [subnetAssignedLinodeDataFactory.build({ id: 85 })], }); const vpc = vpcFactory.build({ @@ -116,7 +117,7 @@ describe('Linode Entity Detail', () => { const subnet = subnetFactory.build({ id: 4, label: '1st-subnet', - linodes: [linode.id], + linodes: [subnetAssignedLinodeDataFactory.build({ id: linode.id })], }); const _vpcs = vpcFactory.buildList(3); diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx index a877c5fe2fa..64e7734be7a 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx @@ -366,7 +366,11 @@ export const Body = React.memo((props: BodyProps) => { const vpcLinodeIsAssignedTo = vpcsList.find((vpc) => { const subnets = vpc.subnets; - return Boolean(subnets.find((subnet) => subnet.linodes.includes(linodeId))); + return Boolean( + subnets.find((subnet) => + subnet.linodes.some((linodeInfo) => linodeInfo.id === linodeId) + ) + ); }); const { data: configs } = useAllLinodeConfigsQuery( diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx index 5923bd50b78..6b19eb9c872 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx @@ -108,7 +108,7 @@ export const SubnetAssignLinodesDrawer = ( // assigned to this subnet const findUnassignedLinodes = React.useCallback(() => { return linodes?.filter((linode) => { - return !subnet?.linodes.includes(linode.id); + return !subnet?.linodes.some((linodeInfo) => linodeInfo.id === linode.id); }); }, [subnet, linodes]); @@ -232,7 +232,9 @@ export const SubnetAssignLinodesDrawer = ( // Check if the selected Linode is already assigned to the subnet if ( values.selectedLinode && - subnet?.linodes.includes(values.selectedLinode.id) + subnet?.linodes.some( + (linodeInfo) => linodeInfo.id === values.selectedLinode?.id + ) ) { const configId = getConfigId(linodeConfigs, values.selectedConfig); @@ -280,7 +282,9 @@ export const SubnetAssignLinodesDrawer = ( // we want to remove it from assignedLinodesAndConfigData const isLinodeToRemoveValid = removedLinodeId.current !== -1 && - !subnet?.linodes.includes(removedLinodeId.current) && + !subnet?.linodes.some( + (linodeInfo) => linodeInfo.id === removedLinodeId.current + ) && !!assignedLinodesAndConfigData.find( (data) => data.id === removedLinodeId.current ); diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetUnassignLinodesDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetUnassignLinodesDrawer.tsx index 71050784fa0..63c09ffe58e 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetUnassignLinodesDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetUnassignLinodesDrawer.tsx @@ -84,7 +84,9 @@ export const SubnetUnassignLinodesDrawer = React.memo( // 2. We need to filter only the linodes that are assigned to the subnet. const findAssignedLinodes = React.useCallback(() => { return linodes?.filter((linode) => { - return subnetLinodeIds?.includes(linode.id); + return subnetLinodeIds?.some( + (linodeInfo) => linodeInfo.id === linode.id + ); }); }, [linodes, subnetLinodeIds]); diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx index cb243706692..bdc81a6b6c7 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx @@ -3,7 +3,10 @@ import { waitForElementToBeRemoved } from '@testing-library/react'; import * as React from 'react'; import { QueryClient } from 'react-query'; -import { subnetFactory } from 'src/factories/subnets'; +import { + subnetAssignedLinodeDataFactory, + subnetFactory, +} from 'src/factories/subnets'; import { makeResourcePage } from 'src/mocks/serverHandlers'; import { rest, server } from 'src/mocks/testServer'; import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; @@ -21,7 +24,13 @@ const loadingTestId = 'circle-progress'; describe('VPC Subnets table', () => { it('should display filter input, subnet label, id, ip range, number of linodes, and action menu', async () => { - const subnet = subnetFactory.build({ linodes: [1, 2, 3] }); + const subnet = subnetFactory.build({ + linodes: [ + subnetAssignedLinodeDataFactory.build({ id: 1 }), + subnetAssignedLinodeDataFactory.build({ id: 2 }), + subnetAssignedLinodeDataFactory.build({ id: 3 }), + ], + }); server.use( rest.get('*/vpcs/:vpcId/subnets', (req, res, ctx) => { return res(ctx.json(makeResourcePage([subnet]))); @@ -81,7 +90,9 @@ describe('VPC Subnets table', () => { }); it('should show linode table head data when table is expanded', async () => { - const subnet = subnetFactory.build({ linodes: [1] }); + const subnet = subnetFactory.build({ + linodes: [subnetAssignedLinodeDataFactory.build({ id: 1 })], + }); server.use( rest.get('*/vpcs/:vpcId/subnets', (req, res, ctx) => { return res(ctx.json(makeResourcePage([subnet]))); diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx index 6722b7e854b..b338229b44f 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx @@ -230,11 +230,11 @@ export const VPCSubnetsTable = (props: Props) => { {subnet.linodes.length > 0 ? ( - subnet.linodes.map((linodeId) => ( + subnet.linodes.map((linodeInfo) => ( diff --git a/packages/manager/src/features/VPCs/utils.test.ts b/packages/manager/src/features/VPCs/utils.test.ts index 5acd9159b7a..84ec44a4a07 100644 --- a/packages/manager/src/features/VPCs/utils.test.ts +++ b/packages/manager/src/features/VPCs/utils.test.ts @@ -1,26 +1,50 @@ import { LinodeConfigInterfaceFactoryWithVPC } from 'src/factories/linodeConfigInterfaceFactory'; import { linodeConfigFactory } from 'src/factories/linodeConfigs'; -import { subnetFactory } from 'src/factories/subnets'; +import { + subnetAssignedLinodeDataFactory, + subnetFactory, +} from 'src/factories/subnets'; import { getSubnetInterfaceFromConfigs, getUniqueLinodesFromSubnets, } from './utils'; +const subnetLinodeInfoList1 = subnetAssignedLinodeDataFactory.buildList(4); +const subnetLinodeInfoId1 = subnetAssignedLinodeDataFactory.build({ id: 1 }); +const subnetLinodeInfoId3 = subnetAssignedLinodeDataFactory.build({ id: 3 }); + describe('getUniqueLinodesFromSubnets', () => { it(`returns the number of unique linodes within a VPC's subnets`, () => { const subnets0 = [subnetFactory.build({ linodes: [] })]; - const subnets1 = [subnetFactory.build({ linodes: [1, 2, 3] })]; - const subnets2 = [subnetFactory.build({ linodes: [1, 1, 3, 3] })]; + const subnets1 = [subnetFactory.build({ linodes: subnetLinodeInfoList1 })]; + const subnets2 = [ + subnetFactory.build({ + linodes: [ + subnetLinodeInfoId1, + subnetLinodeInfoId1, + subnetLinodeInfoId3, + subnetLinodeInfoId3, + ], + }), + ]; const subnets3 = [ - subnetFactory.build({ linodes: [1, 2, 3] }), + subnetFactory.build({ linodes: subnetLinodeInfoList1 }), subnetFactory.build({ linodes: [] }), - subnetFactory.build({ linodes: [3] }), - subnetFactory.build({ linodes: [6, 7, 8, 9, 1] }), + subnetFactory.build({ linodes: [subnetLinodeInfoId3] }), + subnetFactory.build({ + linodes: [ + subnetAssignedLinodeDataFactory.build({ id: 6 }), + subnetAssignedLinodeDataFactory.build({ id: 7 }), + subnetAssignedLinodeDataFactory.build({ id: 8 }), + subnetAssignedLinodeDataFactory.build({ id: 9 }), + subnetLinodeInfoId1, + ], + }), ]; expect(getUniqueLinodesFromSubnets(subnets0)).toBe(0); - expect(getUniqueLinodesFromSubnets(subnets1)).toBe(3); + expect(getUniqueLinodesFromSubnets(subnets1)).toBe(4); expect(getUniqueLinodesFromSubnets(subnets2)).toBe(2); expect(getUniqueLinodesFromSubnets(subnets3)).toBe(7); }); diff --git a/packages/manager/src/features/VPCs/utils.ts b/packages/manager/src/features/VPCs/utils.ts index c727788ca59..62b86b931f1 100644 --- a/packages/manager/src/features/VPCs/utils.ts +++ b/packages/manager/src/features/VPCs/utils.ts @@ -3,9 +3,9 @@ import type { Config, Subnet } from '@linode/api-v4'; export const getUniqueLinodesFromSubnets = (subnets: Subnet[]) => { const linodes: number[] = []; for (const subnet of subnets) { - subnet.linodes.forEach((linodeId) => { - if (!linodes.includes(linodeId)) { - linodes.push(linodeId); + subnet.linodes.forEach((linodeInfo) => { + if (!linodes.includes(linodeInfo.id)) { + linodes.push(linodeInfo.id); } }); }