1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-31 00:16:47 +01:00
unleash.unleash/src/lib/routes/admin-api/event.ts
Nuno Góis b013d4286c
fix: log project access events (#3683)
https://linear.app/unleash/issue/2-992/register-event-when-granting-access-to-a-project

 - Correctly registers a new `project-access-added` event;
- Adds an anonymization method that supports anonymizing objects,
recursively searching for keys to anonymize;
- Fixes the event label used by `ProjectUserUpdateRoleEvent` to be
`PROJECT_USER_ROLE_CHANGED`;


![image](https://user-images.githubusercontent.com/14320932/236216227-bf6e5dff-f509-48a4-ba7c-9f37e23e87c0.png)
2023-05-09 11:13:38 +02:00

185 lines
6.0 KiB
TypeScript

import { Request, Response } from 'express';
import { IUnleashConfig } from '../../types/option';
import { IUnleashServices } from '../../types/services';
import EventService from '../../services/event-service';
import { ADMIN, NONE } from '../../types/permissions';
import { IEvent, IEventList } from '../../types/events';
import Controller from '../controller';
import { anonymiseKeys } from '../../util/anonymise';
import { OpenApiService } from '../../services/openapi-service';
import { createResponseSchema } from '../../openapi/util/create-response-schema';
import { endpointDescriptions } from '../../openapi/endpoint-descriptions';
import {
eventsSchema,
EventsSchema,
} from '../../../lib/openapi/spec/events-schema';
import { serializeDates } from '../../../lib/types/serialize-dates';
import {
featureEventsSchema,
FeatureEventsSchema,
} from '../../../lib/openapi/spec/feature-events-schema';
import { getStandardResponses } from '../../../lib/openapi/util/standard-responses';
import { createRequestSchema } from '../../openapi/util/create-request-schema';
import { SearchEventsSchema } from '../../openapi/spec/search-events-schema';
import { IFlagResolver } from '../../types/experimental';
const ANON_KEYS = ['email', 'username', 'createdBy'];
const version = 1;
export default class EventController extends Controller {
private eventService: EventService;
private flagResolver: IFlagResolver;
private openApiService: OpenApiService;
constructor(
config: IUnleashConfig,
{
eventService,
openApiService,
}: Pick<IUnleashServices, 'eventService' | 'openApiService'>,
) {
super(config);
this.eventService = eventService;
this.flagResolver = config.flagResolver;
this.openApiService = openApiService;
this.route({
method: 'get',
path: '',
handler: this.getEvents,
permission: ADMIN,
middleware: [
openApiService.validPath({
operationId: 'getEvents',
tags: ['Events'],
responses: {
...getStandardResponses(401),
200: createResponseSchema('eventsSchema'),
},
parameters: [
{
name: 'project',
description:
'The name of the project whose events you want to retrieve',
schema: { type: 'string' },
in: 'query',
},
],
...endpointDescriptions.admin.events,
}),
],
});
this.route({
method: 'get',
path: '/:featureName',
handler: this.getEventsForToggle,
permission: NONE,
middleware: [
openApiService.validPath({
operationId: 'getEventsForToggle',
tags: ['Events'],
responses: {
...getStandardResponses(401),
200: createResponseSchema('featureEventsSchema'),
},
...endpointDescriptions.admin.eventsPerFeature,
}),
],
});
this.route({
method: 'post',
path: '/search',
handler: this.searchEvents,
permission: NONE,
middleware: [
openApiService.validPath({
operationId: 'searchEvents',
tags: ['Events'],
requestBody: createRequestSchema('searchEventsSchema'),
responses: { 200: createResponseSchema('eventsSchema') },
}),
],
});
}
maybeAnonymiseEvents(events: IEvent[]): IEvent[] {
if (this.flagResolver.isEnabled('anonymiseEventLog')) {
return anonymiseKeys(events, ANON_KEYS);
}
return events;
}
async getEvents(
req: Request<any, any, any, { project?: string }>,
res: Response<EventsSchema>,
): Promise<void> {
const { project } = req.query;
let eventList: IEventList;
if (project) {
eventList = await this.eventService.searchEvents({ project });
} else {
eventList = await this.eventService.getEvents();
}
const response: EventsSchema = {
version,
events: serializeDates(this.maybeAnonymiseEvents(eventList.events)),
totalEvents: eventList.totalEvents,
};
this.openApiService.respondWithValidation(
200,
res,
eventsSchema.$id,
response,
);
}
async getEventsForToggle(
req: Request<{ featureName: string }>,
res: Response<FeatureEventsSchema>,
): Promise<void> {
const feature = req.params.featureName;
const eventList = await this.eventService.searchEvents({ feature });
const response = {
version,
toggleName: feature,
events: serializeDates(this.maybeAnonymiseEvents(eventList.events)),
totalEvents: eventList.totalEvents,
};
this.openApiService.respondWithValidation(
200,
res,
featureEventsSchema.$id,
response,
);
}
async searchEvents(
req: Request<unknown, unknown, SearchEventsSchema>,
res: Response<EventsSchema>,
): Promise<void> {
const eventList = await this.eventService.searchEvents(req.body);
const response = {
version,
events: serializeDates(this.maybeAnonymiseEvents(eventList.events)),
totalEvents: eventList.totalEvents,
};
this.openApiService.respondWithValidation(
200,
res,
featureEventsSchema.$id,
response,
);
}
}