Added repo signature on preview and repo card

Signed-off-by: Amelia-Maria Breda <ambreda@cisco.com>
This commit is contained in:
Amelia-Maria Breda 2022-10-13 09:47:07 +01:00 committed by Raul Kele
parent ed570b03ec
commit 5f494adb5a
6 changed files with 99 additions and 25 deletions

View File

@ -20,6 +20,7 @@ const mockImageList = {
NewestImage: {
Tag: 'latest',
Description: 'w',
IsSigned: false,
Licenses: '',
Vendor: '',
Labels: ''
@ -32,6 +33,7 @@ const mockImageList = {
NewestImage: {
Tag: 'latest',
Description: '',
IsSigned: true,
Licenses: '',
Vendor: '',
Labels: ''
@ -44,6 +46,7 @@ const mockImageList = {
NewestImage: {
Tag: 'latest',
Description: '',
IsSigned: true,
Licenses: '',
Vendor: '',
Labels: ''
@ -71,6 +74,16 @@ describe('Home component', () => {
await waitFor(() => expect(screen.getAllByText(/node/i)).toHaveLength(1));
});
it('renders signature chips', async () => {
// @ts-ignore
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
render(<Home />);
expect(await screen.findAllByTestId('unverified-icon')).toHaveLength(1);
expect(await screen.findAllByTestId('verified-icon')).toHaveLength(2);
expect(await screen.findAllByTestId('unverified-chip')).toHaveLength(1);
expect(await screen.findAllByTestId('verified-chip')).toHaveLength(1);
});
it("should log an error when data can't be fetched", async () => {
// @ts-ignore
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });

View File

@ -63,7 +63,7 @@ const endpoints = {
repoList: ({ pageNumber = 1, pageSize = 15 } = {}) =>
`/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Title Source Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Title Source IsSigned Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
detailedRepoInfo: (name) =>
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag Layers {Size Digest}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
detailedImageInfo: (name, tag) =>

View File

@ -96,7 +96,7 @@ function Home() {
homeData.slice(0, 4).map((item, index) => {
return (
<Grid item xs={3} key={index}>
<PreviewCard name={item.name} lastUpdated={item.lastUpdated} />
<PreviewCard name={item.name} lastUpdated={item.lastUpdated} isSigned={item.isSigned} />
</Grid>
);
})
@ -136,6 +136,7 @@ function Home() {
name={item.name}
version={item.latestVersion}
description={item.description}
isSigned={item.isSigned}
tags={item.tags}
vendor={item.vendor}
platforms={item.platforms}

View File

@ -9,6 +9,11 @@ import repocube2 from '../assets/repocube-2.png';
import repocube3 from '../assets/repocube-3.png';
import repocube4 from '../assets/repocube-4.png';
//icons
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
//import GppMaybeOutlinedIcon from '@mui/icons-material/GppMaybeOutlined';
// temporary utility to get image
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
@ -68,7 +73,7 @@ const useStyles = makeStyles(() => ({
function PreviewCard(props) {
const classes = useStyles();
const navigate = useNavigate();
const { name } = props;
const { name, isSigned } = props;
const goToDetails = () => {
navigate(`/image/${encodeURIComponent(name)}`);
@ -86,14 +91,40 @@ function PreviewCard(props) {
// return(getRandom(arrVulnerability));
// }
// const signatureCheck = () => {
// const unverifiedSignature = <GppBadOutlinedIcon sx={{ color: "#E53935!important", padding:"0.2rem", background: "#FEEBEE", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
// const untrustedSignature = <GppMaybeOutlinedIcon sx={{ color: "#52637A!important", padding:"0.2rem", background: "#ECEFF1", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
// const verifiedSignature = <GppGoodOutlinedIcon sx={{ color: "#43A047!important", padding:"0.2rem", background: "#E8F5E9", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
// const arrSignature = [unverifiedSignature, untrustedSignature, verifiedSignature]
// return(getRandom(arrSignature));
// }
const signatureCheck = () => {
const unverifiedSignature = (
<GppBadOutlinedIcon
sx={{
color: '#E53935!important',
padding: '0.2rem',
background: '#FEEBEE',
borderRadius: '1rem',
height: '1.5rem',
width: '1.6rem'
}}
data-testid="unverified-icon"
/>
);
//const untrustedSignature = <GppMaybeOutlinedIcon sx={{ color: "#52637A!important", padding:"0.2rem", background: "#ECEFF1", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
const verifiedSignature = (
<GppGoodOutlinedIcon
sx={{
color: '#43A047!important',
padding: '0.2rem',
background: '#E8F5E9',
borderRadius: '1rem',
height: '1.5rem',
width: '1.6rem'
}}
data-testid="verified-icon"
/>
);
if (isSigned) {
return verifiedSignature;
} else {
return unverifiedSignature;
}
};
return (
<Card variant="outlined" className={classes.card}>
@ -118,7 +149,7 @@ function PreviewCard(props) {
size: '1.5rem',
lineHeight: '2rem',
color: '#220052',
width: '10rem',
width: '8rem',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
@ -126,8 +157,8 @@ function PreviewCard(props) {
>
{name}
</Typography>
{/* {vulnerabilityCheck()}
{signatureCheck()} */}
{/* {vulnerabilityCheck()} */}
{signatureCheck()}
</Stack>
</Grid>
<Grid item xs={12} mt={2}></Grid>

View File

@ -14,6 +14,10 @@ import repocube2 from '../assets/repocube-2.png';
import repocube3 from '../assets/repocube-3.png';
import repocube4 from '../assets/repocube-4.png';
//icons
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
// temporary utility to get image
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
@ -78,7 +82,7 @@ const useStyles = makeStyles(() => ({
function RepoCard(props) {
const classes = useStyles();
const navigate = useNavigate();
const { name, vendor, platforms, description, lastUpdated, version } = props;
const { name, vendor, platforms, description, isSigned, lastUpdated, version } = props;
//function that returns a random element from an array
// function getRandom(list) {
@ -101,14 +105,38 @@ function RepoCard(props) {
// return(getRandom(arrVulnerability));
// };
// const signatureCheck = () => {
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
// const verifiedSignature = <Chip label="Verified Signature" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppGoodOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
// const arrSignature = [unverifiedSignature, untrustedSignature, verifiedSignature]
// return(getRandom(arrSignature));
// }
const signatureCheck = () => {
const unverifiedSignature = (
<Chip
label="Unverified Signature"
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
variant="filled"
onDelete={() => {
return;
}}
deleteIcon={<GppBadOutlinedIcon sx={{ color: '#E53935!important' }} />}
data-testid="unverified-chip"
/>
);
//const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
const verifiedSignature = (
<Chip
label="Verified Signature"
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
variant="filled"
onDelete={() => {
return;
}}
deleteIcon={<GppGoodOutlinedIcon sx={{ color: '#388E3C!important' }} />}
data-testid="verified-chip"
/>
);
if (isSigned) {
return verifiedSignature;
} else {
return unverifiedSignature;
}
};
const platformChips = () => {
// if platforms not received, mock data
@ -167,8 +195,8 @@ function RepoCard(props) {
<Typography variant="h5" component="div">
{name}
</Typography>
{/* {vulnerabilityCheck()}
{signatureCheck()} */}
{/* {vulnerabilityCheck()} */}
{signatureCheck()}
{/* <Chip label="Verified licensee" sx={{ backgroundColor: "#E8F5E9", color: "#388E3C" }} variant="filled" onDelete={() => { return }} deleteIcon={vulnerabilityCheck()} /> */}
</Stack>
<Typography className={classes.versionLast} pt={1} sx={{ fontSize: 12 }} gutterBottom noWrap>

View File

@ -4,6 +4,7 @@ const mapToRepo = (responseRepo) => {
latestVersion: responseRepo.NewestImage?.Tag,
tags: responseRepo.NewestImage?.Labels,
description: responseRepo.NewestImage?.Description,
isSigned: responseRepo.NewestImage?.IsSigned,
platforms: responseRepo.Platforms,
licenses: responseRepo.NewestImage?.Licenses,
size: responseRepo.Size,