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

feat: [M3-6729] - Add VPC column to Linodes landing table #9485

Merged
merged 23 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5ad2576
add vpc column to linode landing pg table
coliu-akamai Aug 1, 2023
7c7aeac
Merge branch 'develop' into feat-m3-6729
coliu-akamai Aug 1, 2023
fd54d0d
roundabout way to get vpcs associated with a linode
coliu-akamai Aug 2, 2023
75fdffc
Merge branch 'develop' into feat-m3-6729
coliu-akamai Aug 2, 2023
c5cbae0
add comments and todos
coliu-akamai Aug 2, 2023
1701322
sortable table row for vpcs now
coliu-akamai Aug 3, 2023
5284cd0
remove unneeded comment
coliu-akamai Aug 3, 2023
8356a4d
...fix comment
coliu-akamai Aug 3, 2023
d146bbe
changeset and update test
coliu-akamai Aug 3, 2023
db8aa49
linode row trying to test for vpc column wip
coliu-akamai Aug 3, 2023
faf21e1
test vpc column shows up with feature flag
coliu-akamai Aug 3, 2023
2d653e1
updated linode row vpc column
coliu-akamai Aug 11, 2023
379b9cd
Added changeset: Update linode config interface return object as per …
coliu-akamai Aug 11, 2023
ef9c477
Merge branch 'develop' into feat-m3-6729
coliu-akamai Aug 11, 2023
17ad18f
Update packages/api-v4/.changeset/pr-9485-upcoming-features-169176419…
coliu-akamai Aug 11, 2023
87f492c
Update packages/manager/src/features/Linodes/LinodesLanding/LinodeRow…
coliu-akamai Aug 11, 2023
8451e7d
address feedback
coliu-akamai Aug 11, 2023
3a1c207
Added changeset: Update validation for `linodeInterfaceSchema`: inclu…
coliu-akamai Aug 11, 2023
20cfb48
Update packages/manager/src/features/Linodes/LinodesLanding/LinodeRow…
coliu-akamai Aug 14, 2023
f3fe379
address feedback @bnussman-akamai
coliu-akamai Aug 14, 2023
14d3938
Merge branch 'develop' into feat-m3-6729
coliu-akamai Aug 14, 2023
62577fa
update cypress test
coliu-akamai Aug 14, 2023
091181e
remove vpc_id from linode interface schema
coliu-akamai Aug 14, 2023
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
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-9485-changed-1691764196950.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Include `vpc_id` and rename `subnet` to `subnet_id` in Linode config interface return object ([#9485](https://github.com/linode/manager/pull/9485))
3 changes: 2 additions & 1 deletion packages/api-v4/src/linodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ export interface Interface {
purpose: InterfacePurpose;
ipam_address: string | null;
primary?: boolean;
subnet?: number | null;
subnet_id?: number | null;
vpc_id?: number | null;
ipv4?: ConfigInterfaceIPv4;
ipv6?: ConfigInterfaceIPv6;
ip_ranges?: string[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add VPC column to linodes landing page table ([#9485](https://github.com/linode/manager/pull/9485))
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ describe('linode landing checks', () => {
fbtVisible('Plan');
});
getVisible('[aria-label="Sort by ipv4[0]"]').within(() => {
fbtVisible('IP Address');
fbtVisible('Public IP Address');
});

getVisible(`tr[data-qa-linode="${label}"]`).within(() => {
Expand Down
19 changes: 19 additions & 0 deletions packages/manager/src/factories/linodeConfigInterfaceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,22 @@ export const LinodeConfigInterfaceFactory = Factory.Sync.makeFactory<Interface>(
purpose: 'vlan',
}
);

export const LinodeConfigInterfaceFactoryWithVPC = Factory.Sync.makeFactory<Interface>(
{
id: Factory.each((i) => i),
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',
},
ipv6: {
vpc: '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
},
ip_ranges: ['192.0.2.0/24'],
vpc_id: Factory.each((i) => i + 1),
subnet_id: Factory.each((i) => i),
}
);
8 changes: 7 additions & 1 deletion packages/manager/src/factories/linodeConfigs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Config } from '@linode/api-v4/lib/linodes/types';
import * as Factory from 'factory.ts';

import { LinodeConfigInterfaceFactory } from 'src/factories/linodeConfigInterfaceFactory';
import {
LinodeConfigInterfaceFactory,
LinodeConfigInterfaceFactoryWithVPC,
} from 'src/factories/linodeConfigInterfaceFactory';

const generateRandomId = () => Math.floor(Math.random() * 10000);

Expand All @@ -13,6 +16,8 @@ const [vlanInterface1, vlanInterface2] = LinodeConfigInterfaceFactory.buildList(
2
);

const vpcInterface = LinodeConfigInterfaceFactoryWithVPC.build();

export const linodeConfigFactory = Factory.Sync.makeFactory<Config>({
comments: '',
created: '2018-06-26T16:04:28',
Expand Down Expand Up @@ -49,6 +54,7 @@ export const linodeConfigFactory = Factory.Sync.makeFactory<Config>({
publicInterface,
vlanInterface1,
vlanInterface2,
vpcInterface,
],
kernel: 'linode/grub2',
label: 'My Arch Linux Disk Profile',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
import { shallow } from 'enzyme';
import * as React from 'react';

import { QueryClient } from 'react-query';

import { linodeFactory, linodeConfigFactory, vpcFactory } from 'src/factories';
import {
mockMatchMedia,
renderWithTheme,
wrapWithTableBody,
} from 'src/utilities/testHelpers';

import { mockNotification } from 'src/__data__/notifications';

import { RenderFlag } from './LinodeRow';
import { RenderFlag, LinodeRow } from './LinodeRow';

const queryClient = new QueryClient();

beforeAll(() => mockMatchMedia());
afterEach(() => {
queryClient.clear();
});

jest.mock('src/hooks/useFlags', () => ({
__esModule: true,
useFlags: jest.fn().mockReturnValue({ vpc: true }),
}));

jest.mock('src/queries/linodes/configs.ts', () => ({
useAllLinodeConfigsQuery: jest.fn().mockReturnValue({
data: linodeConfigFactory.buildList(1),
isLoading: false,
error: {},
}),
}));

jest.mock('src/queries/vpcs.ts', () => ({
useVPCQuery: jest.fn().mockReturnValue({
data: vpcFactory.build({ label: 'vpc-1' }),
isLoading: false,
error: {},
}),
}));

describe('LinodeRow', () => {
describe.skip('when Linode has notification', () => {
describe('when Linode has notification', () => {
it('should render a Flag', () => {
const wrapper = shallow(
<RenderFlag
Expand All @@ -15,10 +52,57 @@ describe('LinodeRow', () => {
/>
);

const Tooltip = wrapper.find('WithStyles(Tooltip)');
const Tooltip = wrapper.find('Tooltip');

expect(Tooltip).toHaveLength(1);
expect(Tooltip.props()).toHaveProperty('title', mockNotification.message);
});
});

// TODO: VPC - when a feature flag is no longer needed for vpc, this should be changed
it('should render a linode row with associated vpc information if the feature flag is on', () => {
const linode = linodeFactory.build();
const renderedLinode = (
<LinodeRow
handlers={{
onOpenDeleteDialog: () => {},
onOpenMigrateDialog: () => {},
onOpenPowerDialog: (action) => {},
onOpenRebuildDialog: () => {},
onOpenRescueDialog: () => {},
onOpenResizeDialog: () => {},
}}
alerts={linode.alerts}
backups={linode.backups}
created={linode.created}
group={linode.group}
hypervisor={linode.hypervisor}
id={linode.id}
image={linode.image}
ipv4={linode.ipv4}
ipv6={linode.ipv6 || ''}
key={`linode-row-${1}`}
label={linode.label}
region={linode.region}
specs={linode.specs}
status={linode.status}
tags={linode.tags}
type={linode.type}
updated={linode.updated}
watchdog_enabled={linode.watchdog_enabled}
/>
);

const { getByText } = renderWithTheme(
wrapWithTableBody(renderedLinode, { queryClient })
);

getByText('vpc-1');
getByText(linode.label);
getByText('Power Off');
getByText('Reboot');
getByText('Launch LISH Console');
getByText('Clone');
getByText('Resize');
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { Config } from '@linode/api-v4';
import { Notification } from '@linode/api-v4/lib/account';
import { Linode } from '@linode/api-v4/lib/linodes';
import { SxProps } from '@mui/system';
import * as React from 'react';

import Flag from 'src/assets/icons/flag.svg';
import { BackupStatus } from 'src/components/BackupStatus/BackupStatus';
import { Hidden } from 'src/components/Hidden';
import { Link } from 'src/components/Link';
import { Skeleton } from 'src/components/Skeleton';
import { StatusIcon } from 'src/components/StatusIcon/StatusIcon';
import { TableCell } from 'src/components/TableCell';
import { TableRow } from 'src/components/TableRow';
Expand All @@ -19,12 +20,15 @@ import {
transitionText,
} from 'src/features/Linodes/transitions';
import { notificationContext as _notificationContext } from 'src/features/NotificationCenter/NotificationContext';
import { useAllAccountMaintenanceQuery } from 'src/queries/accountMaintenance';
import { useNotificationsQuery } from 'src/queries/accountNotifications';
import { useAllLinodeConfigsQuery } from 'src/queries/linodes/configs';
import { useVPCQuery } from 'src/queries/vpcs';
import { useTypeQuery } from 'src/queries/types';
import { useFlags } from 'src/hooks/useFlags';
import { useRecentEventForLinode } from 'src/store/selectors/recentEventForLinode';
import { capitalizeAllWords } from 'src/utilities/capitalize';
import { formatStorageUnits } from 'src/utilities/formatStorageUnits';
import { LinodeWithMaintenance } from 'src/utilities/linodes';

import { IPAddress } from '../IPAddress';
import { LinodeActionMenu } from '../LinodeActionMenu';
Expand All @@ -37,23 +41,36 @@ import {
StyledMaintenanceTableCell,
} from './LinodeRow.styles';

type Props = Linode & { handlers: LinodeHandlers };
type Props = LinodeWithMaintenance & { handlers: LinodeHandlers };

export const LinodeRow = (props: Props) => {
const { backups, handlers, id, ipv4, label, region, status, type } = props;
const flags = useFlags();
const {
backups,
handlers,
id,
ipv4,
label,
region,
status,
type,
maintenance,
} = props;

const notificationContext = React.useContext(_notificationContext);

const { data: notifications } = useNotificationsQuery();

const { data: accountMaintenanceData } = useAllAccountMaintenanceQuery(
{},
{ status: { '+or': ['pending, started'] } }
// TODO: VPC - later if there is a way to directly get a linode's vpc, replace this
const { data: configs, isLoading: configsLoading } = useAllLinodeConfigsQuery(
id
);

const maintenance = accountMaintenanceData?.find(
(m) => m.entity.id === id && m.entity.type === 'linode'
const vpcId = getVPCId(configs ?? []);
const { data: vpc, isLoading: vpcLoading } = useVPCQuery(
vpcId ?? -1,
vpcId !== undefined && vpcId !== null
);
const vpcLabel = vpc?.label;

const linodeNotifications =
notifications?.filter(
Expand Down Expand Up @@ -163,6 +180,21 @@ export const LinodeRow = (props: Props) => {
<RegionIndicator region={region} />
</TableCell>
</Hidden>
{flags.vpc && (
<Hidden smDown>
<TableCell noWrap>
{vpcLoading || configsLoading ? (
<Skeleton />
) : vpcLabel ? (
<Link tabIndex={0} to={`/vpc/${vpcId}`}>
{vpcLabel}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have some kind of state for when there is no VPC attached?

Screenshot 2023-08-14 at 1 45 44 PM
Screenshot 2023-08-14 at 1 52 21 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Along the same lines: because we have to make a lot of API calls now, should we add a <Skeleton /> as a placeholder while we load the data? We do this for NodeBalancer Ports and it looks nice.

Screenshot 2023-08-14 at 1 56 52 PM

</Link>
) : (
'None'
Comment on lines +186 to +193
Copy link
Contributor Author

@coliu-akamai coliu-akamai Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bnussman-akamai added the skeleton / 'None'! I think the skeleton is definitely a nice addition for loading. The 'none' looked good too imo -- would we need to get UX approval for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks great! We probably should get UX approval. Can you reach out to Andrew?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Andrew approved! 🎉

)}
</TableCell>
</Hidden>
)}
</Hidden>
<Hidden lgDown>
<TableCell>
Expand Down Expand Up @@ -229,6 +261,18 @@ export const RenderFlag: React.FC<{
return null;
};

const getVPCId = (configs: Config[]) => {
for (const config of configs) {
for (const linodeInterface of config.interfaces) {
if (linodeInterface.purpose === 'vpc') {
return linodeInterface.vpc_id;
}
}
}

return undefined;
};

RenderFlag.displayName = `RenderFlag`;

export const ProgressDisplay: React.FC<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const ListView = (props: RenderLinodesProps) => {
type={linode.type}
updated={linode.updated}
watchdog_enabled={linode.watchdog_enabled}
maintenance={linode.maintenance}
/>
))}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TableHead } from 'src/components/TableHead';
import { TableRow } from 'src/components/TableRow';
import { TableSortCell } from 'src/components/TableSortCell';
import { Tooltip } from 'src/components/Tooltip';
import { useFlags } from 'src/hooks/useFlags';

import { StyledToggleButton } from './DisplayLinodes.styles';

Expand All @@ -29,6 +30,7 @@ type CombinedProps<T> = Props & Omit<OrderByProps<T>, 'data'>;
export const SortableTableHead = <T extends unknown>(
props: CombinedProps<T>
) => {
const flags = useFlags();
const theme = useTheme();

const {
Expand Down Expand Up @@ -116,8 +118,9 @@ export const SortableTableHead = <T extends unknown>(
direction={order}
handleClick={handleOrderChange}
label="ipv4[0]" // we want to sort by the first ipv4
noWrap
>
IP Address
Public IP Address
</TableSortCell>
<Hidden lgDown>
<TableSortCell
Expand All @@ -138,6 +141,11 @@ export const SortableTableHead = <T extends unknown>(
</TableSortCell>
</Hidden>
</Hidden>
{flags.vpc && (
<Hidden smDown>
<TableCell>VPC</TableCell>
</Hidden>
)}
<Hidden lgDown>
<TableSortCell
active={isActive('backups:last_successful')}
Expand Down
4 changes: 4 additions & 0 deletions packages/manager/src/mocks/serverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ const vpc = [
rest.put('*/vpcs/:vpcId', (req, res, ctx) => {
return res(ctx.json(vpcFactory.build({ description: 'testing' })));
}),
rest.get('*/vpcs/:vpcID', (req, res, ctx) => {
const id = Number(req.params.id);
return res(ctx.json(vpcFactory.build({ id })));
}),
];

const nanodeType = linodeTypeFactory.build({ id: 'g6-nanode-1' });
Expand Down
6 changes: 4 additions & 2 deletions packages/manager/src/queries/vpcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ export const useVPCsQuery = (params: Params, filter: Filter) => {
);
};

export const useVPCQuery = (id: number) => {
return useQuery<VPC, APIError[]>([vpcQueryKey, 'vpc', id], () => getVPC(id));
export const useVPCQuery = (id: number, enabled: boolean = true) => {
return useQuery<VPC, APIError[]>([vpcQueryKey, 'vpc', id], () => getVPC(id), {
enabled,
});
};

export const useCreateVPCMutation = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/validation": Changed
---

Update validation for `linodeInterfaceSchema`: include validation for `vpc_id` and update `subnet` to `subnet_id` ([#9485](https://github.com/linode/manager/pull/9485))
Loading