Skip to content

Commit e534839

Browse files
committed
filtering but at what cost
1 parent db3c59d commit e534839

File tree

2 files changed

+95
-57
lines changed

2 files changed

+95
-57
lines changed

Diff for: packages/manager/src/features/Firewalls/FirewallDetail/Devices/AddLinodeDrawer.tsx

+93-52
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getLinodeInterfaces } from '@linode/api-v4';
22
import {
33
useAddFirewallDeviceMutation,
44
useAllFirewallsQuery,
5+
useAllLinodesQuery,
56
useGrants,
67
useProfile,
78
} from '@linode/queries';
@@ -18,6 +19,7 @@ import { SupportLink } from 'src/components/SupportLink';
1819
import { getLinodeInterfaceType } from 'src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/utilities';
1920
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
2021
import { getEntityIdsByPermission } from 'src/utilities/grants';
22+
import { useIsLinodeInterfacesEnabled } from 'src/utilities/linodes';
2123
import { sanitizeHTML } from 'src/utilities/sanitizeHTML';
2224

2325
import type { Linode, LinodeInterface } from '@linode/api-v4';
@@ -54,9 +56,12 @@ export const AddLinodeDrawer = (props: Props) => {
5456

5557
const { data: grants } = useGrants();
5658
const { data: profile } = useProfile();
59+
const { isLinodeInterfacesEnabled } = useIsLinodeInterfacesEnabled();
5760
const isRestrictedUser = Boolean(profile?.restricted);
5861

5962
const { data, error, isLoading } = useAllFirewallsQuery();
63+
const { data: allLinodes } = useAllLinodesQuery({}, {});
64+
const [linodeOptions, setLinodeOptions] = React.useState<Linode[]>([]);
6065

6166
const firewall = data?.find((firewall) => firewall.id === Number(id));
6267

@@ -214,17 +219,15 @@ export const AddLinodeDrawer = (props: Props) => {
214219
? getEntityIdsByPermission(grants, 'linode', 'read_only')
215220
: [];
216221

217-
const assignedLinodes = data
218-
?.map((firewall) => firewall.entities)
219-
.flat()
220-
?.filter((service) => service.type === 'linode');
221-
222-
const linodeOptionsFilter = (linode: Linode) => {
223-
return (
224-
!readOnlyLinodeIds.includes(linode.id) &&
225-
!assignedLinodes?.some((service) => service.id === linode.id)
226-
);
227-
};
222+
const firewallEntities = data?.map((firewall) => firewall.entities).flat();
223+
const assignedLinodes = firewallEntities?.filter(
224+
(service) => service.type === 'linode'
225+
);
226+
const assignedInterfaceIds = new Set<number>(
227+
firewallEntities
228+
?.filter((service) => service.type === 'interface')
229+
?.map((service) => service.id) ?? []
230+
);
228231

229232
const onSelectionChange = async (linodes: Linode[]) => {
230233
setLocalError('');
@@ -233,10 +236,10 @@ export const AddLinodeDrawer = (props: Props) => {
233236
const _interfacesToAddMap = new Map<number, InterfaceDeviceInfo | null>();
234237

235238
for (const linode of linodes) {
236-
if (linode.interface_generation === 'legacy_config') {
237-
legacyLinodes.push(linode);
238-
} else {
239+
if (linode.interface_generation === 'linode') {
239240
interfaceLinodes.push(linode);
241+
} else {
242+
legacyLinodes.push(linode);
240243
}
241244
}
242245

@@ -247,19 +250,19 @@ export const AddLinodeDrawer = (props: Props) => {
247250
const linodeId = linode.id;
248251
const interfaces = await getLinodeInterfaces(linodeId);
249252
// vlan interfaces cannot have a firewall assigned to them
250-
const nonVlanInterfaces = interfaces.interfaces.filter(
251-
(iface) => !iface.vlan
253+
const assignableInterfaces = interfaces.interfaces.filter(
254+
(iface) => !iface.vlan && !assignedInterfaceIds.has(iface.id)
252255
);
253256

254-
if (nonVlanInterfaces.length === 1) {
257+
if (assignableInterfaces.length === 1) {
255258
_interfacesToAddMap.set(linodeId, {
256259
linodeId,
257260
linodeLabel: linode.label,
258-
interfaceId: nonVlanInterfaces[0].id,
261+
interfaceId: assignableInterfaces[0].id,
259262
});
260263
}
261264

262-
if (nonVlanInterfaces.length > 1) {
265+
if (assignableInterfaces.length > 1) {
263266
if (!interfacesToAddMap.has(linodeId)) {
264267
_interfacesToAddMap.set(linodeId, null);
265268
} else {
@@ -269,7 +272,7 @@ export const AddLinodeDrawer = (props: Props) => {
269272
);
270273
}
271274

272-
const interfacesWithLabels = nonVlanInterfaces.map((iface) => ({
275+
const interfacesWithLabels = assignableInterfaces.map((iface) => ({
273276
...iface,
274277
label: `${getLinodeInterfaceType(iface)} Interface (ID : ${iface.id})`,
275278
value: iface.id,
@@ -294,12 +297,6 @@ export const AddLinodeDrawer = (props: Props) => {
294297
setInterfacesToAddMap(_interfacesToAddMap);
295298
};
296299

297-
React.useEffect(() => {
298-
if (error) {
299-
setLocalError('Could not load firewall data');
300-
}
301-
}, [error]);
302-
303300
const handleClose = () => {
304301
setLinodesToAdd([]);
305302
setInterfacesToAddMap(new Map());
@@ -308,6 +305,49 @@ export const AddLinodeDrawer = (props: Props) => {
308305
onClose();
309306
};
310307

308+
React.useEffect(() => {
309+
const linodeOptionsFilter = async (linode: Linode) => {
310+
if (linode.interface_generation === 'linode') {
311+
const interfaces = await getLinodeInterfaces(linode.id);
312+
// Return true if Linode has some non-vlan interface that is not assigned to a firewall
313+
return (
314+
!readOnlyLinodeIds.includes(linode.id) &&
315+
interfaces.interfaces.some(
316+
(iface) => !iface.vlan && !assignedInterfaceIds.has(iface.id)
317+
)
318+
);
319+
}
320+
return (
321+
!readOnlyLinodeIds.includes(linode.id) &&
322+
!assignedLinodes?.some((service) => service.id === linode.id)
323+
);
324+
};
325+
326+
const filterLinodes = async () => {
327+
const asyncFilteredDataPromises = allLinodes?.map((linode) =>
328+
linodeOptionsFilter(linode)
329+
);
330+
const predicateArr = await Promise.all(asyncFilteredDataPromises ?? []);
331+
const filteredLinodes = allLinodes?.filter((_, idx) => predicateArr[idx]);
332+
333+
setLinodeOptions(filteredLinodes ?? []);
334+
};
335+
336+
filterLinodes();
337+
// eslint-disable-next-line react-hooks/exhaustive-deps
338+
}, [
339+
allLinodes?.length,
340+
assignedLinodes?.length,
341+
readOnlyLinodeIds?.length,
342+
assignedInterfaceIds.size,
343+
]);
344+
345+
React.useEffect(() => {
346+
if (error) {
347+
setLocalError('Could not load firewall data');
348+
}
349+
}, [error]);
350+
311351
return (
312352
<Drawer
313353
NotFoundComponent={NotFound}
@@ -327,42 +367,43 @@ export const AddLinodeDrawer = (props: Props) => {
327367
helperText={helperText}
328368
multiple
329369
onSelectionChange={(linodes) => onSelectionChange(linodes)}
330-
optionsFilter={linodeOptionsFilter}
370+
options={linodeOptions}
331371
value={[
332372
...linodesToAdd.map((linode) => linode.id),
333373
...Array.from(interfacesToAddMap.keys()),
334374
]}
335375
/>
336-
{linodesWithMultiInterfaces.length > 0 && (
376+
{isLinodeInterfacesEnabled && linodesWithMultiInterfaces.length > 0 && (
337377
<Typography marginTop={3}>
338378
{`The following ${linodesWithMultiInterfaces.length === 1 ? 'Linode has' : 'Linodes have'}
339379
more than one interface to which a firewall can be applied. Select which interface.`}
340380
</Typography>
341381
)}
342-
{linodesWithMultiInterfaces.map((linode) => (
343-
<Select
344-
key={linode.linodeId}
345-
label={`${linode.linodeLabel} Interface`}
346-
onChange={(e, option) => {
347-
const updatedInterfacesToAdd = new Map(interfacesToAddMap);
348-
updatedInterfacesToAdd.set(linode.linodeId, {
349-
linodeId: linode.linodeId,
350-
linodeLabel: linode.linodeLabel,
351-
interfaceId: option.value,
352-
});
353-
setInterfacesToAddMap(updatedInterfacesToAdd);
354-
}}
355-
options={linode.linodeInterfaces}
356-
placeholder="Select Interface"
357-
value={
358-
linode.linodeInterfaces.find(
359-
(iface) =>
360-
iface.id ===
361-
interfacesToAddMap.get(linode.linodeId)?.interfaceId
362-
) ?? null
363-
}
364-
/>
365-
))}
382+
{isLinodeInterfacesEnabled &&
383+
linodesWithMultiInterfaces.map((linode) => (
384+
<Select
385+
key={linode.linodeId}
386+
label={`${linode.linodeLabel} Interface`}
387+
onChange={(e, option) => {
388+
const updatedInterfacesToAdd = new Map(interfacesToAddMap);
389+
updatedInterfacesToAdd.set(linode.linodeId, {
390+
linodeId: linode.linodeId,
391+
linodeLabel: linode.linodeLabel,
392+
interfaceId: option.value,
393+
});
394+
setInterfacesToAddMap(updatedInterfacesToAdd);
395+
}}
396+
options={linode.linodeInterfaces}
397+
placeholder="Select Interface"
398+
value={
399+
linode.linodeInterfaces.find(
400+
(iface) =>
401+
iface.id ===
402+
interfacesToAddMap.get(linode.linodeId)?.interfaceId
403+
) ?? null
404+
}
405+
/>
406+
))}
366407
<ActionsPanel
367408
primaryButtonProps={{
368409
disabled:

Diff for: packages/manager/src/features/Firewalls/FirewallLanding/FirewallRow.tsx

+2-5
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,8 @@ export const FirewallRow = React.memo((props: FirewallRowProps) => {
3030

3131
const { isLinodeInterfacesEnabled } = useIsLinodeInterfacesEnabled();
3232

33-
const {
34-
defaultNumEntities,
35-
isDefault,
36-
tooltipText,
37-
} = useDefaultFirewallChipInformation(id);
33+
const { defaultNumEntities, isDefault, tooltipText } =
34+
useDefaultFirewallChipInformation(id);
3835

3936
const neededLinodeIdsForInterfaceDevices = entities
4037
.slice(0, 3) // only take the first three entities since we only show those entity links

0 commit comments

Comments
 (0)