Compare commits
1 Commits
commit-1cf
...
commit-4db
Author | SHA1 | Date | |
---|---|---|---|
4db0c2ee8b |
@ -12,6 +12,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'project-stacker/c3/static-ubuntu-amd64',
|
||||
Tag: 'tag1',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
@ -20,6 +21,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag2',
|
||||
Tag: 'tag2',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 2
|
||||
@ -28,6 +30,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag3',
|
||||
Tag: 'tag3',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 7
|
||||
@ -36,6 +39,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag4',
|
||||
Tag: 'tag4',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { api } from 'api';
|
||||
import HistoryLayers from 'components/Tag/Tabs/HistoryLayers';
|
||||
import React from 'react';
|
||||
|
||||
const mockLayersList = [
|
||||
{
|
||||
Layer: { Size: '2806054', Digest: '213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49', Score: null },
|
||||
Layer: { Size: '2806054', Digest: '213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49' },
|
||||
HistoryDescription: {
|
||||
Created: '2022-08-09T17:19:53.274069586Z',
|
||||
CreatedBy: '/bin/sh -c #(nop) ADD file:2a949686d9886ac7c10582a6c29116fd29d3077d02755e87e111870d63607725 in / ',
|
||||
@ -33,33 +32,20 @@ afterEach(() => {
|
||||
|
||||
describe('Layers page', () => {
|
||||
it('renders the layers if there are any', async () => {
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { Image: { History: mockLayersList } } } });
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
render(<HistoryLayers name="alpine:latest" history={mockLayersList} />);
|
||||
expect(await screen.findAllByTestId('layer-card-container')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders no layers if there are not any', async () => {
|
||||
jest.spyOn(api, 'get').mockResolvedValue({
|
||||
status: 200,
|
||||
data: { data: { History: { Tag: '', mockLayersList: [] } } }
|
||||
});
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
render(<HistoryLayers name="alpine:latest" history={[]} />);
|
||||
await waitFor(() => expect(screen.getAllByText(/No Layer data available/i)).toHaveLength(1));
|
||||
});
|
||||
|
||||
it('opens dropdown and renders layer command and digest', async () => {
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { Image: { History: mockLayersList } } } });
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
render(<HistoryLayers name="alpine:latest" history={mockLayersList} />);
|
||||
expect(screen.queryAllByText(/DIGEST/i)).toHaveLength(0);
|
||||
const openDetails = await screen.findAllByText(/details/i);
|
||||
fireEvent.click(openDetails[0]);
|
||||
expect(await screen.findAllByText(/DIGEST/i)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
await waitFor(() => expect(error).toBeCalledTimes(1));
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ const mockDependentsList = {
|
||||
{
|
||||
RepoName: 'project-stacker/c3/static-ubuntu-amd64',
|
||||
Tag: 'tag1',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
@ -20,6 +21,7 @@ const mockDependentsList = {
|
||||
{
|
||||
RepoName: 'tag2',
|
||||
Tag: 'tag2',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 2
|
||||
@ -28,6 +30,7 @@ const mockDependentsList = {
|
||||
{
|
||||
RepoName: 'tag3',
|
||||
Tag: 'tag3',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 5
|
||||
@ -36,6 +39,7 @@ const mockDependentsList = {
|
||||
{
|
||||
RepoName: 'tag4',
|
||||
Tag: 'tag4',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 3
|
||||
|
@ -24,57 +24,61 @@ const mockImage = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
History: [
|
||||
{
|
||||
Layer: {
|
||||
Size: '75181999',
|
||||
Digest: 'sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621',
|
||||
Score: null
|
||||
},
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:52.526672082Z',
|
||||
CreatedBy:
|
||||
'/bin/sh -c #(nop) ADD file:bd7a2aed6ede423b719ceb2f723e4ecdfa662b28639c8429731c878e86fb138b in / ',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: false
|
||||
}
|
||||
},
|
||||
{
|
||||
Layer: null,
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:52.895811646Z',
|
||||
CreatedBy:
|
||||
'/bin/sh -c #(nop) LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201204',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: true
|
||||
}
|
||||
},
|
||||
{
|
||||
Layer: null,
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:53.076477777Z',
|
||||
CreatedBy: '/bin/sh -c #(nop) CMD ["/bin/bash"]',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS',
|
||||
History: [
|
||||
{
|
||||
Layer: {
|
||||
Size: '75181999',
|
||||
Digest: 'sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621',
|
||||
Score: null
|
||||
},
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:52.526672082Z',
|
||||
CreatedBy:
|
||||
'/bin/sh -c #(nop) ADD file:bd7a2aed6ede423b719ceb2f723e4ecdfa662b28639c8429731c878e86fb138b in / ',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: false
|
||||
}
|
||||
},
|
||||
{
|
||||
Layer: null,
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:52.895811646Z',
|
||||
CreatedBy:
|
||||
'/bin/sh -c #(nop) LABEL org.label-schema.schema-version=1.0 org.label-schema.name=CentOS Base Image org.label-schema.vendor=CentOS org.label-schema.license=GPLv2 org.label-schema.build-date=20201204',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: true
|
||||
}
|
||||
},
|
||||
{
|
||||
Layer: null,
|
||||
HistoryDescription: {
|
||||
Created: '2020-12-08T00:22:53.076477777Z',
|
||||
CreatedBy: '/bin/sh -c #(nop) CMD ["/bin/bash"]',
|
||||
Author: '',
|
||||
Comment: '',
|
||||
EmptyLayer: true
|
||||
}
|
||||
}
|
||||
]
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
@ -82,14 +86,18 @@ const mockImageNone = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'NONE',
|
||||
Count: 10
|
||||
@ -102,14 +110,18 @@ const mockImageUnknown = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'UNKNOWN',
|
||||
Count: 10
|
||||
@ -122,14 +134,18 @@ const mockImageFailed = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: '',
|
||||
Count: 10
|
||||
@ -142,14 +158,18 @@ const mockImageLow = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 10
|
||||
@ -162,14 +182,18 @@ const mockImageMedium = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'MEDIUM',
|
||||
Count: 10
|
||||
@ -182,14 +206,18 @@ const mockImageHigh = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Manifests: [
|
||||
{
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
}
|
||||
}
|
||||
],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 10
|
||||
@ -206,6 +234,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'project-stacker/c3/static-ubuntu-amd64',
|
||||
Tag: 'tag1',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
@ -214,6 +243,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag2',
|
||||
Tag: 'tag2',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 2
|
||||
@ -222,6 +252,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag3',
|
||||
Tag: 'tag3',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 7
|
||||
@ -230,6 +261,7 @@ const mockDependenciesList = {
|
||||
{
|
||||
RepoName: 'tag4',
|
||||
Tag: 'tag4',
|
||||
Manifests: [],
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
|
10
src/api.js
10
src/api.js
@ -76,15 +76,13 @@ const endpoints = {
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Results {Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description Licenses Title Source IsSigned Documentation Vendor Labels} DownloadCount}}}`,
|
||||
detailedRepoInfo: (name) =>
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor Size Platform {Os Arch}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName IsSigned Vulnerabilities {MaxSeverity Count} Layers {Size Digest} Digest Tag Title Documentation DownloadCount Source Description Licenses History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Manifests {Digest Platform {Os Arch}} Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor Size } 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 Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor Licenses }}`,
|
||||
`/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 }}`,
|
||||
vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }) =>
|
||||
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Tag Page {TotalCount ItemCount} CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
||||
layersDetailsForImage: (name) =>
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}"){History {Layer {Size Digest Score} HistoryDescription {Created CreatedBy Author Comment EmptyLayer} }}}`,
|
||||
imageListWithCVEFixed: (cveId, repoName, { pageNumber = 1, pageSize = 3 }) =>
|
||||
`/v2/_zot/ext/search?query={ImageListWithCVEFixed(id:"${cveId}", image:"${repoName}", requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
@ -92,11 +90,11 @@ const endpoints = {
|
||||
dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) =>
|
||||
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Page {TotalCount ItemCount} Results { RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}}`,
|
||||
}}){Page {TotalCount ItemCount} Results { RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned Vulnerabilities {MaxSeverity Count}}}}`,
|
||||
isDependentOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) =>
|
||||
`/v2/_zot/ext/search?query={DerivedImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Page {TotalCount ItemCount} Results {RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}}`,
|
||||
}}){Page {TotalCount ItemCount} Results {RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned Vulnerabilities {MaxSeverity Count}}}}`,
|
||||
globalSearch: ({
|
||||
searchQuery = '""',
|
||||
pageNumber = 1,
|
||||
|
@ -241,7 +241,7 @@ function SearchSuggestion() {
|
||||
root: classes.searchItemIconBg,
|
||||
img: classes.searchItemIcon
|
||||
}}
|
||||
src={`data:image/png;base64, ${suggestion.logo}`}
|
||||
src={suggestion.logo ? `data:image/png;base64, ${suggestion.logo}` : ''}
|
||||
>
|
||||
<PhotoIcon className={classes.searchItemIcon} />
|
||||
</Avatar>
|
||||
|
@ -1,6 +1,8 @@
|
||||
// react global
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { head } from 'lodash';
|
||||
|
||||
// components
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { Card, CardContent, Divider, Stack, InputBase, FormControl, Select, InputLabel, MenuItem } from '@mui/material';
|
||||
@ -87,10 +89,10 @@ export default function Tags(props) {
|
||||
key={tag.Tag}
|
||||
tag={tag.Tag}
|
||||
lastUpdated={tag.LastUpdated}
|
||||
digest={tag.Digest}
|
||||
digest={head(tag.Manifests)?.Digest}
|
||||
vendor={tag.Vendor}
|
||||
size={tag.Size}
|
||||
platform={tag.Platform}
|
||||
platform={head(tag.Manifests)?.Platform}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useMemo, useRef } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, head } from 'lodash';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../../../api';
|
||||
@ -148,10 +148,10 @@ function DependsOn(props) {
|
||||
repoName={dependence.repoName}
|
||||
tag={dependence.tag}
|
||||
vendor={dependence.vendor}
|
||||
platform={dependence.platform}
|
||||
platform={head(dependence.manifests)?.platform}
|
||||
isSigned={dependence.isSigned}
|
||||
size={dependence.size}
|
||||
digest={dependence.digest}
|
||||
size={head(dependence.manifests)?.size}
|
||||
digest={head(dependence.manifests)?.digest}
|
||||
key={index}
|
||||
lastUpdated={dependence.lastUpdated}
|
||||
/>
|
||||
|
@ -1,14 +1,9 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../../../api';
|
||||
|
||||
// components
|
||||
import { Divider, Stack, Typography } from '@mui/material';
|
||||
import LayerCard from '../../Shared/LayerCard.jsx';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host } from '../../../host';
|
||||
import { isEmpty } from 'lodash';
|
||||
import Loading from '../../Shared/Loading';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
@ -83,25 +78,8 @@ function HistoryLayers(props) {
|
||||
const { name, history } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (history && !isEmpty(history)) {
|
||||
setHistoryData(history);
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
api
|
||||
.get(`${host()}${endpoints.layersDetailsForImage(name)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let layersHistory = response.data.data.Image;
|
||||
setHistoryData(layersHistory?.History);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
setHistoryData([]);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
setHistoryData(history);
|
||||
setIsLoading(false);
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
@ -129,7 +107,7 @@ function HistoryLayers(props) {
|
||||
<Loading />
|
||||
) : (
|
||||
<Stack direction="column" spacing={2} sx={{ marginTop: '1.7rem' }} data-testid="layer-card-container">
|
||||
{historyData ? (
|
||||
{historyData?.length > 0 ? (
|
||||
historyData.map((layer, index) => {
|
||||
return (
|
||||
<LayerCard
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useMemo, useState, useRef } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, head } from 'lodash';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../../../api';
|
||||
@ -148,10 +148,10 @@ function IsDependentOn(props) {
|
||||
repoName={dependence.repoName}
|
||||
tag={dependence.tag}
|
||||
vendor={dependence.vendor}
|
||||
platform={dependence.platform}
|
||||
platform={head(dependence.manifests)?.platform}
|
||||
isSigned={dependence.isSigned}
|
||||
size={dependence.size}
|
||||
digest={dependence.digest}
|
||||
size={head(dependence.manifests)?.size}
|
||||
digest={head(dependence.manifests)?.digest}
|
||||
key={index}
|
||||
lastUpdated={dependence.lastUpdated}
|
||||
/>
|
||||
|
@ -37,7 +37,7 @@ import VulnerabilitiesDetails from './Tabs/VulnerabilitiesDetails';
|
||||
import HistoryLayers from './Tabs/HistoryLayers';
|
||||
import DependsOn from './Tabs/DependsOn';
|
||||
import IsDependentOn from './Tabs/IsDependentOn';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, head } from 'lodash';
|
||||
import Loading from '../Shared/Loading';
|
||||
import { dockerPull, podmanPull, skopeoPull } from 'utilities/pullStrings';
|
||||
import { VulnerabilityIconCheck, SignatureIconCheck } from 'utilities/vulnerabilityAndSignatureCheck';
|
||||
@ -212,6 +212,7 @@ const randomImage = () => {
|
||||
|
||||
function TagDetails() {
|
||||
const [imageDetailData, setImageDetailData] = useState({});
|
||||
const [selectedManifest, setSelectedManifest] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedTab, setSelectedTab] = useState('Layers');
|
||||
const [selectedPullTab, setSelectedPullTab] = useState('');
|
||||
@ -239,6 +240,7 @@ function TagDetails() {
|
||||
let imageInfo = response.data.data.Image;
|
||||
let imageData = mapToImage(imageInfo);
|
||||
setImageDetailData(imageData);
|
||||
setSelectedManifest(head(imageData.manifests));
|
||||
setPullString(dockerPull(imageData.name));
|
||||
setSelectedPullTab(dockerPull(imageData.name));
|
||||
} else if (!isEmpty(response.data.errors)) {
|
||||
@ -258,7 +260,7 @@ function TagDetails() {
|
||||
}, [reponame, tag]);
|
||||
|
||||
const getPlatform = () => {
|
||||
return imageDetailData?.platform ? imageDetailData.platform : '--/--';
|
||||
return selectedManifest.platform ? selectedManifest.platform : '--/--';
|
||||
};
|
||||
|
||||
const handleTabChange = (event, newValue) => {
|
||||
@ -329,7 +331,7 @@ function TagDetails() {
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Typography gutterBottom className={classes.digest}>
|
||||
DIGEST: {imageDetailData?.digest}
|
||||
DIGEST: {selectedManifest?.digest}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={0} md={4} className={`${classes.pull} hide-on-mobile`}>
|
||||
@ -527,19 +529,19 @@ function TagDetails() {
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel value="Layers" className={classes.tabPanel}>
|
||||
<HistoryLayers name={imageDetailData.name} history={imageDetailData.history} />
|
||||
<HistoryLayers name={imageDetailData.name} history={selectedManifest.history} />
|
||||
</TabPanel>
|
||||
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
||||
<DependsOn name={imageDetailData.name} />
|
||||
<DependsOn name={imageDetailData.name} digest={selectedManifest.digest} />
|
||||
</TabPanel>
|
||||
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
||||
<IsDependentOn name={imageDetailData.name} />
|
||||
<IsDependentOn name={imageDetailData.name} digest={selectedManifest.digest} />
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
<VulnerabilitiesDetails name={reponame} tag={tag} />
|
||||
</TabPanel>
|
||||
<TabPanel value="ReferredBy" className={classes.tabPanel}>
|
||||
<ReferredBy repoName={reponame} digest={imageDetailData?.digest} />
|
||||
<ReferredBy repoName={reponame} digest={selectedManifest?.digest} />
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -549,8 +551,8 @@ function TagDetails() {
|
||||
<Grid item xs={12} md={4} className={classes.metadata}>
|
||||
<TagDetailsMetadata
|
||||
platform={getPlatform()}
|
||||
size={imageDetailData?.size}
|
||||
lastUpdated={imageDetailData?.lastUpdated}
|
||||
size={selectedManifest?.size}
|
||||
lastUpdated={selectedManifest?.lastUpdated}
|
||||
license={imageDetailData?.license}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -43,22 +43,40 @@ const mapToImage = (responseImage) => {
|
||||
return {
|
||||
repoName: responseImage.RepoName,
|
||||
tag: responseImage.Tag,
|
||||
lastUpdated: responseImage.LastUpdated,
|
||||
manifests: responseImage.Manifests?.map((manifest) => mapToManifest(manifest)) || [],
|
||||
size: responseImage.Size,
|
||||
digest: responseImage.Digest || responseImage.ConfigDigest,
|
||||
platform: responseImage.Platform,
|
||||
vendor: responseImage.Vendor,
|
||||
history: responseImage.History,
|
||||
downloadCount: responseImage.DownloadCount,
|
||||
lastUpdated: responseImage.LastUpdated,
|
||||
description: responseImage.Description,
|
||||
isSigned: responseImage.IsSigned,
|
||||
license: responseImage.Licenses,
|
||||
labels: responseImage.Labels,
|
||||
title: responseImage.Title,
|
||||
source: responseImage.Source,
|
||||
documentation: responseImage.Documentation,
|
||||
vendor: responseImage.Vendor,
|
||||
authors: responseImage.Authors,
|
||||
vulnerabiltySeverity: responseImage.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: responseImage.Vulnerabilities?.Count,
|
||||
isSigned: responseImage.IsSigned,
|
||||
logo: responseImage.Logo,
|
||||
// frontend only prop to increase interop with Repo objects and code reusability
|
||||
name: `${responseImage.RepoName}:${responseImage.Tag}`
|
||||
};
|
||||
};
|
||||
|
||||
const mapToManifest = (responseManifest) => {
|
||||
return {
|
||||
digest: responseManifest.Digest,
|
||||
configDigest: responseManifest.ConfigDigest,
|
||||
lastUpdated: responseManifest.LastUpdated,
|
||||
size: responseManifest.Size,
|
||||
platform: responseManifest.Platform,
|
||||
downloadCount: responseManifest.DownloadCount,
|
||||
layers: responseManifest.Layers,
|
||||
history: responseManifest.History,
|
||||
vulnerabilities: responseManifest.Vulnerabilities
|
||||
};
|
||||
};
|
||||
|
||||
const mapCVEInfo = (cveInfo) => {
|
||||
const cveList = cveInfo.map((cve) => {
|
||||
return {
|
||||
@ -79,4 +97,4 @@ const mapReferrer = (referrer) => ({
|
||||
annotations: referrer.Annotations?.map((annotation) => ({ key: annotation.Key, value: annotation.Value }))
|
||||
});
|
||||
|
||||
export { mapToRepo, mapToImage, mapToRepoFromRepoInfo, mapCVEInfo, mapReferrer };
|
||||
export { mapToRepo, mapToImage, mapToRepoFromRepoInfo, mapCVEInfo, mapReferrer, mapToManifest };
|
||||
|
Reference in New Issue
Block a user