Merge branch 'features/api-token-v2' into api-token-v2/regenerate-an-api-token

This commit is contained in:
Simone Taeggi 2022-08-29 11:31:38 +02:00
commit c641c5d71d
9 changed files with 125 additions and 38 deletions

View File

@ -119,7 +119,15 @@ const ApiTokenCreateView = () => {
toggleNotification({ toggleNotification({
type: 'success', type: 'success',
message: formatMessage({ id: 'notification.success.saved', defaultMessage: 'Saved' }), message: isCreating
? formatMessage({
id: 'notification.success.tokencreated',
defaultMessage: 'API Token successfully created',
})
: formatMessage({
id: 'notification.success.tokenedited',
defaultMessage: 'API Token successfully edited',
}),
}); });
trackUsageRef.current(isCreating ? 'didCreateToken' : 'didEditToken', { trackUsageRef.current(isCreating ? 'didCreateToken' : 'didEditToken', {

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { useIntl } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
@ -13,13 +12,11 @@ import {
pxToRem, pxToRem,
useTracking, useTracking,
} from '@strapi/helper-plugin'; } from '@strapi/helper-plugin';
import DeleteButton from './DeleteButton'; import DeleteButton from './DeleteButton';
import UpdateButton from './UpdateButton'; import UpdateButton from './UpdateButton';
import ReadButton from './ReadButton'; import ReadButton from './ReadButton';
const TableRows = ({ canDelete, canUpdate, canRead, onClickDelete, withBulkActions, rows }) => { const TableRows = ({ canDelete, canUpdate, canRead, onClickDelete, withBulkActions, rows }) => {
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams(); const [{ query }] = useQueryParams();
const [, sortOrder] = query.sort.split(':'); const [, sortOrder] = query.sort.split(':');
const { const {
@ -60,16 +57,15 @@ const TableRows = ({ canDelete, canUpdate, canRead, onClickDelete, withBulkActio
</Td> </Td>
<Td> <Td>
<Typography textColor="neutral800"> <Typography textColor="neutral800">
{formatMessage({ <RelativeTime timestamp={new Date(apiToken.createdAt)} />
id: `Settings.apiTokens.types.${apiToken.type}`,
defaultMessage: 'Type unknown',
})}
</Typography> </Typography>
</Td> </Td>
<Td> <Td>
<Typography textColor="neutral800"> {apiToken.lastUsedAt && (
<RelativeTime timestamp={new Date(apiToken.createdAt)} /> <Typography textColor="neutral800">
</Typography> <RelativeTime timestamp={new Date(apiToken.lastUsedAt)} />
</Typography>
)}
</Td> </Td>
{withBulkActions && ( {withBulkActions && (

View File

@ -806,10 +806,10 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
<span <span
aria-labelledby="tooltip-5" aria-labelledby="tooltip-5"
class="c23" class="c23"
label="Token type" label="Created at"
tabindex="-1" tabindex="-1"
> >
Token type Created at
</span> </span>
</span> </span>
<span <span
@ -828,10 +828,10 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
<span <span
aria-labelledby="tooltip-7" aria-labelledby="tooltip-7"
class="c23" class="c23"
label="Created at" label="Last used"
tabindex="-1" tabindex="-1"
> >
Created at Last used
</span> </span>
</span> </span>
<span <span
@ -893,17 +893,6 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
aria-colindex="3" aria-colindex="3"
class="c21" class="c21"
tabindex="-1" tabindex="-1"
>
<span
class="c33"
>
Type unknown
</span>
</td>
<td
aria-colindex="4"
class="c21"
tabindex="-1"
> >
<span <span
class="c33" class="c33"
@ -916,6 +905,11 @@ describe('ADMIN | Pages | API TOKENS | ListPage', () => {
</time> </time>
</span> </span>
</td> </td>
<td
aria-colindex="4"
class="c21"
tabindex="-1"
/>
<td <td
aria-colindex="5" aria-colindex="5"
class="c21" class="c21"

View File

@ -21,17 +21,6 @@ const tableHeaders = [
sortable: false, sortable: false,
}, },
}, },
{
name: 'type',
key: 'type',
metadatas: {
label: {
id: 'Settings.apiTokens.ListView.headers.type',
defaultMessage: 'Token type',
},
sortable: false,
},
},
{ {
name: 'createdAt', name: 'createdAt',
key: 'createdAt', key: 'createdAt',
@ -43,6 +32,17 @@ const tableHeaders = [
sortable: false, sortable: false,
}, },
}, },
{
name: 'lastUsedAt',
key: 'lastUsedAt',
metadatas: {
label: {
id: 'Settings.apiTokens.ListView.headers.lastUsedAt',
defaultMessage: 'Last used',
},
sortable: false,
},
},
]; ];
export default tableHeaders; export default tableHeaders;

View File

@ -76,7 +76,7 @@ const permissions = {
update: [{ action: 'admin::webhooks.update', subject: null }], update: [{ action: 'admin::webhooks.update', subject: null }],
}, },
'api-tokens': { 'api-tokens': {
main: [], main: [{ action: 'admin::api-tokens.access', subject: null }],
create: [{ action: 'admin::api-tokens.create', subject: null }], create: [{ action: 'admin::api-tokens.create', subject: null }],
delete: [{ action: 'admin::api-tokens.delete', subject: null }], delete: [{ action: 'admin::api-tokens.delete', subject: null }],
read: [{ action: 'admin::api-tokens.read', subject: null }], read: [{ action: 'admin::api-tokens.read', subject: null }],

View File

@ -91,6 +91,7 @@
"Settings.apiTokens.ListView.headers.description": "Description", "Settings.apiTokens.ListView.headers.description": "Description",
"Settings.apiTokens.ListView.headers.type": "Token type", "Settings.apiTokens.ListView.headers.type": "Token type",
"Settings.apiTokens.ListView.headers.createdAt": "Created at", "Settings.apiTokens.ListView.headers.createdAt": "Created at",
"Settings.apiTokens.ListView.headers.lastUsedAt": "Last used",
"Settings.apiTokens.notification.copied": "Token copied to clipboard.", "Settings.apiTokens.notification.copied": "Token copied to clipboard.",
"Settings.apiTokens.title": "API Tokens", "Settings.apiTokens.title": "API Tokens",
"Settings.apiTokens.types.full-access": "Full access", "Settings.apiTokens.types.full-access": "Full access",
@ -768,6 +769,8 @@
"notification.success.delete": "The item has been deleted", "notification.success.delete": "The item has been deleted",
"notification.success.saved": "Saved", "notification.success.saved": "Saved",
"notification.success.title": "Success:", "notification.success.title": "Success:",
"notification.success.tokencreated": "API Token successfully created",
"notification.success.tokenedited": "API Token successfully edited",
"notification.version.update.message": "A new version of Strapi is available!", "notification.version.update.message": "A new version of Strapi is available!",
"notification.warning.title": "Warning:", "notification.warning.title": "Warning:",
"notification.warning.404": "404 - Not found", "notification.warning.404": "404 - Not found",

View File

@ -118,12 +118,21 @@ module.exports = {
category: 'users and roles', category: 'users and roles',
subCategory: 'roles', subCategory: 'roles',
}, },
{
uid: 'api-tokens.access',
displayName: 'Access the API tokens settings page',
pluginName: 'admin',
section: 'settings',
category: 'api tokens',
subCategory: 'api Tokens',
},
{ {
uid: 'api-tokens.create', uid: 'api-tokens.create',
displayName: 'Create (generate)', displayName: 'Create (generate)',
pluginName: 'admin', pluginName: 'admin',
section: 'settings', section: 'settings',
category: 'api tokens', category: 'api tokens',
subCategory: 'general',
}, },
{ {
uid: 'api-tokens.read', uid: 'api-tokens.read',
@ -131,6 +140,7 @@ module.exports = {
pluginName: 'admin', pluginName: 'admin',
section: 'settings', section: 'settings',
category: 'api tokens', category: 'api tokens',
subCategory: 'general',
}, },
{ {
uid: 'api-tokens.update', uid: 'api-tokens.update',
@ -138,6 +148,7 @@ module.exports = {
pluginName: 'admin', pluginName: 'admin',
section: 'settings', section: 'settings',
category: 'api tokens', category: 'api tokens',
subCategory: 'general',
}, },
{ {
uid: 'api-tokens.delete', uid: 'api-tokens.delete',
@ -145,6 +156,7 @@ module.exports = {
pluginName: 'admin', pluginName: 'admin',
section: 'settings', section: 'settings',
category: 'api tokens', category: 'api tokens',
subCategory: 'general',
}, },
{ {
uid: 'project-settings.update', uid: 'project-settings.update',

View File

@ -258,6 +258,62 @@ describe('API Token Controller', () => {
}); });
}); });
describe('Regenerate an API token', () => {
const token = {
id: 1,
name: 'api-token_tests-regenerate',
description: 'api-token_tests-description',
type: 'read-only',
};
test('Regenerates an API token successfully', async () => {
const regenerate = jest.fn().mockResolvedValue(token);
const getById = jest.fn().mockResolvedValue(token);
const created = jest.fn();
const ctx = createContext({ params: { id: token.id } }, { created });
global.strapi = {
admin: {
services: {
'api-token': {
regenerate,
getById,
},
},
},
};
await apiTokenController.regenerate(ctx);
expect(regenerate).toHaveBeenCalledWith(token.id);
});
test('Fails if token not found', async () => {
const regenerate = jest.fn().mockResolvedValue(token);
const getById = jest.fn().mockResolvedValue(null);
const created = jest.fn();
const notFound = jest.fn();
const ctx = createContext({ params: { id: token.id } }, { created, notFound });
global.strapi = {
admin: {
services: {
'api-token': {
regenerate,
getById,
},
},
},
};
await apiTokenController.regenerate(ctx);
expect(regenerate).not.toHaveBeenCalled();
expect(getById).toHaveBeenCalledWith(token.id);
expect(notFound).toHaveBeenCalledWith('API Token not found');
});
});
describe('Retrieve an API token', () => { describe('Retrieve an API token', () => {
const token = { const token = {
id: 1, id: 1,

View File

@ -293,6 +293,12 @@ describe('Role CRUD End to End', () => {
}, },
], ],
"settings": Array [ "settings": Array [
Object {
"action": "admin::api-tokens.access",
"category": "api tokens",
"displayName": "Access the API tokens settings page",
"subCategory": "api Tokens",
},
Object { Object {
"action": "admin::api-tokens.create", "action": "admin::api-tokens.create",
"category": "api tokens", "category": "api tokens",
@ -784,6 +790,12 @@ describe('Role CRUD End to End', () => {
}, },
], ],
"settings": Array [ "settings": Array [
Object {
"action": "admin::api-tokens.access",
"category": "api tokens",
"displayName": "Access the API tokens settings page",
"subCategory": "api Tokens",
},
Object { Object {
"action": "admin::api-tokens.create", "action": "admin::api-tokens.create",
"category": "api tokens", "category": "api tokens",
@ -1210,6 +1222,12 @@ describe('Role CRUD End to End', () => {
}, },
], ],
"settings": Array [ "settings": Array [
Object {
"action": "admin::api-tokens.access",
"category": "api tokens",
"displayName": "Access the API tokens settings page",
"subCategory": "api Tokens",
},
Object { Object {
"action": "admin::api-tokens.create", "action": "admin::api-tokens.create",
"category": "api tokens", "category": "api tokens",