mirror of
https://github.com/strapi/strapi.git
synced 2025-12-16 09:45:08 +00:00
audit logs api mapping, updated test cases
This commit is contained in:
parent
477cd21eba
commit
10dbf10d0e
@ -1,13 +1,23 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import useFormatTimeStamp from '../../hooks/useFormatTimeStamp';
|
||||
import getDefaultMessage from '../../utils/getActionTypesDefaultMessages';
|
||||
|
||||
const CellValue = ({ type, value }) => {
|
||||
const formatTimeStamp = useFormatTimeStamp();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (type === 'date') {
|
||||
return formatTimeStamp(value);
|
||||
}
|
||||
|
||||
if (type === 'action') {
|
||||
return formatMessage({
|
||||
id: `Settings.permissions.auditLogs.${value}`,
|
||||
defaultMessage: getDefaultMessage(value),
|
||||
});
|
||||
}
|
||||
|
||||
return value || '-';
|
||||
};
|
||||
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { Typography } from '@strapi/design-system/Typography';
|
||||
|
||||
const ActionItem = ({ actionLabel, actionName }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<Flex direction="column" alignItems="baseline" gap={1}>
|
||||
<Typography textColor="neutral600" variant="sigma">
|
||||
{formatMessage(actionLabel)}
|
||||
{actionLabel}
|
||||
</Typography>
|
||||
<Typography textColor="neutral600">{actionName}</Typography>
|
||||
</Flex>
|
||||
@ -18,7 +15,7 @@ const ActionItem = ({ actionLabel, actionName }) => {
|
||||
};
|
||||
|
||||
ActionItem.propTypes = {
|
||||
actionLabel: PropTypes.object.isRequired,
|
||||
actionLabel: PropTypes.string.isRequired,
|
||||
actionName: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import { Grid } from '@strapi/design-system/Grid';
|
||||
import { Box } from '@strapi/design-system/Box';
|
||||
import { Typography } from '@strapi/design-system/Typography';
|
||||
import { pxToRem } from '@strapi/helper-plugin';
|
||||
import getDefaultMessage from '../utils/getActionTypesDefaultMessages';
|
||||
import useFormatTimeStamp from '../hooks/useFormatTimeStamp';
|
||||
import ActionItem from './ActionItem';
|
||||
|
||||
@ -42,25 +43,28 @@ const ModalDialog = ({ onToggle, data: { date, user, action } }) => {
|
||||
hasRadius
|
||||
>
|
||||
<ActionItem
|
||||
actionLabel={{
|
||||
actionLabel={formatMessage({
|
||||
id: 'Settings.permissions.auditLogs.action',
|
||||
defaultMessage: 'Action',
|
||||
}}
|
||||
actionName={action}
|
||||
})}
|
||||
actionName={formatMessage({
|
||||
id: `Settings.permissions.auditLogs.${action}`,
|
||||
defaultMessage: getDefaultMessage(action),
|
||||
})}
|
||||
/>
|
||||
<ActionItem
|
||||
actionLabel={{
|
||||
actionLabel={formatMessage({
|
||||
id: 'Settings.permissions.auditLogs.date',
|
||||
defaultMessage: 'Date',
|
||||
}}
|
||||
})}
|
||||
actionName={formattedDate}
|
||||
/>
|
||||
<ActionItem
|
||||
actionLabel={{
|
||||
actionLabel={formatMessage({
|
||||
id: 'Settings.permissions.auditLogs.user',
|
||||
defaultMessage: 'User',
|
||||
}}
|
||||
actionName={user}
|
||||
})}
|
||||
actionName={user || '-'}
|
||||
/>
|
||||
</Grid>
|
||||
</ModalBody>
|
||||
|
||||
@ -1,43 +1,61 @@
|
||||
import React, { useState } from 'react';
|
||||
import { SettingsPageTitle, DynamicTable } from '@strapi/helper-plugin';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useQuery } from 'react-query';
|
||||
import {
|
||||
SettingsPageTitle,
|
||||
DynamicTable,
|
||||
useRBAC,
|
||||
useNotification,
|
||||
useFocusWhenNavigate,
|
||||
} from '@strapi/helper-plugin';
|
||||
import { HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
|
||||
import { Main } from '@strapi/design-system/Main';
|
||||
import { useIntl } from 'react-intl';
|
||||
import adminPermissions from '../../../../../permissions';
|
||||
// import { useFetchClient } from '../../../../../hooks';
|
||||
import TableRows from './DynamicTable/TableRows';
|
||||
import tableHeaders from './utils/tableHeaders';
|
||||
import { fetchData } from './utils/api';
|
||||
import ModalDialog from './ModalDialog';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
action: 'Update',
|
||||
date: '2022-11-14T23:04:00.000Z',
|
||||
user: 'John Doe',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
action: 'Create',
|
||||
date: '2022-11-04T18:24:00.000Z',
|
||||
user: 'Kai Doe',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
action: 'Delete',
|
||||
date: '2022-10-09T11:26:00.000Z',
|
||||
user: 'John Doe',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
action: 'Log in',
|
||||
date: '2022-10-09T11:24:00.000Z',
|
||||
user: 'Kai Doe',
|
||||
},
|
||||
];
|
||||
const QUERY = 'audit-logs';
|
||||
|
||||
const ListView = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [detailsActionData, setDetailsActionData] = useState(null);
|
||||
const toggleNotification = useNotification();
|
||||
const {
|
||||
allowedActions: { canRead },
|
||||
} = useRBAC(adminPermissions.settings.auditLogs);
|
||||
|
||||
useFocusWhenNavigate();
|
||||
|
||||
/**
|
||||
* TODO: using fetchclient facing difficulties to mock, for time being using axiosinstance to pass the tests
|
||||
*
|
||||
const { get } = useFetchClient();
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { results },
|
||||
} = await get(`/admin/audit-logs`);
|
||||
|
||||
return results;
|
||||
}; */
|
||||
|
||||
const { status, data, isFetching } = useQuery(QUERY, fetchData, {
|
||||
enabled: canRead,
|
||||
keepPreviousData: true,
|
||||
retry: false,
|
||||
staleTime: 1000 * 20,
|
||||
onError() {
|
||||
toggleNotification({
|
||||
type: 'warning',
|
||||
message: { id: 'notification.error', defaultMessage: 'An error occured' },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isLoading = status === 'loading' || isFetching;
|
||||
|
||||
const title = formatMessage({
|
||||
id: 'global.auditLogs',
|
||||
@ -62,17 +80,23 @@ const ListView = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Main>
|
||||
<Main aria-busy={isLoading}>
|
||||
<SettingsPageTitle name={title} />
|
||||
<HeaderLayout
|
||||
title={title}
|
||||
subtitle={formatMessage({
|
||||
id: 'Settings.permissions.auditLogs.listview.header.subtitle',
|
||||
defaultMessage: 'Logs of all the activities that happened on your environment',
|
||||
defaultMessage: 'Logs of all the activities that happened in your environment',
|
||||
})}
|
||||
/>
|
||||
<ContentLayout>
|
||||
<DynamicTable contentType="Audit logs" headers={headers} rows={data} withBulkActions>
|
||||
<DynamicTable
|
||||
contentType="Audit logs"
|
||||
headers={headers}
|
||||
rows={data}
|
||||
withBulkActions
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<TableRows headers={headers} rows={data} onModalToggle={handleToggle} />
|
||||
</DynamicTable>
|
||||
</ContentLayout>
|
||||
|
||||
@ -2,16 +2,42 @@ import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import { TrackingProvider } from '@strapi/helper-plugin';
|
||||
// import { useFetchClient } from '../../../../../../hooks';
|
||||
import ListView from '../index';
|
||||
import server from './utils/server';
|
||||
|
||||
const history = createMemoryHistory();
|
||||
const user = userEvent.setup();
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useNotification: jest.fn(),
|
||||
useFocusWhenNavigate: jest.fn(),
|
||||
useRBAC: jest.fn(() => ({
|
||||
allowedActions: { canRead: true },
|
||||
})),
|
||||
}));
|
||||
|
||||
// jest.mock('../../../../../../hooks', () => ({
|
||||
// ...jest.requireActual('../../../../../../hooks'),
|
||||
// useFetchClient: jest.fn(),
|
||||
// }));
|
||||
|
||||
const client = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const App = (
|
||||
<QueryClientProvider client={client}>
|
||||
<TrackingProvider>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en" textComponent="span">
|
||||
@ -21,24 +47,47 @@ const App = (
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
</TrackingProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
describe('ADMIN | Pages | AUDIT LOGS | ListView', () => {
|
||||
beforeAll(() => {
|
||||
server.listen();
|
||||
// useFetchClient.mockImplementationOnce(() => ({
|
||||
// get: jest.fn(),
|
||||
// }));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => server.resetHandlers());
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should render page with right header details', () => {
|
||||
render(App);
|
||||
const title = screen.getByText(/audit logs/i);
|
||||
expect(title).toBeInTheDocument();
|
||||
const subTitle = screen.getByText(
|
||||
/logs of all the activities that happened on your environment/i
|
||||
/logs of all the activities that happened in your environment/i
|
||||
);
|
||||
expect(subTitle).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show a list of audit logs with right count', () => {
|
||||
const { container } = render(App);
|
||||
const rows = container.querySelector('tbody').querySelectorAll('tr');
|
||||
expect(rows.length).toBe(4);
|
||||
expect(screen.getByText('Update')).toBeInTheDocument();
|
||||
it('should show a list of audit logs with right count', async () => {
|
||||
render(App);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Create role')).toBeInTheDocument();
|
||||
expect(screen.getByText('Delete role')).toBeInTheDocument();
|
||||
expect(screen.getByText('Create entry')).toBeInTheDocument();
|
||||
expect(screen.getByText('Admin logout')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should open a modal when clicked on a table row and close modal when clicked', async () => {
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
import { setupServer } from 'msw/node';
|
||||
import { rest } from 'msw';
|
||||
|
||||
const handlers = [
|
||||
rest.get('*/audit-logs', (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.delay(1000),
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
action: 'role.create',
|
||||
date: '2022-12-27T10:02:06.598Z',
|
||||
user: 'test user',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
action: 'role.delete',
|
||||
date: '2022-12-27T16:28:08.977Z',
|
||||
user: 'test user',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
action: 'entry.create',
|
||||
date: '2022-12-27T17:34:00.673Z',
|
||||
user: null,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
action: 'admin.logout',
|
||||
date: '2022-12-27T17:51:04.146Z',
|
||||
user: 'test user',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
];
|
||||
|
||||
const server = setupServer(...handlers);
|
||||
|
||||
export default server;
|
||||
@ -0,0 +1,11 @@
|
||||
import { axiosInstance } from '../../../../../../core/utils';
|
||||
|
||||
const fetchData = async () => {
|
||||
const {
|
||||
data: { results },
|
||||
} = await axiosInstance.get(`/admin/audit-logs`);
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export { fetchData };
|
||||
@ -0,0 +1,58 @@
|
||||
const getDefaultMessage = (value) => {
|
||||
switch (value) {
|
||||
case 'entry.create':
|
||||
return 'Create entry';
|
||||
case 'entry.update':
|
||||
return 'Update entry';
|
||||
case 'entry.delete':
|
||||
return 'Delete entry';
|
||||
case 'entry.publish':
|
||||
return 'Publish entry';
|
||||
case 'entry.unpublish':
|
||||
return 'Unpublish entry';
|
||||
case 'media.create':
|
||||
return 'Create media';
|
||||
case 'media.update':
|
||||
return 'Update media';
|
||||
case 'media.delete':
|
||||
return 'Delete media';
|
||||
case 'user.create':
|
||||
return 'Create user';
|
||||
case 'user.update':
|
||||
return 'Update user';
|
||||
case 'user.delete':
|
||||
return 'Delete user';
|
||||
case 'admin.auth.success':
|
||||
return 'Admin login';
|
||||
case 'admin.logout':
|
||||
return 'Admin logout';
|
||||
case 'content-type.create':
|
||||
return 'Create content type';
|
||||
case 'content-type.update':
|
||||
return 'Update content type';
|
||||
case 'content-type.delete':
|
||||
return 'Delete content type';
|
||||
case 'component.create':
|
||||
return 'Create component';
|
||||
case 'component.update':
|
||||
return 'Update component';
|
||||
case 'component.delete':
|
||||
return 'Delete component';
|
||||
case 'role.create':
|
||||
return 'Create role';
|
||||
case 'role.delete':
|
||||
return 'Delete role';
|
||||
case 'role.update':
|
||||
return 'Update role';
|
||||
case 'permission.create':
|
||||
return 'Create permission';
|
||||
case 'permission.delete':
|
||||
return 'Delete permission';
|
||||
case 'permission.update':
|
||||
return 'Update permission';
|
||||
default:
|
||||
return '-';
|
||||
}
|
||||
};
|
||||
|
||||
export default getDefaultMessage;
|
||||
@ -21,17 +21,17 @@ const tableHeaders = [
|
||||
sortable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'user',
|
||||
name: 'user',
|
||||
metadatas: {
|
||||
label: {
|
||||
id: 'Settings.permissions.auditLogs.user',
|
||||
defaultMessage: 'User',
|
||||
},
|
||||
sortable: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'user',
|
||||
// name: 'user',
|
||||
// metadatas: {
|
||||
// label: {
|
||||
// id: 'Settings.permissions.auditLogs.user',
|
||||
// defaultMessage: 'User',
|
||||
// },
|
||||
// sortable: true,
|
||||
// },
|
||||
// },
|
||||
];
|
||||
|
||||
export default tableHeaders;
|
||||
|
||||
@ -179,7 +179,32 @@
|
||||
"Settings.permissions.auditLogs.date": "Date",
|
||||
"Settings.permissions.auditLogs.user": "User",
|
||||
"Settings.permissions.auditLogs.details": "Log Details",
|
||||
"Settings.permissions.auditLogs.listview.header.subtitle": "Logs of all the activities that happened on your environment",
|
||||
"Settings.permissions.auditLogs.listview.header.subtitle": "Logs of all the activities that happened in your environment",
|
||||
"Settings.permissions.auditLogs.entry.create": "Create entry",
|
||||
"Settings.permissions.auditLogs.entry.update": "Update entry",
|
||||
"Settings.permissions.auditLogs.entry.delete": "Delete entry",
|
||||
"Settings.permissions.auditLogs.entry.publish": "Publish entry",
|
||||
"Settings.permissions.auditLogs.entry.unpublish": "Unpublish entry",
|
||||
"Settings.permissions.auditLogs.media.create": "Create media",
|
||||
"Settings.permissions.auditLogs.media.update": "Update media",
|
||||
"Settings.permissions.auditLogs.media.delete": "Delete media",
|
||||
"Settings.permissions.auditLogs.user.create": "Create user",
|
||||
"Settings.permissions.auditLogs.user.update": "Update user",
|
||||
"Settings.permissions.auditLogs.user.delete": "Delete user",
|
||||
"Settings.permissions.auditLogs.admin.auth.success": "Admin login",
|
||||
"Settings.permissions.auditLogs.admin.logout": "Admin logout",
|
||||
"Settings.permissions.auditLogs.content-type.create": "Create content type",
|
||||
"Settings.permissions.auditLogs.content-type.update": "Update content type",
|
||||
"Settings.permissions.auditLogs.content-type.delete": "Delete content type",
|
||||
"Settings.permissions.auditLogs.component.create": "Create component",
|
||||
"Settings.permissions.auditLogs.component.update": "Update component",
|
||||
"Settings.permissions.auditLogs.component.delete": "Delete component",
|
||||
"Settings.permissions.auditLogs.role.create": "Create role",
|
||||
"Settings.permissions.auditLogs.role.update": "Update role",
|
||||
"Settings.permissions.auditLogs.role.delete": "Delete role",
|
||||
"Settings.permissions.auditLogs.permission.create": "Create permission",
|
||||
"Settings.permissions.auditLogs.permission.update": "Update permission",
|
||||
"Settings.permissions.auditLogs.permission.delete": "Delete permission",
|
||||
"Settings.profile.form.notify.data.loaded": "Your profile data has been loaded",
|
||||
"Settings.profile.form.section.experience.clear.select": "Clear the interface language selected",
|
||||
"Settings.profile.form.section.experience.here": "here",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user