Skip to content

Commit

Permalink
Merge pull request #1963 from deepfence/es-2122-name-fixes
Browse files Browse the repository at this point in the history
Fix node name issues in applied filters
  • Loading branch information
manV authored Feb 20, 2024
2 parents 188466a + 342bd88 commit 0cb2f10
Show file tree
Hide file tree
Showing 19 changed files with 1,232 additions and 269 deletions.
1 change: 1 addition & 0 deletions deepfence_frontend/apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@suspensive/react": "^1.11.6",
"@suspensive/react-query": "^1.11.6",
"@tanstack/react-query": "^4.32.6",
"@yornaath/batshit": "^0.9.0",
"cron-converter": "^2.0.1",
"cronstrue": "^2.41.0",
"dayjs": "^1.11.9",
Expand Down
1 change: 1 addition & 0 deletions deepfence_frontend/apps/dashboard/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ export function getLookupApiClient() {
lookupMalwares: lookupApi.getMalwares.bind(lookupApi),
lookupCompliances: lookupApi.getCompliances.bind(lookupApi),
lookupCloudCompliances: lookupApi.getCloudCompliances.bind(lookupApi),
lookupRegistryAccounts: lookupApi.getRegistryAccount.bind(lookupApi),
};
}

Expand Down
224 changes: 208 additions & 16 deletions deepfence_frontend/apps/dashboard/src/components/filters/FilterBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,217 @@
import { useSuspenseQuery } from '@suspensive/react-query';
import { ReactNode, Suspense } from 'react';

import { TimesIcon } from '@/components/icons/common/Times';
import {
lookupClusterBatcher,
lookupContainerBatcher,
lookupContainerImagesBatcher,
lookupHostsBatcher,
lookupPodBatcher,
lookupRegistryAccountBatcher,
} from '@/queries/batchers/lookup';

const Wrapper = ({ children }: { children: ReactNode }) => {
return (
<div className="flex items-center py-1 px-2.5 dark:border dark:border-accent-accent rounded-[11px] gap-2">
{children}
</div>
);
};

const Text = ({ children }: { children: ReactNode }) => {
return (
<div className="text-p8 dark:text-text-input-value truncate max-w-[250px]">
{children}
</div>
);
};

const RemoveButton = ({ onClick }: { onClick: () => void }) => {
return (
<button
className="h-3.5 w-3.5 dark:text-text-input-value"
onClick={() => {
onClick();
}}
>
<TimesIcon />
</button>
);
};

type PrettifiableNodeTypes =
| 'host'
| 'container'
| 'containerImage'
| 'cluster'
| 'registryAccount'
| 'pod';

export const FilterBadge = ({
text,
onRemove,
const PrettyNameHost = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'host', id],
queryFn: async () => {
return lookupHostsBatcher.fetch(id);
},
});
if (data) {
return data.node_name;
}
return id;
};

const PrettyNameContainerImage = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'containerImage', id],
queryFn: async () => {
return lookupContainerImagesBatcher.fetch(id);
},
});
if (data) {
return data.node_name;
}
return id;
};

const PrettyNameClusters = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'cluster', id],
queryFn: async () => {
return lookupClusterBatcher.fetch(id);
},
});
if (data) {
return data.node_name;
}
return id;
};

const PrettyNameRegistryAccounts = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'registryAccount', id],
queryFn: async () => {
return lookupRegistryAccountBatcher.fetch(id);
},
});
if (data) {
return `${data.name} (${data.registry_type})`;
}
return id;
};

const PrettyNamePod = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'pod', id],
queryFn: async () => {
return lookupPodBatcher.fetch(id);
},
});
if (data) {
return data.node_name;
}
return id;
};

const PrettyNameContainers = ({ id }: { id: string }) => {
const { data } = useSuspenseQuery({
queryKey: ['badge', 'lookup', 'container', id],
queryFn: async () => {
return lookupContainerBatcher.fetch(id);
},
});
if (data) {
return data.node_name;
}
return id;
};

const PrettyName = ({
id,
nodeType,
}: {
text: string;
onRemove: () => void;
id: string;
nodeType: PrettifiableNodeTypes;
}) => {
if (nodeType === 'host') {
return (
<Suspense fallback={id}>
<PrettyNameHost id={id} />
</Suspense>
);
} else if (nodeType === 'containerImage') {
return (
<Suspense fallback={id}>
<PrettyNameContainerImage id={id} />
</Suspense>
);
} else if (nodeType === 'cluster') {
return (
<Suspense fallback={id}>
<PrettyNameClusters id={id} />
</Suspense>
);
} else if (nodeType === 'registryAccount') {
return (
<Suspense fallback={id}>
<PrettyNameRegistryAccounts id={id} />
</Suspense>
);
} else if (nodeType === 'container') {
return (
<Suspense fallback={id}>
<PrettyNameContainers id={id} />
</Suspense>
);
} else if (nodeType === 'pod') {
return (
<Suspense fallback={id}>
<PrettyNamePod id={id} />
</Suspense>
);
}
return id;
};

export const FilterBadge = (
props:
| {
label?: string;
text: string;
onRemove: () => void;
}
| {
label: string;
id: string;
nodeType: PrettifiableNodeTypes;
onRemove: () => void;
},
) => {
if ('id' in props) {
return (
<Wrapper>
<Text>
{props.label}: <PrettyName id={props.id} nodeType={props.nodeType} />
</Text>
<RemoveButton
onClick={() => {
props.onRemove();
}}
/>
</Wrapper>
);
}
return (
<div className="flex items-center py-1 px-2.5 dark:border dark:border-accent-accent rounded-[11px] gap-2">
<div className="text-p8 dark:text-text-input-value truncate max-w-[150px]">
{text}
</div>
<button
className="h-3.5 w-3.5 dark:text-text-input-value"
<Wrapper>
<Text>
{props.label?.length ? `${props.label}: ` : ''}
{props.text}
</Text>
<RemoveButton
onClick={() => {
onRemove();
props.onRemove();
}}
>
<TimesIcon />
</button>
</div>
/>
</Wrapper>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,25 @@ const ActionDropdown = ({
);
};

const FILTER_SEARCHPARAMS: Record<string, string> = {
enum FILTER_SEARCHPARAMS_KEYS_ENUM {
nodeType = 'nodeType',
malwareScanStatus = 'malwareScanStatus',
containerImages = 'containerImages',
containers = 'containers',
hosts = 'hosts',
clusters = 'clusters',
registryAccounts = 'registryAccounts',
}

const FILTER_SEARCHPARAMS_DYNAMIC_KEYS = [
FILTER_SEARCHPARAMS_KEYS_ENUM.hosts,
FILTER_SEARCHPARAMS_KEYS_ENUM.containerImages,
FILTER_SEARCHPARAMS_KEYS_ENUM.clusters,
FILTER_SEARCHPARAMS_KEYS_ENUM.registryAccounts,
FILTER_SEARCHPARAMS_KEYS_ENUM.containers,
];

const FILTER_SEARCHPARAMS: Record<FILTER_SEARCHPARAMS_KEYS_ENUM, string> = {
nodeType: 'Node Type',
malwareScanStatus: 'Malware scan status',
containerImages: 'Container image',
Expand All @@ -361,6 +379,20 @@ const Filters = () => {
const [nodeType, setNodeType] = useState('');
const [malwareScanStatusSearchText, setMalwareScanStatusSearchText] = useState('');

const onFilterRemove = ({ key, value }: { key: string; value: string }) => {
return () => {
setSearchParams((prev) => {
const existingValues = prev.getAll(key);
prev.delete(key);
existingValues.forEach((existingValue) => {
if (existingValue !== value) prev.append(key, existingValue);
});
prev.delete('page');
return prev;
});
};
};

const appliedFilterCount = getAppliedFiltersCount(searchParams);
return (
<div className="px-4 py-2.5 mb-4 border dark:border-bg-hover-3 rounded-[5px] overflow-hidden dark:bg-bg-left-nav">
Expand Down Expand Up @@ -555,29 +587,44 @@ const Filters = () => {
</div>
{appliedFilterCount > 0 ? (
<div className="flex gap-2.5 mt-4 flex-wrap items-center">
{Array.from(searchParams)
.filter(([key]) => {
{(
Array.from(searchParams).filter(([key]) => {
return Object.keys(FILTER_SEARCHPARAMS).includes(key);
})
.map(([key, value]) => {
}) as Array<[FILTER_SEARCHPARAMS_KEYS_ENUM, string]>
).map(([key, value]) => {
if (FILTER_SEARCHPARAMS_DYNAMIC_KEYS.includes(key)) {
return (
<FilterBadge
key={`${key}-${value}`}
nodeType={(() => {
if (key === FILTER_SEARCHPARAMS_KEYS_ENUM.hosts) {
return 'host';
} else if (key === FILTER_SEARCHPARAMS_KEYS_ENUM.containerImages) {
return 'containerImage';
} else if (key === FILTER_SEARCHPARAMS_KEYS_ENUM.clusters) {
return 'cluster';
} else if (key === FILTER_SEARCHPARAMS_KEYS_ENUM.registryAccounts) {
return 'registryAccount';
} else if (key === FILTER_SEARCHPARAMS_KEYS_ENUM.containers) {
return 'container';
}
throw new Error('unknown key');
})()}
onRemove={onFilterRemove({ key, value })}
id={value}
label={FILTER_SEARCHPARAMS[key]}
/>
);
} else {
return (
<FilterBadge
key={`${key}-${value}`}
onRemove={() => {
setSearchParams((prev) => {
const existingValues = prev.getAll(key);
prev.delete(key);
existingValues.forEach((existingValue) => {
if (existingValue !== value) prev.append(key, existingValue);
});
prev.delete('page');
return prev;
});
}}
onRemove={onFilterRemove({ key, value })}
text={`${FILTER_SEARCHPARAMS[key]}: ${value}`}
/>
);
})}
}
})}
<Button
variant="flat"
color="default"
Expand Down
Loading

0 comments on commit 0cb2f10

Please sign in to comment.