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({
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', {

View File

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

View File

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

View File

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

View File

@ -76,7 +76,7 @@ const permissions = {
update: [{ action: 'admin::webhooks.update', subject: null }],
},
'api-tokens': {
main: [],
main: [{ action: 'admin::api-tokens.access', subject: null }],
create: [{ action: 'admin::api-tokens.create', subject: null }],
delete: [{ action: 'admin::api-tokens.delete', 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.type": "Token type",
"Settings.apiTokens.ListView.headers.createdAt": "Created at",
"Settings.apiTokens.ListView.headers.lastUsedAt": "Last used",
"Settings.apiTokens.notification.copied": "Token copied to clipboard.",
"Settings.apiTokens.title": "API Tokens",
"Settings.apiTokens.types.full-access": "Full access",
@ -768,6 +769,8 @@
"notification.success.delete": "The item has been deleted",
"notification.success.saved": "Saved",
"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.warning.title": "Warning:",
"notification.warning.404": "404 - Not found",

View File

@ -118,12 +118,21 @@ module.exports = {
category: 'users and 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',
displayName: 'Create (generate)',
pluginName: 'admin',
section: 'settings',
category: 'api tokens',
subCategory: 'general',
},
{
uid: 'api-tokens.read',
@ -131,6 +140,7 @@ module.exports = {
pluginName: 'admin',
section: 'settings',
category: 'api tokens',
subCategory: 'general',
},
{
uid: 'api-tokens.update',
@ -138,6 +148,7 @@ module.exports = {
pluginName: 'admin',
section: 'settings',
category: 'api tokens',
subCategory: 'general',
},
{
uid: 'api-tokens.delete',
@ -145,6 +156,7 @@ module.exports = {
pluginName: 'admin',
section: 'settings',
category: 'api tokens',
subCategory: 'general',
},
{
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', () => {
const token = {
id: 1,

View File

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