mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: add create api token screen (NEW PR) (#600)
* feat: add create api token screen * fix: update headers * fix: remove old api create Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com>
This commit is contained in:
parent
3a41de2246
commit
b209368c84
@ -1,224 +0,0 @@
|
||||
import { TextField } from '@material-ui/core';
|
||||
import classNames from 'classnames';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { styles as commonStyles } from '../../../common';
|
||||
import { IApiTokenCreate } from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
import useEnvironments from '../../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProjects from '../../../../hooks/api/getters/useProjects/useProjects';
|
||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import ConditionallyRender from '../../../common/ConditionallyRender';
|
||||
import Dialogue from '../../../common/Dialogue';
|
||||
import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect';
|
||||
|
||||
import { useStyles } from './styles';
|
||||
|
||||
const ALL = '*';
|
||||
const TYPE_ADMIN = 'ADMIN';
|
||||
const TYPE_CLIENT = 'CLIENT';
|
||||
|
||||
interface IApiTokenCreateProps {
|
||||
showDialog: boolean;
|
||||
closeDialog: () => void;
|
||||
createToken: (token: IApiTokenCreate) => Promise<void>;
|
||||
}
|
||||
|
||||
interface IDataError {
|
||||
username?: string;
|
||||
general?: string;
|
||||
}
|
||||
|
||||
const INITIAL_DATA: IApiTokenCreate = {
|
||||
username: '',
|
||||
type: TYPE_CLIENT,
|
||||
project: ALL,
|
||||
};
|
||||
|
||||
const ApiTokenCreate = ({
|
||||
showDialog,
|
||||
closeDialog,
|
||||
createToken,
|
||||
}: IApiTokenCreateProps) => {
|
||||
const styles = useStyles();
|
||||
const [data, setData] = useState(INITIAL_DATA);
|
||||
const [error, setError] = useState<IDataError>({});
|
||||
const { projects } = useProjects();
|
||||
const { environments } = useEnvironments();
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
environments &&
|
||||
environments.length > 0 &&
|
||||
data.type === TYPE_CLIENT &&
|
||||
!data.environment
|
||||
) {
|
||||
setData({ ...data, environment: environments[0].name });
|
||||
}
|
||||
}, [data, environments]);
|
||||
|
||||
const clear = () => {
|
||||
const environment =
|
||||
environments && environments.length > 0
|
||||
? environments[0].name
|
||||
: undefined;
|
||||
setData({ ...INITIAL_DATA, environment });
|
||||
setError({});
|
||||
};
|
||||
|
||||
const onCancel = (e: Event) => {
|
||||
clear();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
const isValid = () => {
|
||||
if (!data.username) {
|
||||
setError({ username: 'Username is required.' });
|
||||
return false;
|
||||
} else {
|
||||
setError({});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await createToken(data);
|
||||
clear();
|
||||
closeDialog();
|
||||
} catch (error) {
|
||||
setError({ general: 'Unable to create new API token' });
|
||||
}
|
||||
};
|
||||
|
||||
const setType = (event: React.ChangeEvent<{ value: string }>) => {
|
||||
const value = event.target.value;
|
||||
if (value === TYPE_ADMIN) {
|
||||
setData({ ...data, type: value, environment: ALL, project: ALL });
|
||||
} else {
|
||||
setData({
|
||||
...data,
|
||||
type: value,
|
||||
environment: environments[0].name,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const setUsername = (event: React.ChangeEvent<{ value: string }>) => {
|
||||
const value = event.target.value;
|
||||
setData({ ...data, username: value });
|
||||
};
|
||||
|
||||
const setProject = (event: React.ChangeEvent<{ value: string }>) => {
|
||||
const value = event.target.value;
|
||||
setData({ ...data, project: value });
|
||||
};
|
||||
|
||||
const setEnvironment = (event: React.ChangeEvent<{ value: string }>) => {
|
||||
const value = event.target.value;
|
||||
setData({ ...data, environment: value });
|
||||
};
|
||||
|
||||
const selectableProjects = [{ id: '*', name: 'ALL' }, ...projects].map(
|
||||
i => ({
|
||||
key: i.id,
|
||||
label: i.name,
|
||||
title: i.name,
|
||||
})
|
||||
);
|
||||
|
||||
const selectableEnvs =
|
||||
data.type === TYPE_ADMIN
|
||||
? [{ key: '*', label: 'ALL' }]
|
||||
: environments.map(i => ({
|
||||
key: i.name,
|
||||
label: i.name,
|
||||
title: i.name,
|
||||
}));
|
||||
|
||||
const selectableTypes = [
|
||||
{ key: 'CLIENT', label: 'Client', title: 'Client SDK token' },
|
||||
{ key: 'ADMIN', label: 'Admin', title: 'Admin API token' },
|
||||
];
|
||||
|
||||
const formId = 'create-api-token-form';
|
||||
|
||||
return (
|
||||
<Dialogue
|
||||
onClick={() => submit()}
|
||||
open={showDialog}
|
||||
onClose={onCancel}
|
||||
primaryButtonText="Create"
|
||||
secondaryButtonText="Cancel"
|
||||
title="New API token"
|
||||
formId={formId}
|
||||
>
|
||||
<form
|
||||
id={formId}
|
||||
onSubmit={submit}
|
||||
className={classNames(
|
||||
styles.addApiKeyForm,
|
||||
commonStyles.contentSpacing
|
||||
)}
|
||||
>
|
||||
<TextField
|
||||
autoFocus
|
||||
value={data.username}
|
||||
name="username"
|
||||
onChange={setUsername}
|
||||
onBlur={isValid}
|
||||
label="Username"
|
||||
style={{ width: '200px' }}
|
||||
error={error.username !== undefined}
|
||||
helperText={error.username}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
required
|
||||
/>
|
||||
<GeneralSelect
|
||||
disabled={false}
|
||||
options={selectableTypes}
|
||||
value={data.type}
|
||||
onChange={setType}
|
||||
label="Token Type"
|
||||
id="api_key_type"
|
||||
name="type"
|
||||
className={undefined}
|
||||
classes={undefined}
|
||||
/>
|
||||
<GeneralSelect
|
||||
disabled={data.type === TYPE_ADMIN}
|
||||
options={selectableProjects}
|
||||
value={data.project}
|
||||
onChange={setProject}
|
||||
label="Project"
|
||||
id="api_key_project"
|
||||
name="project"
|
||||
className={undefined}
|
||||
classes={undefined}
|
||||
/>
|
||||
<ConditionallyRender condition={uiConfig?.flags.E} show={
|
||||
<>
|
||||
<GeneralSelect
|
||||
disabled={data.type === TYPE_ADMIN}
|
||||
options={selectableEnvs}
|
||||
value={data.environment}
|
||||
required
|
||||
onChange={setEnvironment}
|
||||
label="Environment"
|
||||
id="api_key_environment"
|
||||
name="environment"
|
||||
className={undefined}
|
||||
classes={undefined}
|
||||
/>
|
||||
</>
|
||||
} />
|
||||
</form>
|
||||
</Dialogue>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiTokenCreate;
|
@ -1,8 +0,0 @@
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
|
||||
export const useStyles = makeStyles({
|
||||
addApiKeyForm: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
});
|
@ -0,0 +1,53 @@
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
export const useStyles = makeStyles(theme => ({
|
||||
container: {
|
||||
maxWidth: '400px',
|
||||
},
|
||||
form: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
input: { width: '100%', marginBottom: '1rem' },
|
||||
selectInput: {
|
||||
marginBottom: '1rem',
|
||||
minWidth: '400px',
|
||||
[theme.breakpoints.down(600)]: {
|
||||
minWidth: '379px',
|
||||
},
|
||||
},
|
||||
label: {
|
||||
minWidth: '300px',
|
||||
[theme.breakpoints.down(600)]: {
|
||||
minWidth: 'auto',
|
||||
},
|
||||
},
|
||||
buttonContainer: {
|
||||
marginTop: 'auto',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
cancelButton: {
|
||||
marginRight: '1.5rem',
|
||||
},
|
||||
inputDescription: {
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
formHeader: {
|
||||
fontWeight: 'normal',
|
||||
marginTop: '0',
|
||||
},
|
||||
header: {
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
permissionErrorContainer: {
|
||||
position: 'relative',
|
||||
},
|
||||
errorMessage: {
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
color: theme.palette.error.main,
|
||||
position: 'absolute',
|
||||
top: '-8px',
|
||||
},
|
||||
}));
|
@ -0,0 +1,142 @@
|
||||
import { Button } from '@material-ui/core';
|
||||
import { KeyboardArrowDownOutlined } from '@material-ui/icons';
|
||||
import React from 'react';
|
||||
import useEnvironments from '../../../../hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProjects from '../../../../hooks/api/getters/useProjects/useProjects';
|
||||
import GeneralSelect from '../../../common/GeneralSelect/GeneralSelect';
|
||||
import Input from '../../../common/Input/Input';
|
||||
import PermissionButton from '../../../common/PermissionButton/PermissionButton';
|
||||
import { ADMIN } from '../../../providers/AccessProvider/permissions';
|
||||
import { useStyles } from './ApiTokenForm.styles';
|
||||
|
||||
interface IApiTokenFormProps {
|
||||
username: string;
|
||||
type: string;
|
||||
project: string;
|
||||
environment: string;
|
||||
setTokenType: (value: string) => void;
|
||||
setUsername: React.Dispatch<React.SetStateAction<string>>;
|
||||
setProject: React.Dispatch<React.SetStateAction<string>>;
|
||||
setEnvironment: React.Dispatch<React.SetStateAction<string>>;
|
||||
handleSubmit: (e: any) => void;
|
||||
handleCancel: () => void;
|
||||
errors: { [key: string]: string };
|
||||
submitButtonText: string;
|
||||
clearErrors: () => void;
|
||||
}
|
||||
const ApiTokenForm = ({
|
||||
username,
|
||||
type,
|
||||
project,
|
||||
environment,
|
||||
setUsername,
|
||||
setTokenType,
|
||||
setProject,
|
||||
setEnvironment,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
errors,
|
||||
clearErrors,
|
||||
submitButtonText,
|
||||
}: IApiTokenFormProps) => {
|
||||
const TYPE_ADMIN = 'ADMIN';
|
||||
const styles = useStyles();
|
||||
const { environments } = useEnvironments();
|
||||
const { projects } = useProjects();
|
||||
|
||||
const selectableTypes = [
|
||||
{ key: 'CLIENT', label: 'Client', title: 'Client SDK token' },
|
||||
{ key: 'ADMIN', label: 'Admin', title: 'Admin API token' },
|
||||
];
|
||||
|
||||
const selectableProjects = [{ id: '*', name: 'ALL' }, ...projects].map(
|
||||
i => ({
|
||||
key: i.id,
|
||||
label: i.name,
|
||||
title: i.name,
|
||||
})
|
||||
);
|
||||
const selectableEnvs =
|
||||
type === TYPE_ADMIN
|
||||
? [{ key: '*', label: 'ALL' }]
|
||||
: environments.map(i => ({
|
||||
key: i.name,
|
||||
label: i.name,
|
||||
title: i.name,
|
||||
}));
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className={styles.form}>
|
||||
<h3 className={styles.formHeader}>Token information</h3>
|
||||
|
||||
<div className={styles.container}>
|
||||
<p className={styles.inputDescription}>
|
||||
Who are you generating the token for?
|
||||
</p>
|
||||
<Input
|
||||
className={styles.input}
|
||||
value={username}
|
||||
name="username"
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
label="Username"
|
||||
error={errors.username !== undefined}
|
||||
errorText={errors.username}
|
||||
onFocus={() => clearErrors()}
|
||||
/>
|
||||
<p className={styles.inputDescription}>
|
||||
What is your token type?
|
||||
</p>
|
||||
<GeneralSelect
|
||||
options={selectableTypes}
|
||||
value={type}
|
||||
onChange={e => setTokenType(e.target.value as string)}
|
||||
label="Token Type"
|
||||
id="api_key_type"
|
||||
name="type"
|
||||
IconComponent={KeyboardArrowDownOutlined}
|
||||
className={styles.selectInput}
|
||||
/>
|
||||
<p className={styles.inputDescription}>
|
||||
Which project do you want to give access to?
|
||||
</p>
|
||||
<GeneralSelect
|
||||
disabled={type === TYPE_ADMIN}
|
||||
value={project}
|
||||
options={selectableProjects}
|
||||
onChange={e => setProject(e.target.value as string)}
|
||||
label="Project"
|
||||
IconComponent={KeyboardArrowDownOutlined}
|
||||
className={styles.selectInput}
|
||||
/>
|
||||
<p className={styles.inputDescription}>
|
||||
Which environment should the token have access to?
|
||||
</p>
|
||||
<GeneralSelect
|
||||
disabled={type === TYPE_ADMIN}
|
||||
options={selectableEnvs}
|
||||
value={environment}
|
||||
onChange={e => setEnvironment(e.target.value as string)}
|
||||
label="Environment"
|
||||
id="api_key_environment"
|
||||
name="environment"
|
||||
IconComponent={KeyboardArrowDownOutlined}
|
||||
className={styles.selectInput}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.buttonContainer}>
|
||||
<Button onClick={handleCancel} className={styles.cancelButton}>
|
||||
Cancel
|
||||
</Button>
|
||||
<PermissionButton
|
||||
onClick={handleSubmit}
|
||||
permission={ADMIN}
|
||||
type="submit"
|
||||
>
|
||||
{submitButtonText} token
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiTokenForm;
|
@ -1,5 +1,5 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
@ -14,9 +14,7 @@ import useToast from '../../../../hooks/useToast';
|
||||
import useLoading from '../../../../hooks/useLoading';
|
||||
import useApiTokens from '../../../../hooks/api/getters/useApiTokens/useApiTokens';
|
||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useApiTokensApi, {
|
||||
IApiTokenCreate,
|
||||
} from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
import useApiTokensApi from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
import ApiError from '../../../common/ApiError/ApiError';
|
||||
import PageContent from '../../../common/PageContent';
|
||||
import HeaderTitle from '../../../common/HeaderTitle';
|
||||
@ -29,7 +27,6 @@ import { useStyles } from './ApiTokenList.styles';
|
||||
import { formatDateWithLocale } from '../../../common/util';
|
||||
import Secret from './secret';
|
||||
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';
|
||||
@ -54,20 +51,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const [showDelete, setShowDelete] = useState(false);
|
||||
const [delToken, setDeleteToken] = useState<IApiToken>();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { setToastData } = useToast();
|
||||
const { tokens, loading, refetch, error } = useApiTokens();
|
||||
const { deleteToken, createToken } = useApiTokensApi();
|
||||
const { deleteToken } = useApiTokensApi();
|
||||
const ref = useLoading(loading);
|
||||
|
||||
const [showDialog, setDialog] = useState(false);
|
||||
|
||||
const openDialog = () => {
|
||||
setDialog(true);
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
setDialog(false);
|
||||
};
|
||||
const history = useHistory();
|
||||
|
||||
const renderError = () => {
|
||||
return (
|
||||
@ -79,26 +67,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
|
||||
);
|
||||
};
|
||||
|
||||
const onCreateToken = async (token: IApiTokenCreate) => {
|
||||
try {
|
||||
await createToken(token);
|
||||
refetch();
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Created token',
|
||||
text: 'Successfully created API token',
|
||||
confetti: true,
|
||||
});
|
||||
} catch (e) {
|
||||
setToastApiError(e.message);
|
||||
}
|
||||
};
|
||||
const copyToken = (value: string) => {
|
||||
if (copy(value)) {
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Token copied',
|
||||
confetti: true,
|
||||
text: `Token is copied to clipboard`,
|
||||
});
|
||||
}
|
||||
@ -271,7 +244,11 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={openDialog}
|
||||
onClick={() =>
|
||||
history.push(
|
||||
'/admin/api/create-token'
|
||||
)
|
||||
}
|
||||
data-test={CREATE_API_TOKEN_BUTTON}
|
||||
>
|
||||
Create API token
|
||||
@ -312,11 +289,6 @@ const ApiTokenList = ({ location }: IApiTokenList) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ApiTokenCreate
|
||||
showDialog={showDialog}
|
||||
createToken={onCreateToken}
|
||||
closeDialog={closeDialog}
|
||||
/>
|
||||
<Dialogue
|
||||
open={showDelete}
|
||||
onClick={onDeleteToken}
|
||||
|
@ -0,0 +1,90 @@
|
||||
import FormTemplate from '../../../common/FormTemplate/FormTemplate';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import ApiTokenForm from '../ApiTokenForm/ApiTokenForm';
|
||||
import useApiTokenForm from '../hooks/useApiTokenForm';
|
||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from '../../../../hooks/useToast';
|
||||
import useApiTokensApi from '../../../../hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
|
||||
const CreateApiToken = () => {
|
||||
/* @ts-ignore */
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
const {
|
||||
getApiTokenPayload,
|
||||
username,
|
||||
type,
|
||||
project,
|
||||
environment,
|
||||
setUsername,
|
||||
setTokenType,
|
||||
setProject,
|
||||
setEnvironment,
|
||||
isValid,
|
||||
errors,
|
||||
clearErrors,
|
||||
} = useApiTokenForm();
|
||||
|
||||
const { createToken, loading } = useApiTokensApi();
|
||||
|
||||
const handleSubmit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!isValid()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await createToken(getApiTokenPayload());
|
||||
history.push('/admin/api');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Created token',
|
||||
text: 'Successfully created API token',
|
||||
confetti: true,
|
||||
});
|
||||
} catch (e: any) {
|
||||
setToastApiError(e.toString());
|
||||
}
|
||||
};
|
||||
|
||||
const formatApiCode = () => {
|
||||
return `curl --location --request POST '${
|
||||
uiConfig.unleashUrl
|
||||
}/api/admin/api-tokens' \\
|
||||
--header 'Authorization: INSERT_API_KEY' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '${JSON.stringify(getApiTokenPayload(), undefined, 2)}'`;
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<FormTemplate
|
||||
loading={loading}
|
||||
title="Create Api Token"
|
||||
description="In order to connect to Unleash clients will need an API token to grant access. A client SDK will need to token with 'client privileges', which allows them to fetch feature toggle configuration and post usage metrics back."
|
||||
documentationLink="https://docs.getunleash.io/user_guide/api-token"
|
||||
formatApiCode={formatApiCode}
|
||||
>
|
||||
<ApiTokenForm
|
||||
username={username}
|
||||
type={type}
|
||||
project={project}
|
||||
environment={environment}
|
||||
setEnvironment={setEnvironment}
|
||||
setTokenType={setTokenType}
|
||||
setUsername={setUsername}
|
||||
setProject={setProject}
|
||||
errors={errors}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
submitButtonText="Create"
|
||||
clearErrors={clearErrors}
|
||||
/>
|
||||
</FormTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateApiToken;
|
@ -0,0 +1,86 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const useApiToken = (
|
||||
initialUserName = '',
|
||||
initialtype = 'CLIENT',
|
||||
initialProject = '*',
|
||||
initialEnvironment = 'default'
|
||||
) => {
|
||||
const [username, setUsername] = useState(initialUserName);
|
||||
const [type, setType] = useState(initialtype);
|
||||
const [project, setProject] = useState(initialtype);
|
||||
const [environment, setEnvironment] = useState(initialEnvironment);
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
setUsername(initialUserName);
|
||||
}, [initialUserName]);
|
||||
|
||||
useEffect(() => {
|
||||
setType(initialtype);
|
||||
if (type === 'ADMIN') {
|
||||
setProject('*');
|
||||
setEnvironment('*')
|
||||
}
|
||||
//eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initialtype]);
|
||||
|
||||
useEffect(() => {
|
||||
setProject(initialProject);
|
||||
}, [initialProject]);
|
||||
|
||||
useEffect(() => {
|
||||
setEnvironment(initialEnvironment);
|
||||
}, [initialEnvironment]);
|
||||
|
||||
const setTokenType = (value: string) => {
|
||||
if (value === 'ADMIN') {
|
||||
setType(value)
|
||||
setProject('*');
|
||||
setEnvironment('*');
|
||||
} else {
|
||||
setType(value)
|
||||
setEnvironment(initialEnvironment);
|
||||
}
|
||||
};
|
||||
|
||||
const getApiTokenPayload = () => {
|
||||
return {
|
||||
username: username,
|
||||
type: type,
|
||||
project: project,
|
||||
environment: environment,
|
||||
};
|
||||
};
|
||||
|
||||
const isValid = () => {
|
||||
if (!username) {
|
||||
setErrors({ username: 'Username is required.' });
|
||||
return false;
|
||||
} else {
|
||||
setErrors({});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const clearErrors = () => {
|
||||
setErrors({});
|
||||
};
|
||||
|
||||
return {
|
||||
username,
|
||||
type,
|
||||
project,
|
||||
environment,
|
||||
setUsername,
|
||||
setTokenType,
|
||||
setProject,
|
||||
setEnvironment,
|
||||
getApiTokenPayload,
|
||||
isValid,
|
||||
clearErrors,
|
||||
errors,
|
||||
};
|
||||
};
|
||||
|
||||
export default useApiToken;
|
@ -14,6 +14,7 @@ const FeatureProjectSelect = ({
|
||||
value,
|
||||
onChange,
|
||||
filter,
|
||||
...rest
|
||||
}: IFeatureProjectSelect) => {
|
||||
const { projects } = useProjects();
|
||||
|
||||
@ -50,6 +51,7 @@ const FeatureProjectSelect = ({
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -330,6 +330,15 @@ Array [
|
||||
"title": "Archived Toggles",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"menu": Object {},
|
||||
"parent": "/admin",
|
||||
"path": "/admin/api/create-token",
|
||||
"title": "API access",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"flag": "RE",
|
||||
|
@ -43,6 +43,7 @@ import FeatureCreate from '../feature/FeatureCreate/FeatureCreate';
|
||||
import ProjectRoles from '../admin/project-roles/ProjectRoles/ProjectRoles';
|
||||
import CreateProjectRole from '../admin/project-roles/CreateProjectRole/CreateProjectRole';
|
||||
import EditProjectRole from '../admin/project-roles/EditProjectRole/EditProjectRole';
|
||||
import CreateApiToken from '../admin/api-token/CreateApiToken/CreateApiToken';
|
||||
|
||||
export const routes = [
|
||||
// Project
|
||||
@ -378,6 +379,15 @@ export const routes = [
|
||||
},
|
||||
|
||||
// Admin
|
||||
{
|
||||
path: '/admin/api/create-token',
|
||||
parent: '/admin',
|
||||
title: 'API access',
|
||||
component: CreateApiToken,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
menu: {},
|
||||
},
|
||||
{
|
||||
path: '/admin/create-project-role',
|
||||
title: 'Create',
|
||||
|
@ -8,7 +8,7 @@ export interface IApiTokenCreate {
|
||||
}
|
||||
|
||||
const useApiTokensApi = () => {
|
||||
const { makeRequest, createRequest, errors } = useAPI({
|
||||
const { makeRequest, createRequest, errors, loading } = useAPI({
|
||||
propagateErrors: true,
|
||||
});
|
||||
|
||||
@ -38,7 +38,7 @@ const useApiTokensApi = () => {
|
||||
}
|
||||
};
|
||||
|
||||
return { deleteToken, createToken, errors };
|
||||
return { deleteToken, createToken, errors, loading };
|
||||
};
|
||||
|
||||
export default useApiTokensApi;
|
||||
|
Loading…
Reference in New Issue
Block a user