patch: referrers from image query

Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
Raul Kele 2023-03-21 16:47:11 +02:00
parent ddf1d9224b
commit 2f94cc30ae
5 changed files with 49 additions and 89 deletions

View File

@ -1,47 +1,42 @@
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { api } from 'api';
import ReferredBy from 'components/Tag/Tabs/ReferredBy';
import React from 'react';
const mockReferrersList = {
data: {
Referrers: [
const mockReferrersList = [
{
MediaType: 'application/vnd.oci.artifact.manifest.v1+json',
ArtifactType: 'application/vnd.example.icecream.v1',
Size: 466,
Digest: 'sha256:be7a3d01c35a2cf53c502e9dc50cdf36b15d9361c81c63bf319f1d5cbe44ab7c',
Annotations: [
{
MediaType: 'application/vnd.oci.artifact.manifest.v1+json',
ArtifactType: 'application/vnd.example.icecream.v1',
Size: 466,
Digest: 'sha256:be7a3d01c35a2cf53c502e9dc50cdf36b15d9361c81c63bf319f1d5cbe44ab7c',
Annotations: [
{
Key: 'demo',
Value: 'true'
},
{
Key: 'format',
Value: 'oci'
}
]
Key: 'demo',
Value: 'true'
},
{
MediaType: 'application/vnd.oci.artifact.manifest.v1+json',
ArtifactType: 'application/vnd.example.icecream.v1',
Size: 466,
Digest: 'sha256:d9ad22f41d9cb9797c134401416eee2a70446cee1a8eb76fc6b191f4320dade2',
Annotations: [
{
Key: 'demo',
Value: 'true'
},
{
Key: 'format',
Value: 'oci'
}
]
Key: 'format',
Value: 'oci'
}
]
},
{
MediaType: 'application/vnd.oci.artifact.manifest.v1+json',
ArtifactType: 'application/vnd.example.icecream.v1',
Size: 466,
Digest: 'sha256:d9ad22f41d9cb9797c134401416eee2a70446cee1a8eb76fc6b191f4320dade2',
Annotations: [
{
Key: 'demo',
Value: 'true'
},
{
Key: 'format',
Value: 'oci'
}
]
}
};
];
// useNavigate mock
const mockedUsedNavigate = jest.fn();
@ -57,27 +52,17 @@ afterEach(() => {
describe('Referred by tab', () => {
it('should render referrers if there are any', async () => {
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: mockReferrersList });
render(<ReferredBy repoName="golang" digest="test" />);
render(<ReferredBy referrers={mockReferrersList} />);
expect(await screen.findAllByText('Media type: application/vnd.oci.artifact.manifest.v1+json')).toHaveLength(2);
});
it("renders no referrers if there aren't any", async () => {
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { Referrers: [] } } });
render(<ReferredBy repoName="golang" digest="test" />);
render(<ReferredBy referrers={[]} />);
expect(await screen.findByText(/Nothing found/i)).toBeInTheDocument();
});
it('should log an error if the request fails', async () => {
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<ReferredBy repoName="golang" digest="test" />);
await waitFor(() => expect(error).toBeCalledTimes(1));
});
it('should display the digest when clicking the dropdowns', async () => {
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: mockReferrersList });
render(<ReferredBy repoName="golang" digest="test" />);
render(<ReferredBy referrers={mockReferrersList} />);
const firstDigest = (await screen.findAllByText(/digest/i))[0];
expect(firstDigest).toBeInTheDocument();
await userEvent.click(firstDigest);
@ -91,8 +76,7 @@ describe('Referred by tab', () => {
});
it('should display the annotations when clicking the dropdown', async () => {
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: mockReferrersList });
render(<ReferredBy repoName="golang" digest="test" />);
render(<ReferredBy referrers={mockReferrersList} />);
const firstAnnotations = (await screen.findAllByText(/ANNOTATIONS/i))[0];
expect(firstAnnotations).toBeInTheDocument();
await userEvent.click(firstAnnotations);

View File

@ -78,7 +78,7 @@ const endpoints = {
detailedRepoInfo: (name) =>
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Manifests {Digest Platform {Os Arch} Size} Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName IsSigned Vulnerabilities {MaxSeverity Count} Manifests {Digest} Tag Title Documentation DownloadCount Source Description Licenses}}}}`,
detailedImageInfo: (name, tag) =>
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned Vulnerabilities {MaxSeverity Count} Tag Manifests {History {Layer {Size Digest} HistoryDescription {CreatedBy EmptyLayer}} Digest ConfigDigest LastUpdated Size Platform {Os Arch}} Vendor Licenses }}`,
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned Vulnerabilities {MaxSeverity Count} Referrers {MediaType ArtifactType Size Digest Annotations{Key Value}} Tag Manifests {History {Layer {Size Digest} HistoryDescription {CreatedBy EmptyLayer}} Digest ConfigDigest LastUpdated Size Platform {Os Arch}} Vendor Licenses }}`,
vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }, searchTerm = '') => {
let query = `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize

View File

@ -1,11 +1,9 @@
import React, { useEffect, useState, useMemo } from 'react';
import React, { useEffect, useState } from 'react';
import { makeStyles } from '@mui/styles';
import { isEmpty } from 'lodash';
import { Divider, Typography, Stack } from '@mui/material';
import ReferrerCard from '../../Shared/ReferrerCard';
import Loading from '../../Shared/Loading';
import { api, endpoints } from 'api';
import { host } from '../../../host';
import { mapReferrer } from 'utilities/objectModels';
const useStyles = makeStyles(() => ({
@ -17,36 +15,24 @@ const useStyles = makeStyles(() => ({
}));
function ReferredBy(props) {
const { repoName, digest } = props;
const [referrers, setReferrers] = useState([]);
const { referrers } = props;
const [referrersData, setReferrersData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const classes = useStyles();
const abortController = useMemo(() => new AbortController(), []);
useEffect(() => {
api
.get(`${host()}${endpoints.referrers({ repo: repoName, digest })}`, abortController.signal)
.then((response) => {
if (response.data && response.data.data) {
let referrersData = response.data.data.Referrers?.map((referrer) => mapReferrer(referrer));
if (!isEmpty(referrersData)) {
setReferrers(referrersData);
}
}
setIsLoading(false);
})
.catch((e) => {
console.error(e);
setIsLoading(false);
});
return () => {
abortController.abort();
};
if (!isEmpty(referrers)) {
const mappedReferrersData = referrers.map((referrer) => mapReferrer(referrer));
setReferrersData(mappedReferrersData);
} else {
setReferrersData([]);
}
setIsLoading(false);
}, []);
const renderReferrers = () => {
return !isEmpty(referrers) ? (
referrers.map((referrer, index) => {
return !isEmpty(referrersData) ? (
referrersData.map((referrer, index) => {
return (
<ReferrerCard
key={index}
@ -63,16 +49,6 @@ function ReferredBy(props) {
);
};
const renderListBottom = () => {
if (isLoading) {
return <Loading />;
}
if (!isLoading) {
return <div />;
}
return;
};
return (
<div data-testid="referred-by-container">
<Typography
@ -95,8 +71,7 @@ function ReferredBy(props) {
/>
<Stack direction="column" spacing={2}>
<Stack direction="column" spacing={2}>
{renderReferrers()}
{renderListBottom()}
{isLoading ? <Loading /> : renderReferrers()}
</Stack>
</Stack>
</div>

View File

@ -580,7 +580,7 @@ function TagDetails() {
<VulnerabilitiesDetails name={reponame} tag={tag} />
</TabPanel>
<TabPanel value="ReferredBy" className={classes.tabPanel}>
<ReferredBy repoName={reponame} digest={selectedManifest?.digest} />
<ReferredBy referrers={imageDetailData.referrers} />
</TabPanel>
</Grid>
</Grid>

View File

@ -44,6 +44,7 @@ const mapToImage = (responseImage) => {
repoName: responseImage.RepoName,
tag: responseImage.Tag,
manifests: responseImage.Manifests?.map((manifest) => mapToManifest(manifest)) || [],
referrers: responseImage.Referrers,
size: responseImage.Size,
downloadCount: responseImage.DownloadCount,
lastUpdated: responseImage.LastUpdated,