1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: add a button to download user access information (#4746)

This commit is contained in:
Simon Hornby 2023-09-15 11:51:29 +02:00 committed by GitHub
parent 53c40372dd
commit 7843c93dc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 1 deletions

View File

@ -0,0 +1,17 @@
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
import { IconButton } from '@mui/material';
import { Download } from '@mui/icons-material';
import { useAccessOverviewApi } from 'hooks/api/actions/useAccessOverviewApi/useAccessOverviewApi';
export const AccessOverview = () => {
const { downloadCSV } = useAccessOverviewApi();
return (
<IconButton onClick={downloadCSV}>
<Download />
</IconButton>
);
};

View File

@ -6,6 +6,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded';
import { useUsers } from 'hooks/api/getters/useUsers/useUsers';
import useAdminUsersApi from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
import { useAccessOverviewApi } from 'hooks/api/actions/useAccessOverviewApi/useAccessOverviewApi';
import { IUser } from 'interfaces/user';
import { IRole } from 'interfaces/role';
import useToast from 'hooks/useToast';
@ -13,7 +14,7 @@ import { formatUnknownError } from 'utils/formatUnknownError';
import { useUsersPlan } from 'hooks/useUsersPlan';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { Button, useMediaQuery } from '@mui/material';
import { Button, IconButton, Tooltip, useMediaQuery } from '@mui/material';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { UserTypeCell } from './UserTypeCell/UserTypeCell';
import { useFlexLayout, useSortBy, useTable } from 'react-table';
@ -31,12 +32,15 @@ import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColum
import { UserLimitWarning } from './UserLimitWarning/UserLimitWarning';
import { RoleCell } from 'component/common/Table/cells/RoleCell/RoleCell';
import { useSearch } from 'hooks/useSearch';
import { Download } from '@mui/icons-material';
import { useUiFlag } from 'hooks/useUiFlag';
const UsersList = () => {
const navigate = useNavigate();
const { users, roles, refetch, loading } = useUsers();
const { setToastData, setToastApiError } = useToast();
const { removeUser, userLoading, userApiErrors } = useAdminUsersApi();
const { downloadCSV } = useAccessOverviewApi();
const [pwDialog, setPwDialog] = useState<{ open: boolean; user?: IUser }>({
open: false,
});
@ -47,6 +51,8 @@ const UsersList = () => {
const [delUser, setDelUser] = useState<IUser>();
const { planUsers, isBillingUsers } = useUsersPlan(users);
const accessOverviewEnabled = useUiFlag('accessOverview');
const [searchValue, setSearchValue] = useState('');
const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
@ -263,6 +269,23 @@ const UsersList = () => {
onChange={setSearchValue}
/>
<PageHeader.Divider />
<ConditionallyRender
condition={Boolean(accessOverviewEnabled)}
show={() => (
<>
<Tooltip
title="Exports user access information"
arrow
describeChild
>
<IconButton onClick={downloadCSV}>
<Download />
</IconButton>
</Tooltip>
</>
)}
/>
<Button
variant="contained"
color="primary"

View File

@ -0,0 +1,30 @@
import useAPI from '../useApi/useApi';
export const useAccessOverviewApi = () => {
const { loading, makeRequest, createRequest, errors } = useAPI({
propagateErrors: true,
});
const downloadCSV = async () => {
const requestId = 'downloadCSV';
const req = createRequest(
'api/admin/access/overview',
{
method: 'GET',
responseType: 'blob',
headers: { Accept: 'text/csv' },
},
requestId
);
const file = await (await makeRequest(req.caller, req.id)).blob();
const url = window.URL.createObjectURL(file);
window.location.assign(url);
};
return {
downloadCSV,
errors,
loading,
};
};

View File

@ -66,6 +66,7 @@ export type UiFlags = {
doraMetrics?: boolean;
variantTypeNumber?: boolean;
privateProjects?: boolean;
accessOverview?: boolean;
[key: string]: boolean | Variant | undefined;
};

View File

@ -71,6 +71,7 @@ exports[`should create default config 1`] = `
"isEnabled": [Function],
},
"flags": {
"accessOverview": false,
"anonymiseEventLog": false,
"caseInsensitiveInOperators": false,
"customRootRolesKillSwitch": false,
@ -108,6 +109,7 @@ exports[`should create default config 1`] = `
},
"flagResolver": FlagResolver {
"experiments": {
"accessOverview": false,
"anonymiseEventLog": false,
"caseInsensitiveInOperators": false,
"customRootRolesKillSwitch": false,

View File

@ -29,6 +29,7 @@ export type IFlagKey =
| 'featureNamingPattern'
| 'doraMetrics'
| 'variantTypeNumber'
| 'accessOverview'
| 'privateProjects';
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
@ -137,6 +138,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_PRIVATE_PROJECTS,
false,
),
accessOverview: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_ACCESS_OVERVIEW,
false,
),
};
export const defaultExperimentalOptions: IExperimentalOptions = {

View File

@ -44,6 +44,7 @@ process.nextTick(async () => {
doraMetrics: true,
variantTypeNumber: true,
privateProjects: true,
accessOverview: true,
},
},
authentication: {