refactor: Applied backend model mapping
Signed-off-by: Raul Kele <raulkeleblk@gmail.com>
This commit is contained in:
parent
26783e61e5
commit
33dcc936bb
10
.eslintignore
Normal file
10
.eslintignore
Normal file
@ -0,0 +1,10 @@
|
||||
**/.git
|
||||
**/.svn
|
||||
**/.hg
|
||||
**/node_modules
|
||||
**/.github
|
||||
README.md
|
||||
LICENSE
|
||||
Makefile
|
||||
**/coverage
|
||||
**/build
|
@ -6,3 +6,5 @@
|
||||
README.md
|
||||
LICENSE
|
||||
Makefile
|
||||
**/coverage
|
||||
**/build
|
@ -28,7 +28,7 @@ function App() {
|
||||
<Route path="/home" element={<HomePage />} />
|
||||
<Route path="/explore" element={<ExplorePage />} />
|
||||
<Route path="/image/:name" element={<RepoPage />} />
|
||||
<Route path="/image/:name/tag/:tag" element={<TagPage />} />
|
||||
<Route path="/image/:reponame/tag/:tag" element={<TagPage />} />
|
||||
</Route>
|
||||
<Route element={<AuthWrapper isLoggedIn={!isLoggedIn} redirect="/" />}>
|
||||
<Route
|
||||
|
@ -8,7 +8,6 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
// router mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -136,7 +135,6 @@ afterEach(() => {
|
||||
|
||||
describe('Explore component', () => {
|
||||
it("fetches image data and renders the list of images based on it's filters", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findByText(/alpine/i)).toBeInTheDocument();
|
||||
@ -145,14 +143,12 @@ describe('Explore component', () => {
|
||||
});
|
||||
|
||||
it('displays the no data message if no data is received', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { GlobalSearch: { Repos: [] } } } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findByText(/Looks like/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders signature icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findAllByTestId('unverified-icon')).toHaveLength(1);
|
||||
@ -160,7 +156,6 @@ describe('Explore component', () => {
|
||||
});
|
||||
|
||||
it('renders vulnerability icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-icon')).toHaveLength(1);
|
||||
@ -172,7 +167,6 @@ describe('Explore component', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<StateExploreWrapper />);
|
||||
@ -180,7 +174,6 @@ describe('Explore component', () => {
|
||||
});
|
||||
|
||||
it("should render the sort filter and be able to change it's value", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
const selectFilter = await screen.findByText('Relevance');
|
||||
|
@ -6,7 +6,6 @@ import React from 'react';
|
||||
// useNavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -129,7 +128,6 @@ afterEach(() => {
|
||||
|
||||
describe('Home component', () => {
|
||||
it('fetches image data and renders popular, bookmarks and recently updated', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<Home />);
|
||||
await waitFor(() => expect(screen.getAllByText(/alpine/i)).toHaveLength(2));
|
||||
@ -138,7 +136,6 @@ describe('Home component', () => {
|
||||
});
|
||||
|
||||
it('renders signature icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<Home />);
|
||||
expect(await screen.findAllByTestId('unverified-icon')).toHaveLength(2);
|
||||
@ -146,7 +143,6 @@ describe('Home component', () => {
|
||||
});
|
||||
|
||||
it('renders vulnerability icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<Home />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-icon')).toHaveLength(2);
|
||||
@ -156,7 +152,6 @@ describe('Home component', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<Home />);
|
||||
|
@ -7,7 +7,6 @@ import userEvent from '@testing-library/user-event';
|
||||
// useNavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -25,7 +24,6 @@ describe('Signin component automatic navigation', () => {
|
||||
|
||||
it('navigates to homepage when auth is disabled', async () => {
|
||||
// mock request to check auth
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: {} });
|
||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||
await waitFor(() => {
|
||||
@ -37,7 +35,6 @@ describe('Signin component automatic navigation', () => {
|
||||
describe('Sign in form', () => {
|
||||
beforeEach(() => {
|
||||
// mock auth check request
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 401, data: {} });
|
||||
});
|
||||
|
||||
@ -70,7 +67,6 @@ describe('Sign in form', () => {
|
||||
it('should log in the user and navigate to homepage if login is successful', async () => {
|
||||
render(<SignIn isAuthEnabled={true} setIsAuthEnabled={() => {}} isLoggedIn={false} setIsLoggedIn={() => {}} />);
|
||||
const submitButton = await screen.findByText('Continue');
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: {} } });
|
||||
fireEvent.click(submitButton);
|
||||
await waitFor(() => {
|
||||
|
@ -12,7 +12,6 @@ const mockUseLocationValue = {
|
||||
};
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useParams: () => {
|
||||
return { name: 'test' };
|
||||
@ -161,34 +160,27 @@ afterEach(() => {
|
||||
|
||||
describe('Repo details component', () => {
|
||||
it('fetches repo detailed data and renders', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsData } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findByText('test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders vulnerability icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsData } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('critical-vulnerability-icon')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsNone } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('none-vulnerability-icon')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
// jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsUnknown } });
|
||||
// render(<RepoDetails />);
|
||||
// expect(await screen.findAllByTestId('unknown-vulnerability-icon')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsLow } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-icon')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsMedium } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('medium-vulnerability-icon')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsHigh } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('high-vulnerability-icon')).toHaveLength(1);
|
||||
@ -202,7 +194,6 @@ describe('Repo details component', () => {
|
||||
});
|
||||
|
||||
it('should switch between tabs', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsData } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findByTestId('overview-container')).toBeInTheDocument();
|
||||
|
@ -4,7 +4,6 @@ import React from 'react';
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: () => ({
|
||||
pathname: 'localhost:3000/image/test',
|
||||
|
@ -4,7 +4,6 @@ import React from 'react';
|
||||
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
|
@ -6,7 +6,6 @@ import PreviewCard from 'components/PreviewCard';
|
||||
// usenavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
|
@ -6,7 +6,6 @@ import RepoCard from 'components/RepoCard';
|
||||
// usenavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
|
@ -7,7 +7,6 @@ import React from 'react';
|
||||
// router mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -71,7 +70,6 @@ afterEach(() => {
|
||||
|
||||
describe('Search component', () => {
|
||||
it('should display suggestions when user searches', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<SearchSuggestion />);
|
||||
const searchInput = screen.getByPlaceholderText(/search for content/i);
|
||||
@ -81,7 +79,6 @@ describe('Search component', () => {
|
||||
});
|
||||
|
||||
it('should navigate to repo page when a repo suggestion is clicked', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<SearchSuggestion />);
|
||||
const searchInput = screen.getByPlaceholderText(/search for content/i);
|
||||
@ -92,7 +89,6 @@ describe('Search component', () => {
|
||||
});
|
||||
|
||||
it('should navigate to repo page when a image suggestion is clicked', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<SearchSuggestion />);
|
||||
const searchInput = screen.getByPlaceholderText(/search for content/i);
|
||||
@ -103,7 +99,6 @@ describe('Search component', () => {
|
||||
});
|
||||
|
||||
it('should log an error if it doesnt receive an ok response for repo search', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<SearchSuggestion />);
|
||||
@ -113,7 +108,6 @@ describe('Search component', () => {
|
||||
});
|
||||
|
||||
it('should log an error if it doesnt receive an ok response for image search', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<SearchSuggestion />);
|
||||
|
@ -56,7 +56,6 @@ const RouterDependsWrapper = () => {
|
||||
// useNavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -68,14 +67,12 @@ afterEach(() => {
|
||||
|
||||
describe('Dependencies tab', () => {
|
||||
it('should render the dependencies if there are any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: mockDependenciesList });
|
||||
render(<RouterDependsWrapper />);
|
||||
expect(await screen.findAllByText(/Tag/i)).toHaveLength(8);
|
||||
});
|
||||
|
||||
it('renders no dependencies if there are not any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({
|
||||
status: 200,
|
||||
data: { data: { BaseImageList: [] } }
|
||||
@ -85,7 +82,6 @@ describe('Dependencies tab', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<RouterDependsWrapper />);
|
||||
|
@ -33,14 +33,12 @@ afterEach(() => {
|
||||
|
||||
describe('Layers page', () => {
|
||||
it('renders the layers if there are any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { Image: { History: mockLayersList } } } });
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
expect(await screen.findAllByTestId('layer-card-container')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('renders no layers if there are not any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({
|
||||
status: 200,
|
||||
data: { data: { History: { Tag: '', mockLayersList: [] } } }
|
||||
@ -50,7 +48,6 @@ describe('Layers page', () => {
|
||||
});
|
||||
|
||||
it('renders hash layers', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: { Image: { History: mockLayersList } } } });
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
expect(await screen.findAllByTestId('hash-typography')).toHaveLength(1);
|
||||
@ -60,7 +57,6 @@ describe('Layers page', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<HistoryLayers name="alpine:latest" />);
|
||||
|
@ -56,7 +56,6 @@ const RouterDependsWrapper = () => {
|
||||
// useNavigate mock
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate
|
||||
}));
|
||||
@ -68,14 +67,12 @@ afterEach(() => {
|
||||
|
||||
describe('Dependents tab', () => {
|
||||
it('should render the dependents if there are any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: mockDependentsList });
|
||||
render(<RouterDependsWrapper />);
|
||||
expect(await screen.findAllByText(/tag/i)).toHaveLength(8);
|
||||
});
|
||||
|
||||
it('renders no dependents if there are not any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({
|
||||
status: 200,
|
||||
data: { data: { DerivedImageList: [] } }
|
||||
@ -85,7 +82,6 @@ describe('Dependents tab', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<RouterDependsWrapper />);
|
||||
|
@ -170,7 +170,6 @@ Object.assign(navigator, {
|
||||
});
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
// @ts-ignore
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useParams: () => {
|
||||
return { name: 'test', tag: '1.0.1' };
|
||||
@ -193,7 +192,6 @@ afterEach(() => {
|
||||
|
||||
describe('Tags details', () => {
|
||||
it('should show tabs and allow nagivation between them', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
render(<TagDetails />);
|
||||
const dependenciesTab = await screen.findByTestId('dependencies-tab');
|
||||
@ -210,34 +208,32 @@ describe('Tags details', () => {
|
||||
});
|
||||
|
||||
it('should show tag details metadata', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('tagDetailsMetadata-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders vulnerability icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('critical-vulnerability-icon')).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageNone } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('none-vulnerability-icon')).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageUnknown } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('unknown-vulnerability-icon')).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageLow } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('low-vulnerability-icon')).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageMedium } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('medium-vulnerability-icon')).toBeInTheDocument();
|
||||
// @ts-ignore
|
||||
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageHigh } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findByTestId('high-vulnerability-icon')).toBeInTheDocument();
|
||||
@ -246,7 +242,7 @@ describe('Tags details', () => {
|
||||
it('should copy the pull string to clipboard', async () => {
|
||||
jest
|
||||
.spyOn(api, 'get')
|
||||
// @ts-ignore
|
||||
|
||||
.mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
render(<TagDetails />);
|
||||
fireEvent.click(await screen.findByTestId('pullcopy-btn'));
|
||||
|
@ -452,7 +452,6 @@ afterEach(() => {
|
||||
|
||||
describe('Vulnerabilties page', () => {
|
||||
it('renders the vulnerabilities if there are any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } });
|
||||
render(<StateVulnerabilitiesWrapper />);
|
||||
await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1));
|
||||
@ -460,7 +459,6 @@ describe('Vulnerabilties page', () => {
|
||||
});
|
||||
|
||||
it('renders no vulnerabilities if there are not any', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({
|
||||
status: 200,
|
||||
data: { data: { CVEListForImage: { Tag: '', CVEList: [] } } }
|
||||
@ -470,7 +468,6 @@ describe('Vulnerabilties page', () => {
|
||||
});
|
||||
|
||||
it('should open and close description dropdown for vulnerabilities', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } });
|
||||
render(<StateVulnerabilitiesWrapper />);
|
||||
await waitFor(() => expect(screen.getAllByText(/description/i)).toHaveLength(20));
|
||||
@ -486,7 +483,6 @@ describe('Vulnerabilties page', () => {
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<StateVulnerabilitiesWrapper />);
|
||||
@ -496,9 +492,9 @@ describe('Vulnerabilties page', () => {
|
||||
it('should find out which version fixes the CVEs', async () => {
|
||||
jest
|
||||
.spyOn(api, 'get')
|
||||
// @ts-ignore
|
||||
|
||||
.mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } })
|
||||
// @ts-ignore
|
||||
|
||||
.mockResolvedValue({ status: 200, data: { data: mockCVEFixed } });
|
||||
render(<StateVulnerabilitiesWrapper />);
|
||||
await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1));
|
||||
|
@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import axios from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { sortByCriteria } from 'utilities/sortCriteria';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../api';
|
||||
@ -9,6 +10,7 @@ import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host } from '../host';
|
||||
import Loading from './Loading';
|
||||
import TagCard from './TagCard';
|
||||
import { mapToImage } from 'utilities/objectModels';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
@ -78,7 +80,7 @@ function DependsOn(props) {
|
||||
.get(`${host()}${endpoints.dependsOnForImage(name)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let imagesData = response.data.data.BaseImageList;
|
||||
let imagesData = response.data.data.BaseImageList?.map((img) => mapToImage(img));
|
||||
setImages(imagesData);
|
||||
}
|
||||
setIsLoading(false);
|
||||
@ -93,20 +95,19 @@ function DependsOn(props) {
|
||||
}, []);
|
||||
|
||||
const renderDependencies = () => {
|
||||
return images?.length ? (
|
||||
return !isEmpty(images) ? (
|
||||
images.map((dependence, index) => {
|
||||
return (
|
||||
<TagCard
|
||||
repoName={dependence.RepoName}
|
||||
tag={dependence.Tag}
|
||||
vendor={dependence.Vendor}
|
||||
platform={dependence.Platform}
|
||||
isSigned={dependence.IsSigned}
|
||||
size={dependence.Size}
|
||||
digest={dependence.Digest}
|
||||
repoName={dependence.repoName}
|
||||
tag={dependence.tag}
|
||||
vendor={dependence.vendor}
|
||||
platform={dependence.platform}
|
||||
isSigned={dependence.isSigned}
|
||||
size={dependence.size}
|
||||
digest={dependence.digest}
|
||||
key={index}
|
||||
vulnerabiltySeverity={dependence.Vulnerabilities?.MaxSeverity}
|
||||
lastUpdated={dependence.LastUpdated}
|
||||
lastUpdated={dependence.lastUpdated}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../api';
|
||||
@ -9,6 +10,7 @@ import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host } from '../host';
|
||||
import Loading from './Loading';
|
||||
import TagCard from './TagCard';
|
||||
import { mapToImage } from 'utilities/objectModels';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
@ -78,7 +80,7 @@ function IsDependentOn(props) {
|
||||
.get(`${host()}${endpoints.isDependentOnForImage(name)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let imageData = response.data.data.DerivedImageList;
|
||||
let imageData = response.data.data.DerivedImageList?.map((img) => mapToImage(img));
|
||||
setImages(imageData);
|
||||
}
|
||||
setIsLoading(false);
|
||||
@ -93,20 +95,19 @@ function IsDependentOn(props) {
|
||||
}, []);
|
||||
|
||||
const renderDependents = () => {
|
||||
return images?.length ? (
|
||||
return !isEmpty(images) ? (
|
||||
images.map((dependence, index) => {
|
||||
return (
|
||||
<TagCard
|
||||
repoName={dependence.RepoName}
|
||||
tag={dependence.Tag}
|
||||
vendor={dependence.Vendor}
|
||||
platform={dependence.Platform}
|
||||
isSigned={dependence.IsSigned}
|
||||
size={dependence.Size}
|
||||
digest={dependence.Digest}
|
||||
repoName={dependence.repoName}
|
||||
tag={dependence.tag}
|
||||
vendor={dependence.vendor}
|
||||
platform={dependence.platform}
|
||||
isSigned={dependence.isSigned}
|
||||
size={dependence.size}
|
||||
digest={dependence.digest}
|
||||
key={index}
|
||||
vulnerabiltySeverity={dependence.Vulnerabilities?.MaxSeverity}
|
||||
lastUpdated={dependence.LastUpdated}
|
||||
lastUpdated={dependence.lastUpdated}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
@ -22,8 +22,8 @@ import RepoDetailsMetadata from './RepoDetailsMetadata';
|
||||
import Loading from './Loading';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { VulnerabilityIconCheck, SignatureIconCheck } from 'utilities/vulnerabilityAndSignatureCheck';
|
||||
import { mapToRepoFromRepoInfo } from 'utilities/objectModels';
|
||||
|
||||
// @ts-ignore
|
||||
const useStyles = makeStyles(() => ({
|
||||
pageWrapper: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
@ -123,7 +123,7 @@ const randomImage = () => {
|
||||
function RepoDetails() {
|
||||
const [repoDetailData, setRepoDetailData] = useState({});
|
||||
const [tags, setTags] = useState([]);
|
||||
// @ts-ignore
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedTab, setSelectedTab] = useState('Overview');
|
||||
// get url param from <Route here (i.e. image name)
|
||||
@ -137,25 +137,7 @@ function RepoDetails() {
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let repoInfo = response.data.data.ExpandedRepoInfo;
|
||||
let imageData = {
|
||||
name: name,
|
||||
images: repoInfo.Images,
|
||||
lastUpdated: repoInfo.Summary?.LastUpdated,
|
||||
size: repoInfo.Summary?.Size,
|
||||
platforms: repoInfo.Summary?.Platforms,
|
||||
vendors: repoInfo.Summary?.Vendors,
|
||||
newestTag: repoInfo.Summary?.NewestImage,
|
||||
description: repoInfo.Summary?.NewestImage?.Description,
|
||||
title: repoInfo.Summary?.NewestImage?.Title,
|
||||
source: repoInfo.Summary?.NewestImage?.Source,
|
||||
downloads: repoInfo.Summary?.NewestImage?.DownloadCount,
|
||||
overview: repoInfo.Summary?.NewestImage?.Documentation,
|
||||
license: repoInfo.Summary?.NewestImage?.Licenses,
|
||||
vulnerabiltySeverity: repoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: repoInfo.Summary?.NewestImage?.Vulnerabilities?.Count,
|
||||
isSigned: repoInfo.Summary?.NewestImage?.IsSigned,
|
||||
logo: repoInfo.Summary?.NewestImage?.Logo
|
||||
};
|
||||
let imageData = mapToRepoFromRepoInfo(repoInfo);
|
||||
setRepoDetailData(imageData);
|
||||
setTags(imageData.images);
|
||||
}
|
||||
@ -173,7 +155,6 @@ function RepoDetails() {
|
||||
}, [name]);
|
||||
|
||||
const platformChips = () => {
|
||||
// @ts-ignore
|
||||
const platforms = repoDetailData?.platforms || [];
|
||||
|
||||
return platforms.map((platform, index) => (
|
||||
@ -200,8 +181,6 @@ function RepoDetails() {
|
||||
));
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
const handleTabChange = (event, newValue) => {
|
||||
setSelectedTab(newValue);
|
||||
};
|
||||
@ -220,10 +199,7 @@ function RepoDetails() {
|
||||
alignSelf: 'stretch'
|
||||
}}
|
||||
>
|
||||
{
|
||||
// @ts-ignore
|
||||
repoDetailData.description || 'Description not available'
|
||||
}
|
||||
{repoDetailData.description || 'Description not available'}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -247,13 +223,10 @@ function RepoDetails() {
|
||||
img: classes.avatar
|
||||
}}
|
||||
component="img"
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
image={
|
||||
// @ts-ignore
|
||||
!isEmpty(repoDetailData?.logo)
|
||||
? // @ts-ignore
|
||||
`data:image/png;base64, ${repoDetailData?.logo}`
|
||||
? `data:image/png;base64, ${repoDetailData?.logo}`
|
||||
: randomImage()
|
||||
}
|
||||
alt="icon"
|
||||
@ -262,19 +235,10 @@ function RepoDetails() {
|
||||
{name}
|
||||
</Typography>
|
||||
<VulnerabilityIconCheck
|
||||
vulnerabilitySeverity={
|
||||
// @ts-ignore
|
||||
repoDetailData.vulnerabiltySeverity
|
||||
}
|
||||
// @ts-ignore
|
||||
vulnerabilitySeverity={repoDetailData.vulnerabiltySeverity}
|
||||
count={repoDetailData?.vulnerabilityCount}
|
||||
/>
|
||||
<SignatureIconCheck
|
||||
isSigned={
|
||||
// @ts-ignore
|
||||
repoDetailData.isSigned
|
||||
}
|
||||
/>
|
||||
<SignatureIconCheck isSigned={repoDetailData.isSigned} />
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
@ -283,10 +247,7 @@ function RepoDetails() {
|
||||
gutterBottom
|
||||
align="left"
|
||||
>
|
||||
{
|
||||
// @ts-ignore
|
||||
repoDetailData?.title || 'Title not available'
|
||||
}
|
||||
{repoDetailData?.title || 'Title not available'}
|
||||
</Typography>
|
||||
<Stack alignItems="center" sx={{ paddingLeft: '4rem' }} direction="row" spacing={2} pt={1}>
|
||||
{platformChips()}
|
||||
@ -320,17 +281,11 @@ function RepoDetails() {
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.metadata}>
|
||||
<RepoDetailsMetadata
|
||||
// @ts-ignore
|
||||
totalDownloads={repoDetailData?.downloads}
|
||||
// @ts-ignore
|
||||
repoURL={repoDetailData?.source}
|
||||
// @ts-ignore
|
||||
lastUpdated={repoDetailData?.lastUpdated}
|
||||
// @ts-ignore
|
||||
size={repoDetailData?.size}
|
||||
// @ts-ignore
|
||||
latestTag={repoDetailData?.newestTag}
|
||||
// @ts-ignore
|
||||
license={repoDetailData?.license}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -37,7 +37,7 @@ const useStyles = makeStyles(() => ({
|
||||
function RepoDetailsMetadata(props) {
|
||||
const classes = useStyles();
|
||||
const { repoURL, totalDownloads, lastUpdated, size, license } = props;
|
||||
// @ts-ignore
|
||||
|
||||
const lastDate = (lastUpdated ? DateTime.fromISO(lastUpdated) : DateTime.now().minus({ days: 1 })).toRelative({
|
||||
unit: ['weeks', 'days', 'hours', 'minutes']
|
||||
});
|
||||
|
@ -65,7 +65,7 @@ export default function TagCard(props) {
|
||||
//const tags = data && data.tags;
|
||||
const [open, setOpen] = useState(false);
|
||||
const classes = useStyles();
|
||||
// @ts-ignore
|
||||
|
||||
const lastDate = (lastUpdated ? DateTime.fromISO(lastUpdated) : DateTime.now().minus({ days: 1 })).toRelative({
|
||||
unit: ['weeks', 'days', 'hours', 'minutes']
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
// utility
|
||||
import { api, endpoints } from '../api';
|
||||
|
||||
import { mapToImage } from '../utilities/objectModels';
|
||||
// components
|
||||
import {
|
||||
Box,
|
||||
@ -41,7 +41,6 @@ import Loading from './Loading';
|
||||
import { dockerPull, podmanPull, skopeoPull } from 'utilities/pullStrings';
|
||||
import { VulnerabilityIconCheck, SignatureIconCheck } from 'utilities/vulnerabilityAndSignatureCheck';
|
||||
|
||||
// @ts-ignore
|
||||
const useStyles = makeStyles(() => ({
|
||||
pageWrapper: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
@ -157,8 +156,7 @@ function TagDetails() {
|
||||
const abortController = useMemo(() => new AbortController(), []);
|
||||
|
||||
// get url param from <Route here (i.e. image name)
|
||||
const { name, tag } = useParams();
|
||||
const [fullName, setFullName] = useState(name + ':' + tag);
|
||||
const { reponame, tag } = useParams();
|
||||
|
||||
const [pullString, setPullString] = useState('');
|
||||
const [snackBarOpen, setSnackbarOpen] = useState(false);
|
||||
@ -170,28 +168,13 @@ function TagDetails() {
|
||||
window?.scrollTo(0, 0);
|
||||
setIsLoading(true);
|
||||
api
|
||||
.get(`${host()}${endpoints.detailedImageInfo(name, tag)}`, abortController.signal)
|
||||
.get(`${host()}${endpoints.detailedImageInfo(reponame, tag)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let imageInfo = response.data.data.Image;
|
||||
let imageData = {
|
||||
name: imageInfo.RepoName,
|
||||
tag: imageInfo.Tag,
|
||||
lastUpdated: imageInfo.LastUpdated,
|
||||
size: imageInfo.Size,
|
||||
digest: imageInfo.ConfigDigest,
|
||||
platform: imageInfo.Platform,
|
||||
vendor: imageInfo.Vendor,
|
||||
history: imageInfo.History,
|
||||
license: imageInfo.Licenses,
|
||||
vulnerabiltySeverity: imageInfo.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: imageInfo.Vulnerabilities?.Count,
|
||||
isSigned: imageInfo.IsSigned,
|
||||
logo: imageInfo.Logo
|
||||
};
|
||||
let imageData = mapToImage(imageInfo);
|
||||
setImageDetailData(imageData);
|
||||
setFullName(imageData.name + ':' + imageData.tag);
|
||||
setPullString(dockerPull(imageData.name + ':' + imageData.tag));
|
||||
setPullString(dockerPull(imageData.name));
|
||||
}
|
||||
setIsLoading(false);
|
||||
})
|
||||
@ -203,14 +186,12 @@ function TagDetails() {
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [name, tag]);
|
||||
}, [reponame, tag]);
|
||||
|
||||
const getPlatform = () => {
|
||||
// @ts-ignore
|
||||
return imageDetailData?.platform ? imageDetailData.platform : '--/--';
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const handleTabChange = (event, newValue) => {
|
||||
setSelectedTab(newValue);
|
||||
};
|
||||
@ -245,32 +226,20 @@ function TagDetails() {
|
||||
}}
|
||||
component="img"
|
||||
image={
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
!isEmpty(imageDetailData?.logo)
|
||||
? // @ts-ignore
|
||||
`data:image/ png;base64, ${imageDetailData?.logo}`
|
||||
? `data:image/ png;base64, ${imageDetailData?.logo}`
|
||||
: randomImage()
|
||||
}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
{name}:{tag}
|
||||
{reponame}:{tag}
|
||||
</Typography>
|
||||
<VulnerabilityIconCheck
|
||||
vulnerabilitySeverity={
|
||||
// @ts-ignore
|
||||
imageDetailData.vulnerabiltySeverity
|
||||
}
|
||||
// @ts-ignore
|
||||
vulnerabilitySeverity={imageDetailData.vulnerabiltySeverity}
|
||||
count={imageDetailData.vulnerabilityCount}
|
||||
/>
|
||||
<SignatureIconCheck
|
||||
isSigned={
|
||||
// @ts-ignore
|
||||
imageDetailData.isSigned
|
||||
}
|
||||
/>
|
||||
<SignatureIconCheck isSigned={imageDetailData.isSigned} />
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
@ -279,11 +248,7 @@ function TagDetails() {
|
||||
gutterBottom
|
||||
align="left"
|
||||
>
|
||||
DIGEST:{' '}
|
||||
{
|
||||
// @ts-ignore
|
||||
imageDetailData?.digest
|
||||
}
|
||||
DIGEST: {imageDetailData?.digest}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.pull}>
|
||||
@ -320,14 +285,14 @@ function TagDetails() {
|
||||
classes: { root: classes.copyStringSelect, list: classes.textEllipsis }
|
||||
}}
|
||||
>
|
||||
<MenuItem className={classes.textEllipsis} value={dockerPull(fullName)}>
|
||||
<Typography noWrap>{dockerPull(fullName)}</Typography>
|
||||
<MenuItem className={classes.textEllipsis} value={dockerPull(imageDetailData.name)}>
|
||||
<Typography noWrap>{dockerPull(imageDetailData.name)}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.textEllipsis} value={podmanPull(fullName)}>
|
||||
<Typography noWrap>{podmanPull(fullName)}</Typography>
|
||||
<MenuItem className={classes.textEllipsis} value={podmanPull(imageDetailData.name)}>
|
||||
<Typography noWrap>{podmanPull(imageDetailData.name)}</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.textEllipsis} value={skopeoPull(fullName)}>
|
||||
<Typography noWrap>{skopeoPull(fullName)}</Typography>
|
||||
<MenuItem className={classes.textEllipsis} value={skopeoPull(imageDetailData.name)}>
|
||||
<Typography noWrap>{skopeoPull(imageDetailData.name)}</Typography>
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
@ -355,22 +320,16 @@ function TagDetails() {
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel value="Layers" className={classes.tabPanel}>
|
||||
<HistoryLayers
|
||||
name={fullName}
|
||||
history={
|
||||
// @ts-ignore
|
||||
imageDetailData.history
|
||||
}
|
||||
/>
|
||||
<HistoryLayers name={imageDetailData.name} history={imageDetailData.history} />
|
||||
</TabPanel>
|
||||
<TabPanel value="DependsOn" className={classes.tabPanel}>
|
||||
<DependsOn name={fullName} />
|
||||
<DependsOn name={imageDetailData.name} />
|
||||
</TabPanel>
|
||||
<TabPanel value="IsDependentOn" className={classes.tabPanel}>
|
||||
<IsDependentOn name={fullName} />
|
||||
<IsDependentOn name={imageDetailData.name} />
|
||||
</TabPanel>
|
||||
<TabPanel value="Vulnerabilities" className={classes.tabPanel}>
|
||||
<VulnerabilitiesDetails name={name} tag={tag} />
|
||||
<VulnerabilitiesDetails name={reponame} tag={tag} />
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -379,13 +338,9 @@ function TagDetails() {
|
||||
</Grid>
|
||||
<Grid item xs={4} className={classes.metadata}>
|
||||
<TagDetailsMetadata
|
||||
// @ts-ignore
|
||||
platform={getPlatform()}
|
||||
// @ts-ignore
|
||||
size={imageDetailData?.size}
|
||||
// @ts-ignore
|
||||
lastUpdated={imageDetailData?.lastUpdated}
|
||||
// @ts-ignore
|
||||
license={imageDetailData?.license}
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -13,6 +13,7 @@ import { Link } from 'react-router-dom';
|
||||
import Loading from './Loading';
|
||||
import { KeyboardArrowDown, KeyboardArrowRight } from '@mui/icons-material';
|
||||
import { VulnerabilityChipCheck } from 'utilities/vulnerabilityAndSignatureCheck';
|
||||
import { mapCVEInfo } from 'utilities/objectModels';
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
card: {
|
||||
@ -96,7 +97,7 @@ function VulnerabilitiyCard(props) {
|
||||
}
|
||||
setLoadingFixed(true);
|
||||
api
|
||||
.get(`${host()}${endpoints.imageListWithCVEFixed(cve.Id, name)}`)
|
||||
.get(`${host()}${endpoints.imageListWithCVEFixed(cve.id, name)}`)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
const fixedTagsList = response.data.data.ImageListWithCVEFixed?.map((e) => e.Tag);
|
||||
@ -129,14 +130,14 @@ function VulnerabilitiyCard(props) {
|
||||
<Stack sx={{ flexDirection: 'row' }}>
|
||||
<Typography variant="body1" align="left" className={classes.values}>
|
||||
{' '}
|
||||
{cve.Id}
|
||||
{cve.id}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<VulnerabilityChipCheck vulnerabilitySeverity={cve.Severity} />
|
||||
<VulnerabilityChipCheck vulnerabilitySeverity={cve.severity} />
|
||||
<Stack sx={{ flexDirection: 'row' }}>
|
||||
<Typography variant="body1" align="left" className={classes.values}>
|
||||
{' '}
|
||||
{cve.Title}
|
||||
{cve.title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack className={classes.dropdown} onClick={() => setOpenFixed(!openFixed)}>
|
||||
@ -167,7 +168,7 @@ function VulnerabilitiyCard(props) {
|
||||
<Box>
|
||||
<Typography variant="body2" align="left" sx={{ color: '#0F2139', fontSize: '1rem' }}>
|
||||
{' '}
|
||||
{cve.Description}{' '}
|
||||
{cve.description}{' '}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Collapse>
|
||||
@ -190,9 +191,7 @@ function VulnerabilitiesDetails(props) {
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let cveInfo = response.data.data.CVEListForImage;
|
||||
let cveListData = {
|
||||
cveList: cveInfo?.CVEList
|
||||
};
|
||||
let cveListData = mapCVEInfo(cveInfo);
|
||||
setCveData(cveListData);
|
||||
}
|
||||
setIsLoading(false);
|
||||
@ -249,14 +248,7 @@ function VulnerabilitiesDetails(props) {
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
renderCVEs(
|
||||
// @ts-ignore
|
||||
cveData?.cveList
|
||||
)
|
||||
)}
|
||||
{isLoading ? <Loading /> : renderCVEs(cveData?.cveList)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -17,14 +17,60 @@ const mapToRepo = (responseRepo) => {
|
||||
};
|
||||
};
|
||||
|
||||
const mapToRepoFromRepoInfo = (responseRepoInfo) => {
|
||||
return {
|
||||
name: responseRepoInfo.Summary?.Name,
|
||||
images: responseRepoInfo.Images,
|
||||
lastUpdated: responseRepoInfo.Summary?.LastUpdated,
|
||||
size: responseRepoInfo.Summary?.Size,
|
||||
platforms: responseRepoInfo.Summary?.Platforms,
|
||||
vendors: responseRepoInfo.Summary?.Vendors,
|
||||
newestTag: responseRepoInfo.Summary?.NewestImage,
|
||||
description: responseRepoInfo.Summary?.NewestImage?.Description,
|
||||
title: responseRepoInfo.Summary?.NewestImage?.Title,
|
||||
source: responseRepoInfo.Summary?.NewestImage?.Source,
|
||||
downloads: responseRepoInfo.Summary?.NewestImage?.DownloadCount,
|
||||
overview: responseRepoInfo.Summary?.NewestImage?.Documentation,
|
||||
license: responseRepoInfo.Summary?.NewestImage?.Licenses,
|
||||
vulnerabiltySeverity: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.Count,
|
||||
isSigned: responseRepoInfo.Summary?.NewestImage?.IsSigned,
|
||||
logo: responseRepoInfo.Summary?.NewestImage?.Logo
|
||||
};
|
||||
};
|
||||
|
||||
const mapToImage = (responseImage) => {
|
||||
return {
|
||||
repoName: responseImage.RepoName,
|
||||
tag: responseImage.Tag,
|
||||
lastUpdated: responseImage.LastUpdated,
|
||||
size: responseImage.Size,
|
||||
digest: responseImage.ConfigDigest,
|
||||
platform: responseImage.Platform,
|
||||
vendor: responseImage.Vendor,
|
||||
history: responseImage.History,
|
||||
license: responseImage.Licenses,
|
||||
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}`,
|
||||
logo: responseImage.Logo
|
||||
name: `${responseImage.RepoName}:${responseImage.Tag}`
|
||||
};
|
||||
};
|
||||
|
||||
export { mapToRepo, mapToImage };
|
||||
const mapCVEInfo = (cveInfo) => {
|
||||
const cveList = cveInfo.CVEList?.map((cve) => {
|
||||
return {
|
||||
id: cve.Id,
|
||||
severity: cve.Severity,
|
||||
title: cve.Title,
|
||||
description: cve.Description
|
||||
};
|
||||
});
|
||||
return {
|
||||
cveList
|
||||
};
|
||||
};
|
||||
|
||||
export { mapToRepo, mapToImage, mapToRepoFromRepoInfo, mapCVEInfo };
|
||||
|
@ -15,7 +15,7 @@ const transform = {
|
||||
let value = bytes / Math.pow(k, unitIdx);
|
||||
|
||||
// minimum 2 significant digits
|
||||
// @ts-ignore
|
||||
|
||||
value = value < 10 ? value.toPrecision(2) : Math.round(value);
|
||||
|
||||
return value + ' ' + DATA_UNITS[unitIdx];
|
||||
|
Loading…
Reference in New Issue
Block a user