1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

Merge pull request #398 from Unleash/feat/API-token-improvement

add info box in project page + make api ui responsive
This commit is contained in:
Youssef Khedher 2021-10-09 21:39:41 +01:00 committed by GitHub
commit ed41919596
5 changed files with 138 additions and 65 deletions

View File

@ -12,16 +12,36 @@ export const useStyles = makeStyles(theme => ({
maxWidth: '400px',
marginBottom: '1rem',
},
cardLink: {
color: 'inherit',
textDecoration: 'none',
border: 'none',
padding: '0',
background: 'transparent',
fontFamily: theme.typography.fontFamily,
pointer: 'cursor',
},
center: {
textAlign: 'center'
},
actionsContainer: {
textAlign: 'center',
display: 'flex-inline',
flexWrap: 'nowrap'
},
infoBoxContainer:{
marginBottom:40
},
hideSM:{
[theme.breakpoints.down('sm')]: {
display: 'none'
}
},
hideMD:{
[theme.breakpoints.down('md')]: {
display: 'none'
}
},
hideXS:{
[theme.breakpoints.down('xs')]: {
display: 'none'
}
},
token:{
textAlign: 'left',
[theme.breakpoints.up('sm')]: {
display: 'none'
}
}
}));

View File

@ -1,6 +1,6 @@
import { useContext, useState } from 'react';
import { Link } from 'react-router-dom';
import { Button, IconButton, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core';
import { Button, IconButton, Table, TableBody, TableCell, TableHead, TableRow, } from '@material-ui/core';
import AccessContext from '../../../contexts/AccessContext';
import useToast from '../../../hooks/useToast';
import useLoading from '../../../hooks/useLoading';
@ -15,9 +15,11 @@ import { CREATE_API_TOKEN, DELETE_API_TOKEN } from '../../AccessProvider/permiss
import { useStyles } from './ApiTokenList.styles';
import { formatDateWithLocale } from '../../common/util';
import Secret from './secret';
import { Delete } from '@material-ui/icons';
import { Delete, FileCopy } from '@material-ui/icons';
import ApiTokenCreate from '../ApiTokenCreate/ApiTokenCreate';
import Dialogue from '../../common/Dialogue';
import {CREATE_API_TOKEN_BUTTON} from '../../../testIds'
import { Alert } from '@material-ui/lab';
interface IApiToken {
createdAt: Date;
@ -73,6 +75,14 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
text: 'Successfully created API token.',
});
}
const copyToken = (value: string) => {
navigator.clipboard.writeText(value);
setToastData({
type: 'success',
show: true,
text: `Token is copied to clipboard`,
});
};
const onDeleteToken = async () => {
if(delToken) {
@ -101,65 +111,86 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
<Table size="small">
<TableHead>
<TableRow>
<TableCell>Created</TableCell>
<TableCell>Username</TableCell>
<TableCell className={styles.center}>Type</TableCell>
<TableCell className={styles.hideSM}>Created</TableCell>
<TableCell className={styles.hideSM}>Username</TableCell>
<TableCell className={`${styles.center} ${styles.hideXS}`}>Type</TableCell>
<ConditionallyRender condition={uiConfig.flags.E} show={<>
<TableCell className={styles.center}>Project</TableCell>
<TableCell className={styles.center}>Environment</TableCell>
<TableCell className={`${styles.center} ${styles.hideXS}`}>Project</TableCell>
<TableCell className={`${styles.center} ${styles.hideXS}`}>Environment</TableCell>
</>} />
<TableCell>Secret</TableCell>
<TableCell align="right">Action</TableCell>
<TableCell className={styles.hideMD}>Secret</TableCell>
<TableCell className={styles.token}>Token</TableCell>
<TableCell className={styles.actionsContainer}>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tokens.map(item => {
return (
<TableRow key={item.secret}>
<TableCell align="left">
<TableCell align="left" className={styles.hideSM}>
{formatDateWithLocale(
item.createdAt,
location.locale
)}
</TableCell>
<TableCell align="left">
<TableCell align="left" className={styles.hideSM}>
{item.username}
</TableCell>
<TableCell className={styles.center}>
<TableCell className={`${styles.center} ${styles.hideXS}`}>
{item.type}
</TableCell>
<ConditionallyRender condition={uiConfig.flags.E} show={<>
<TableCell className={styles.center}>
<TableCell className={`${styles.center} ${styles.hideXS}`}>
{renderProject(item.project)}
</TableCell>
<TableCell className={styles.center}>
<TableCell className={`${styles.center} ${styles.hideXS}`}>
{item.environment}
</TableCell>
</>} />
<TableCell>
<TableCell className={styles.token}>
<b>Type:</b> {item.type}<br/>
<b>Env:</b> {item.environment}<br/>
<b>Project:</b> {renderProject(item.project)}
</TableCell>
</>}
elseShow={<>
<TableCell className={styles.token}>
<b>Type:</b> {item.type}<br/>
<b>Username:</b> {item.username}
</TableCell>
</>}
/>
<TableCell className={styles.hideMD}>
<Secret value={item.secret} />
</TableCell>
<ConditionallyRender
condition={hasAccess(DELETE_API_TOKEN)}
show={<TableCell
width="20"
style={{ textAlign: 'right' }}
>
<IconButton
onClick={() => {
setDeleteToken(item);
setShowDelete(true);
} }
<TableCell
className={styles.actionsContainer}
>
<IconButton
onClick={() => {
copyToken(item.secret)
} }
>
<Delete />
</IconButton>
</TableCell>} />
<FileCopy />
</IconButton>
<ConditionallyRender
condition={hasAccess(DELETE_API_TOKEN)}
show={
<IconButton
onClick={() => {
setDeleteToken(item);
setShowDelete(true);
} }
>
<Delete />
</IconButton>
} />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>)
</Table>
)
}
return (
@ -169,8 +200,27 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
title="API Access"
actions={<ConditionallyRender
condition={hasAccess(CREATE_API_TOKEN)}
show={<Button variant="contained" color="primary" onClick={openDialog}>Create API token</Button>} />} />}
show={<Button variant="contained" color="primary" onClick={openDialog} data-test={CREATE_API_TOKEN_BUTTON}>Create API token</Button>} />} />}
>
<Alert severity="info" className={styles.infoBoxContainer}>
<p>
Read the{' '}
<a
href="https://docs.getunleash.io/docs"
target="_blank"
rel="noreferrer"
>
Getting started guide
</a>{' '}
to learn how to connect to the Unleash API from your
application or programmatically. Please note it can take
up to 1 minute before a new API key is activated.
</p>
<br />
<strong>API URL: </strong>{' '}
<pre style={{ display: 'inline' }}>{uiConfig.unleashUrl}/api/</pre>
</Alert>
<ConditionallyRender condition={error} show={renderError()} />
<div className={styles.container}>
<ConditionallyRender

View File

@ -1,29 +1,9 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { IconButton } from '@material-ui/core';
import { Visibility } from '@material-ui/icons';
function Secret({ value }) {
const [show, setShow] = useState(false);
const toggle = evt => {
evt.preventDefault();
setShow(!show);
};
return (
<div>
{show ? (
<input readOnly value={value} style={{ width: '250px' }} />
) : (
<span style={{ width: '250px', display: 'inline-block' }}>************************************</span>
)}
<IconButton
aria-label="Show token"
onClick={toggle}
title="Show token"
>
<Visibility style={{ marginLeft: '5px', fontSize: '1.2em' }} />
</IconButton>
<span style={{ width: '250px', display: 'inline-block' }}>************************************</span>
</div>
);
}

View File

@ -16,6 +16,9 @@ import useProject from '../../../hooks/api/getters/useProject/useProject';
import { FormControlLabel, FormGroup, Switch } from '@material-ui/core';
import useProjectApi from '../../../hooks/api/actions/useProjectApi/useProjectApi';
import EnvironmentDisableConfirm from './EnvironmentDisableConfirm/EnvironmentDisableConfirm';
import { Link } from 'react-router-dom';
import { Alert } from '@material-ui/lab';
export interface ProjectEnvironment {
name: string;
@ -50,6 +53,7 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => {
const ref = useLoading(loading);
const styles = useStyles();
const refetch = () => {
refetchEnvs();
refetchProject();
@ -166,9 +170,26 @@ const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => {
headerContent={
<HeaderTitle
title={`Configure environments for "${project?.name}"`}
/>
}
/>}
>
<Alert severity="info">
<b>Important!</b> In order for your application to retrieve feature toggle activation strategies for a specific environment, the application<br/> must use an environment-specific API key. You can look up the environment-specific API keys {' '}
<Link
to='/admin/api'
>
here.
</Link>{' '}
<br/>
<br/>
Your administrator can configure an environment-specific API key and add it to your SDK if you can't find it on the list.
If you are an administrator you can create a new API key {' '}
<Link
to='/admin/api'
>
here.
</Link>{' '}
</Alert>
<br/>
<ConditionallyRender condition={error} show={renderError()} />
<div className={styles.container}>
<ConditionallyRender

View File

@ -2,6 +2,8 @@ export const REPORTING_SELECT_ID = 'REPORTING_SELECT_ID';
/* NAVIGATION */
export const NAVIGATE_TO_CREATE_FEATURE = 'NAVIGATE_TO_CREATE_FEATURE';
export const NAVIGATE_TO_CREATE_API_KEY = 'NAVIGATE_TO_CREATE_API_KEY';
export const CREATE_API_TOKEN_BUTTON = 'CREATE_API_TOKEN_BUTTON';
/* CREATE FEATURE */
export const CF_NAME_ID = 'CF_NAME_ID';