1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/src/lib/services/event-service.ts
Gastón Fournier ceaaf3d0f3
feat: admin token calls get an admin token user (#5924)
## About the changes
Whenever we get a call from an admin token we want to associate it with
the [admin token
user](4d42093a07/src/lib/types/core.ts (L34-L41)).
This should give us the needed audit for this type of calls that
currently were lacking a user id (we only stored a string with the token
name in the event log).

We consciously decided not to use `id` as the property to prevent any
unforeseen side effects. The reason is that only `IUser` type has an id
and adding an id to `IApiUser` might lead to confusion.
2024-01-17 16:55:59 +01:00

142 lines
4.5 KiB
TypeScript

import { IUnleashConfig } from '../types/option';
import { IFeatureTagStore, IUnleashStores } from '../types/stores';
import { Logger } from '../logger';
import { IEventStore } from '../types/stores/event-store';
import { IBaseEvent, IEventList, IUserEvent } from '../types/events';
import { SearchEventsSchema } from '../openapi/spec/search-events-schema';
import EventEmitter from 'events';
import { ADMIN_TOKEN_USER, IApiUser, ITag, IUser, SYSTEM_USER } from '../types';
import { ApiTokenType } from '../../lib/types/models/api-token';
export default class EventService {
private logger: Logger;
private eventStore: IEventStore;
private featureTagStore: IFeatureTagStore;
constructor(
{
eventStore,
featureTagStore,
}: Pick<IUnleashStores, 'eventStore' | 'featureTagStore'>,
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
) {
this.logger = getLogger('services/event-service.ts');
this.eventStore = eventStore;
this.featureTagStore = featureTagStore;
}
async getEvents(): Promise<IEventList> {
const totalEvents = await this.eventStore.count();
const events = await this.eventStore.getEvents();
return {
events,
totalEvents,
};
}
async searchEvents(search: SearchEventsSchema): Promise<IEventList> {
const totalEvents = await this.eventStore.filteredCount(search);
const events = await this.eventStore.searchEvents(search);
return {
events,
totalEvents,
};
}
async onEvent(
eventName: string | symbol,
listener: (...args: any[]) => void,
): Promise<EventEmitter> {
return this.eventStore.on(eventName, listener);
}
private async enhanceEventsWithTags(
events: IBaseEvent[],
): Promise<IBaseEvent[]> {
const featureNamesSet = new Set<string>();
for (const event of events) {
if (event.featureName && !event.tags) {
featureNamesSet.add(event.featureName);
}
}
const featureTagsMap: Map<string, ITag[]> = new Map();
const allTagsInFeatures = await this.featureTagStore.getAllByFeatures(
Array.from(featureNamesSet),
);
for (const tag of allTagsInFeatures) {
const featureTags = featureTagsMap.get(tag.featureName) || [];
featureTags.push({ value: tag.tagValue, type: tag.tagType });
featureTagsMap.set(tag.featureName, featureTags);
}
for (const event of events) {
if (event.featureName && !event.tags) {
event.tags = featureTagsMap.get(event.featureName);
}
}
return events;
}
isAdminToken(user: IUser | IApiUser): boolean {
return (user as IApiUser)?.type === ApiTokenType.ADMIN;
}
getUserDetails(user: IUser | IApiUser): {
createdBy: string;
createdByUserId: number;
} {
return {
createdBy:
(user as IUser)?.email ||
user?.username ||
(this.isAdminToken(user)
? ADMIN_TOKEN_USER.username
: SYSTEM_USER.username),
createdByUserId:
(user as IUser)?.id ||
(user as IApiUser)?.internalAdminTokenUserId ||
SYSTEM_USER.id,
};
}
/**
* @deprecated use storeUserEvent instead
*/
async storeEvent(event: IBaseEvent): Promise<void> {
return this.storeEvents([event]);
}
/**
* @deprecated use storeUserEvents instead
*/
async storeEvents(events: IBaseEvent[]): Promise<void> {
let enhancedEvents = events;
for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) {
enhancedEvents = await enhancer(enhancedEvents);
}
return this.eventStore.batchStore(enhancedEvents);
}
async storeUserEvent(event: IUserEvent): Promise<void> {
return this.storeUserEvents([event]);
}
async storeUserEvents(events: IUserEvent[]): Promise<void> {
let enhancedEvents = events.map(({ byUser, ...event }) => {
return {
...event,
...this.getUserDetails(byUser),
};
});
for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) {
enhancedEvents = await enhancer(enhancedEvents);
}
return this.eventStore.batchStore(enhancedEvents);
}
}