mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
https://linear.app/unleash/issue/2-1403/consider-refactoring-the-way-tags-are-fetched-for-the-events This adds 2 methods to `EventService`: - `storeEvent`; - `storeEvents`; This allows us to run event-specific logic inside these methods. In the case of this PR, this means fetching the feature tags in case the event contains a `featureName` and there are no tags specified in the event. This prevents us from having to remember to fetch the tags in order to store feature-related events except for very specific cases, like the deletion of a feature - You can't fetch tags for a feature that no longer exists, so in that case we need to pre-fetch the tags before deleting the feature. This also allows us to do any event-specific post-processing to the event before reaching the DB layer. In general I think it's also nicer that we reference the event service instead of the event store directly. There's a lot of changes and a lot of files touched, but most of it is boilerplate to inject the `eventService` where needed instead of using the `eventStore` directly. Hopefully this will be a better approach than https://github.com/Unleash/unleash/pull/4729 --------- Co-authored-by: Gastón Fournier <gaston@getunleash.io>
103 lines
3.0 KiB
TypeScript
103 lines
3.0 KiB
TypeScript
import { IUnleashConfig, IUnleashStores } from '../types';
|
|
import { Logger } from '../logger';
|
|
import { IPatStore } from '../types/stores/pat-store';
|
|
import { PAT_CREATED, PAT_DELETED } from '../types/events';
|
|
import { IPat } from '../types/models/pat';
|
|
import crypto from 'crypto';
|
|
import User from '../types/user';
|
|
import BadDataError from '../error/bad-data-error';
|
|
import NameExistsError from '../error/name-exists-error';
|
|
import { OperationDeniedError } from '../error/operation-denied-error';
|
|
import { PAT_LIMIT } from '../util/constants';
|
|
import EventService from './event-service';
|
|
|
|
export default class PatService {
|
|
private config: IUnleashConfig;
|
|
|
|
private logger: Logger;
|
|
|
|
private patStore: IPatStore;
|
|
|
|
private eventService: EventService;
|
|
|
|
constructor(
|
|
{ patStore }: Pick<IUnleashStores, 'patStore'>,
|
|
config: IUnleashConfig,
|
|
eventService: EventService,
|
|
) {
|
|
this.config = config;
|
|
this.logger = config.getLogger('services/pat-service.ts');
|
|
this.patStore = patStore;
|
|
this.eventService = eventService;
|
|
}
|
|
|
|
async createPat(pat: IPat, forUserId: number, editor: User): Promise<IPat> {
|
|
await this.validatePat(pat, forUserId);
|
|
pat.secret = this.generateSecretKey();
|
|
pat.userId = forUserId;
|
|
const newPat = await this.patStore.create(pat);
|
|
|
|
pat.secret = '***';
|
|
await this.eventService.storeEvent({
|
|
type: PAT_CREATED,
|
|
createdBy: editor.email || editor.username,
|
|
data: pat,
|
|
});
|
|
|
|
return newPat;
|
|
}
|
|
|
|
async getAll(userId: number): Promise<IPat[]> {
|
|
return this.patStore.getAllByUser(userId);
|
|
}
|
|
|
|
async deletePat(
|
|
id: number,
|
|
forUserId: number,
|
|
editor: User,
|
|
): Promise<void> {
|
|
const pat = await this.patStore.get(id);
|
|
|
|
pat.secret = '***';
|
|
await this.eventService.storeEvent({
|
|
type: PAT_DELETED,
|
|
createdBy: editor.email || editor.username,
|
|
data: pat,
|
|
});
|
|
|
|
return this.patStore.deleteForUser(id, forUserId);
|
|
}
|
|
|
|
async validatePat(
|
|
{ description, expiresAt }: IPat,
|
|
userId: number,
|
|
): Promise<void> {
|
|
if (!description) {
|
|
throw new BadDataError('PAT description cannot be empty');
|
|
}
|
|
|
|
if (new Date(expiresAt) < new Date()) {
|
|
throw new BadDataError('The expiry date should be in future.');
|
|
}
|
|
|
|
if ((await this.patStore.countByUser(userId)) >= PAT_LIMIT) {
|
|
throw new OperationDeniedError(
|
|
`Too many PATs (${PAT_LIMIT}) already exist for this user.`,
|
|
);
|
|
}
|
|
|
|
if (
|
|
await this.patStore.existsWithDescriptionByUser(description, userId)
|
|
) {
|
|
throw new NameExistsError('PAT description already exists');
|
|
}
|
|
}
|
|
|
|
private generateSecretKey() {
|
|
const randomStr = crypto.randomBytes(28).toString('hex');
|
|
return `user:${randomStr}`;
|
|
}
|
|
}
|
|
|
|
module.exports = PatService;
|