mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
fix: serialize API token data correctly in instance stats (#7953)
Turns out we've been trying to return API token data in instance stats for a while, but that the serialization has failed. Serializing a JS map just yields an empty object. This PR fixes that serialization and also adds API tokens to the instance stats schema (it wasn't before, but we did return it). Adding it to the schema is also part of making resource usage visible as part of the soft limits project.
This commit is contained in:
parent
341703978a
commit
e5cca661d9
@ -220,6 +220,30 @@ export const instanceAdminStatsSchema = {
|
||||
example: 0,
|
||||
minimum: 0,
|
||||
},
|
||||
apiTokens: {
|
||||
type: 'object',
|
||||
description: 'The number of API tokens in Unleash, split by type',
|
||||
properties: {
|
||||
admin: {
|
||||
type: 'number',
|
||||
description: 'The number of admin tokens.',
|
||||
minimum: 0,
|
||||
example: 5,
|
||||
},
|
||||
client: {
|
||||
type: 'number',
|
||||
description: 'The number of client tokens.',
|
||||
minimum: 0,
|
||||
example: 5,
|
||||
},
|
||||
frontend: {
|
||||
type: 'number',
|
||||
description: 'The number of frontend tokens.',
|
||||
minimum: 0,
|
||||
example: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
sum: {
|
||||
type: 'string',
|
||||
description:
|
||||
|
@ -5,7 +5,6 @@ import type { IUnleashServices } from '../../types/services';
|
||||
import type { IUnleashConfig } from '../../types/option';
|
||||
import Controller from '../controller';
|
||||
import { NONE } from '../../types/permissions';
|
||||
import type { UiConfigSchema } from '../../openapi/spec/ui-config-schema';
|
||||
import type {
|
||||
InstanceStatsService,
|
||||
InstanceStatsSigned,
|
||||
@ -15,6 +14,8 @@ import {
|
||||
createCsvResponseSchema,
|
||||
createResponseSchema,
|
||||
} from '../../openapi/util/create-response-schema';
|
||||
import type { InstanceAdminStatsSchema } from '../../openapi';
|
||||
import { serializeDates } from '../../types';
|
||||
|
||||
class InstanceAdminController extends Controller {
|
||||
private instanceStatsService: InstanceStatsService;
|
||||
@ -128,17 +129,29 @@ class InstanceAdminController extends Controller {
|
||||
};
|
||||
}
|
||||
|
||||
private serializeStats(
|
||||
instanceStats: InstanceStatsSigned,
|
||||
): InstanceAdminStatsSchema {
|
||||
const apiTokensObj = Object.fromEntries(
|
||||
instanceStats.apiTokens.entries(),
|
||||
);
|
||||
return serializeDates({
|
||||
...instanceStats,
|
||||
apiTokens: apiTokensObj,
|
||||
});
|
||||
}
|
||||
|
||||
async getStatistics(
|
||||
req: AuthedRequest,
|
||||
res: Response<InstanceStatsSigned>,
|
||||
_: AuthedRequest,
|
||||
res: Response<InstanceAdminStatsSchema>,
|
||||
): Promise<void> {
|
||||
const instanceStats = await this.instanceStatsService.getSignedStats();
|
||||
res.json(instanceStats);
|
||||
res.json(this.serializeStats(instanceStats));
|
||||
}
|
||||
|
||||
async getStatisticsCSV(
|
||||
req: AuthedRequest,
|
||||
res: Response<UiConfigSchema>,
|
||||
_: AuthedRequest,
|
||||
res: Response<InstanceAdminStatsSchema>,
|
||||
): Promise<void> {
|
||||
const instanceStats = await this.instanceStatsService.getSignedStats();
|
||||
const fileName = `unleash-${
|
||||
@ -146,7 +159,7 @@ class InstanceAdminController extends Controller {
|
||||
}-${Date.now()}.csv`;
|
||||
|
||||
const json2csvParser = new Parser();
|
||||
const csv = json2csvParser.parse(instanceStats);
|
||||
const csv = json2csvParser.parse(this.serializeStats(instanceStats));
|
||||
|
||||
res.contentType('csv');
|
||||
res.attachment(fileName);
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
} from '../../helpers/test-helper';
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
import type { IUnleashStores } from '../../../../lib/types';
|
||||
import { ApiTokenType } from '../../../../lib/types/models/api-token';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
@ -47,6 +48,43 @@ test('should return instance statistics', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('api tokens are serialized correctly', async () => {
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
tokenName: 'admin',
|
||||
type: ApiTokenType.ADMIN,
|
||||
environment: '*',
|
||||
projects: ['*'],
|
||||
});
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
tokenName: 'frontend',
|
||||
type: ApiTokenType.FRONTEND,
|
||||
environment: 'default',
|
||||
projects: ['*'],
|
||||
});
|
||||
await app.services.apiTokenService.createApiTokenWithProjects({
|
||||
tokenName: 'client',
|
||||
type: ApiTokenType.CLIENT,
|
||||
environment: 'default',
|
||||
projects: ['*'],
|
||||
});
|
||||
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/instance-admin/statistics')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
apiTokens: { client: 1, admin: 1, frontend: 1 },
|
||||
});
|
||||
|
||||
const { text: csv } = await app.request
|
||||
.get('/api/admin/instance-admin/statistics/csv')
|
||||
.expect('Content-Type', /text\/csv/)
|
||||
.expect(200);
|
||||
|
||||
expect(csv).toMatch(/{""client"":1,""admin"":1,""frontend"":1}/);
|
||||
});
|
||||
|
||||
test('should return instance statistics with correct number of projects', async () => {
|
||||
await stores.projectStore.create({
|
||||
id: 'test',
|
||||
@ -77,7 +115,7 @@ test('should return signed instance statistics', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should return instance statistics as CVS', async () => {
|
||||
test('should return instance statistics as CSV', async () => {
|
||||
await stores.featureToggleStore.create('default', {
|
||||
name: 'TestStats2',
|
||||
createdByUserId: 9999,
|
||||
|
Loading…
Reference in New Issue
Block a user