From aa7f5be1b9b664688c5f7e07e28f6676fd0c0e35 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Tue, 12 Dec 2023 11:31:11 +0100 Subject: [PATCH] checkpoint for adding createdByUserId to all events --- src/lib/addons/datadog.test.ts | 7 + .../addons/feature-event-formatter-md.test.ts | 17 ++ src/lib/addons/slack-app.test.ts | 7 +- src/lib/addons/slack.test.ts | 9 + src/lib/addons/teams.test.ts | 6 + src/lib/addons/webhook.test.ts | 6 +- src/lib/db/event-store.ts | 2 + .../dependent-features-service.ts | 11 + .../export-import-controller.ts | 6 +- .../export-import-service.ts | 6 + .../export-import.e2e.test.ts | 2 +- .../archive-feature-toggle-controller.ts | 3 +- .../feature-toggle-controller.ts | 1 + .../feature-toggle/feature-toggle-service.ts | 82 +++++- .../tests/feature-toggle-service.e2e.test.ts | 4 + src/lib/services/project-service.ts | 2 +- src/lib/services/user-service.ts | 3 + src/lib/types/events.ts | 239 ++++++++++++++---- 18 files changed, 355 insertions(+), 58 deletions(-) diff --git a/src/lib/addons/datadog.test.ts b/src/lib/addons/datadog.test.ts index 2d55e76ce94c..2f57c263bb96 100644 --- a/src/lib/addons/datadog.test.ts +++ b/src/lib/addons/datadog.test.ts @@ -45,6 +45,7 @@ test('Should call datadog webhook', async () => { createdAt: new Date(), type: FEATURE_CREATED, createdBy: 'some@user.com', + createdByUserId: -1337, featureName: 'some-toggle', data: { name: 'some-toggle', @@ -74,6 +75,7 @@ test('Should call datadog webhook for archived toggle', async () => { createdAt: new Date(), type: FEATURE_ARCHIVED, createdBy: 'some@user.com', + createdByUserId: -1337, featureName: 'some-toggle', data: { name: 'some-toggle', @@ -102,6 +104,7 @@ test('Should call datadog webhook for archived toggle with project info', async type: FEATURE_ARCHIVED, createdBy: 'some@user.com', featureName: 'some-toggle', + createdByUserId: -1337, project: 'some-project', data: { name: 'some-toggle', @@ -129,6 +132,7 @@ test('Should call datadog webhook for toggled environment', async () => { createdAt: new Date(), type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', + createdByUserId: -1337, environment: 'development', project: 'default', featureName: 'some-toggle', @@ -160,6 +164,7 @@ test('Should include customHeaders in headers when calling service', async () => type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', environment: 'development', + createdByUserId: -1337, project: 'default', featureName: 'some-toggle', data: { @@ -190,6 +195,7 @@ test('Should not include source_type_name when included in the config', async () createdAt: new Date(), type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', + createdByUserId: -1337, environment: 'development', project: 'default', featureName: 'some-toggle', @@ -224,6 +230,7 @@ test('Should call datadog webhook with JSON when template set', async () => { createdAt: new Date(), type: FEATURE_CREATED, createdBy: 'some@user.com', + createdByUserId: -1337, featureName: 'some-toggle', data: { name: 'some-toggle', diff --git a/src/lib/addons/feature-event-formatter-md.test.ts b/src/lib/addons/feature-event-formatter-md.test.ts index f64eaf2b641c..5f2aad1e5b2b 100644 --- a/src/lib/addons/feature-event-formatter-md.test.ts +++ b/src/lib/addons/feature-event-formatter-md.test.ts @@ -6,6 +6,7 @@ import { FEATURE_STRATEGY_REMOVE, FEATURE_STRATEGY_UPDATE, IEvent, + SYSTEM_USER_ID, } from '../types'; import { FeatureEventFormatterMd } from './feature-event-formatter-md'; @@ -34,6 +35,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -67,6 +69,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -100,6 +103,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -133,6 +137,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -174,6 +179,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -207,6 +213,7 @@ const testCases: [string, IEvent][] = [ id: 919, type: FEATURE_STRATEGY_ADD, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:08.290Z'), data: { id: '3f4bf713-696c-43a4-8ce7-d6c607108858', @@ -231,6 +238,7 @@ const testCases: [string, IEvent][] = [ id: 918, type: FEATURE_STRATEGY_REMOVE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:00.229Z'), data: null, preData: { @@ -253,6 +261,7 @@ const testCases: [string, IEvent][] = [ id: 39, type: FEATURE_STRATEGY_UPDATE, createdBy: 'admin', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2023-02-20T20:23:28.791Z'), data: { id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa', @@ -310,6 +319,7 @@ const testCases: [string, IEvent][] = [ type: FEATURE_STRATEGY_UPDATE, createdBy: 'admin', createdAt: new Date('2023-02-20T20:23:28.791Z'), + createdByUserId: SYSTEM_USER_ID, data: { id: 'f2d34aac-52ec-49d2-82d3-08d710e89eaa', name: 'default', @@ -346,6 +356,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { name: 'userWithId', @@ -385,6 +396,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { name: 'remoteAddress', @@ -421,6 +433,7 @@ const testCases: [string, IEvent][] = [ type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', createdAt: new Date('2022-06-01T10:03:11.549Z'), + createdByUserId: SYSTEM_USER_ID, data: { name: 'applicationHostname', constraints: [ @@ -456,6 +469,7 @@ const testCases: [string, IEvent][] = [ type: FEATURE_STRATEGY_UPDATE, createdBy: 'user@company.com', createdAt: new Date('2022-06-01T10:03:11.549Z'), + createdByUserId: SYSTEM_USER_ID, data: { name: 'newStrategy', constraints: [ @@ -491,6 +505,7 @@ const testCases: [string, IEvent][] = [ type: CHANGE_REQUEST_SCHEDULED, createdBy: 'user@company.com', createdAt: new Date('2022-06-01T10:03:11.549Z'), + createdByUserId: SYSTEM_USER_ID, data: { changeRequestId: 1, }, @@ -508,6 +523,7 @@ const testCases: [string, IEvent][] = [ type: CHANGE_REQUEST_SCHEDULED_APPLICATION_SUCCESS, createdBy: 'user@company.com', createdAt: new Date('2022-06-01T10:03:11.549Z'), + createdByUserId: SYSTEM_USER_ID, data: { changeRequestId: 1, }, @@ -524,6 +540,7 @@ const testCases: [string, IEvent][] = [ id: 920, type: CHANGE_REQUEST_SCHEDULED_APPLICATION_FAILURE, createdBy: 'user@company.com', + createdByUserId: SYSTEM_USER_ID, createdAt: new Date('2022-06-01T10:03:11.549Z'), data: { changeRequestId: 1, diff --git a/src/lib/addons/slack-app.test.ts b/src/lib/addons/slack-app.test.ts index abfaa69a30d3..2994769c0198 100644 --- a/src/lib/addons/slack-app.test.ts +++ b/src/lib/addons/slack-app.test.ts @@ -1,4 +1,8 @@ -import { IEvent, FEATURE_ENVIRONMENT_ENABLED } from '../types/events'; +import { + IEvent, + FEATURE_ENVIRONMENT_ENABLED, + SYSTEM_USER_ID, +} from '../types/events'; import SlackAppAddon from './slack-app'; import { ChatPostMessageArguments, ErrorCode } from '@slack/web-api'; @@ -44,6 +48,7 @@ describe('SlackAppAddon', () => { id: 1, createdAt: new Date(), type: FEATURE_ENVIRONMENT_ENABLED, + createdByUserId: SYSTEM_USER_ID, createdBy: 'some@user.com', project: 'default', featureName: 'some-toggle', diff --git a/src/lib/addons/slack.test.ts b/src/lib/addons/slack.test.ts index 416ffc1d70e7..ef4f86d35081 100644 --- a/src/lib/addons/slack.test.ts +++ b/src/lib/addons/slack.test.ts @@ -3,6 +3,7 @@ import { FEATURE_ARCHIVED, FEATURE_ENVIRONMENT_DISABLED, IEvent, + SYSTEM_USER_ID, } from '../types/events'; import { Logger } from '../logger'; @@ -44,6 +45,7 @@ test('Should call slack webhook', async () => { id: 1, createdAt: new Date(), type: FEATURE_CREATED, + createdByUserId: SYSTEM_USER_ID, createdBy: 'some@user.com', project: 'default', featureName: 'some-toggle', @@ -74,6 +76,7 @@ test('Should call slack webhook for archived toggle', async () => { const event: IEvent = { id: 2, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ARCHIVED, featureName: 'some-toggle', createdBy: 'some@user.com', @@ -101,6 +104,7 @@ test('Should call slack webhook for archived toggle with project info', async () const event: IEvent = { id: 2, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ARCHIVED, featureName: 'some-toggle', project: 'some-project', @@ -129,6 +133,7 @@ test(`Should call webhook for toggled environment`, async () => { const event: IEvent = { id: 2, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', environment: 'development', @@ -159,6 +164,7 @@ test('Should use default channel', async () => { const event: IEvent = { id: 3, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -189,6 +195,7 @@ test('Should override default channel with data from tag', async () => { const event: IEvent = { id: 4, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -225,6 +232,7 @@ test('Should post to all channels in tags', async () => { const event: IEvent = { id: 5, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -269,6 +277,7 @@ test('Should include custom headers from parameters in call to service', async ( id: 2, createdAt: new Date(), type: FEATURE_ENVIRONMENT_DISABLED, + createdByUserId: SYSTEM_USER_ID, createdBy: 'some@user.com', environment: 'development', project: 'default', diff --git a/src/lib/addons/teams.test.ts b/src/lib/addons/teams.test.ts index 61f43f8e0f2d..fd3bacbaef21 100644 --- a/src/lib/addons/teams.test.ts +++ b/src/lib/addons/teams.test.ts @@ -5,6 +5,7 @@ import { FEATURE_CREATED, FEATURE_ENVIRONMENT_DISABLED, IEvent, + SYSTEM_USER_ID, } from '../types/events'; import TeamsAddon from './teams'; @@ -45,6 +46,7 @@ test('Should call teams webhook', async () => { id: 1, createdAt: new Date(), type: FEATURE_CREATED, + createdByUserId: SYSTEM_USER_ID, createdBy: 'some@user.com', featureName: 'some-toggle', data: { @@ -72,6 +74,7 @@ test('Should call teams webhook for archived toggle', async () => { const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ARCHIVED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -98,6 +101,7 @@ test('Should call teams webhook for archived toggle with project info', async () const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ARCHIVED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -125,6 +129,7 @@ test(`Should call teams webhook for toggled environment`, async () => { const event: IEvent = { id: 2, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', environment: 'development', @@ -154,6 +159,7 @@ test('Should include custom headers in call to teams', async () => { const event: IEvent = { id: 2, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_ENVIRONMENT_DISABLED, createdBy: 'some@user.com', environment: 'development', diff --git a/src/lib/addons/webhook.test.ts b/src/lib/addons/webhook.test.ts index 43174b95dfc4..b91adec1ab82 100644 --- a/src/lib/addons/webhook.test.ts +++ b/src/lib/addons/webhook.test.ts @@ -1,6 +1,6 @@ import { Logger } from '../logger'; -import { FEATURE_CREATED, IEvent } from '../types/events'; +import { FEATURE_CREATED, IEvent, SYSTEM_USER_ID } from '../types/events'; import WebhookAddon from './webhook'; @@ -36,6 +36,7 @@ test('Should handle event without "bodyTemplate"', () => { const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -61,6 +62,7 @@ test('Should format event with "bodyTemplate"', () => { const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -90,6 +92,7 @@ test('Should format event with "authorization"', () => { const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', @@ -120,6 +123,7 @@ test('Should handle custom headers', async () => { const event: IEvent = { id: 1, createdAt: new Date(), + createdByUserId: SYSTEM_USER_ID, type: FEATURE_CREATED, createdBy: 'some@user.com', featureName: 'some-toggle', diff --git a/src/lib/db/event-store.ts b/src/lib/db/event-store.ts index 524d016e3294..ef4bb1bb9069 100644 --- a/src/lib/db/event-store.ts +++ b/src/lib/db/event-store.ts @@ -74,6 +74,7 @@ export interface IEventTable { type: string; created_by: string; created_at: Date; + created_by_user_id: number; data?: any; pre_data?: any; feature_name?: string; @@ -364,6 +365,7 @@ class EventStore implements IEventStore { type: row.type as IEventType, createdBy: row.created_by, createdAt: row.created_at, + createdByUserId: row.created_by_user_id, data: row.data, preData: row.pre_data, tags: row.tags || [], diff --git a/src/lib/features/dependent-features/dependent-features-service.ts b/src/lib/features/dependent-features/dependent-features-service.ts index 830b7f1a37db..8ff19bfd9f08 100644 --- a/src/lib/features/dependent-features/dependent-features-service.ts +++ b/src/lib/features/dependent-features/dependent-features-service.ts @@ -50,6 +50,7 @@ export class DependentFeaturesService { projectId, }: { featureName: string; newFeatureName: string; projectId: string }, user: string, + userId: number, ) { const parents = await this.dependentFeaturesReadModel.getParents(featureName); @@ -63,6 +64,7 @@ export class DependentFeaturesService { variants: parent.variants, }, user, + userId, ), ), ); @@ -79,6 +81,7 @@ export class DependentFeaturesService { { child, projectId }, dependentFeature, extractUsernameFromUser(user), + user.id, ); } @@ -86,6 +89,7 @@ export class DependentFeaturesService { { child, projectId }: { child: string; projectId: string }, dependentFeature: CreateDependentFeatureSchema, user: string, + userId: number, ): Promise { const { enabled, feature: parent, variants } = dependentFeature; @@ -146,6 +150,7 @@ export class DependentFeaturesService { project: projectId, featureName: child, createdBy: user, + createdByUserId: userId, data: { feature: parent, enabled: featureDependency.enabled, @@ -165,6 +170,7 @@ export class DependentFeaturesService { dependency, projectId, extractUsernameFromUser(user), + user.id, ); } @@ -172,6 +178,7 @@ export class DependentFeaturesService { dependency: FeatureDependencyId, projectId: string, user: string, + userId: number, ): Promise { await this.dependentFeaturesStore.delete(dependency); await this.eventService.storeEvent({ @@ -179,6 +186,7 @@ export class DependentFeaturesService { project: projectId, featureName: dependency.child, createdBy: user, + createdByUserId: userId, data: { feature: dependency.parent }, }); } @@ -194,6 +202,7 @@ export class DependentFeaturesService { features, projectId, extractUsernameFromUser(user), + user.id, ); } @@ -201,6 +210,7 @@ export class DependentFeaturesService { features: string[], projectId: string, user: string, + userId: number, ): Promise { await this.dependentFeaturesStore.deleteAll(features); await this.eventService.storeEvents( @@ -209,6 +219,7 @@ export class DependentFeaturesService { project: projectId, featureName: feature, createdBy: user, + createdByUserId: userId, })), ); } diff --git a/src/lib/features/export-import-toggles/export-import-controller.ts b/src/lib/features/export-import-toggles/export-import-controller.ts index 1bf097d6e3d2..eef65ef4f654 100644 --- a/src/lib/features/export-import-toggles/export-import-controller.ts +++ b/src/lib/features/export-import-toggles/export-import-controller.ts @@ -120,7 +120,11 @@ class ExportImportController extends Controller { const query = req.body; const userName = extractUsername(req); - const data = await this.exportService.export(query, userName); + const data = await this.exportService.export( + query, + userName, + req.user.id, + ); this.openApiService.respondWithValidation( 200, diff --git a/src/lib/features/export-import-toggles/export-import-service.ts b/src/lib/features/export-import-toggles/export-import-service.ts index 729db717cbe1..a855089059d9 100644 --- a/src/lib/features/export-import-toggles/export-import-service.ts +++ b/src/lib/features/export-import-toggles/export-import-service.ts @@ -67,6 +67,7 @@ export type IExportService = { export( query: ExportQuerySchema, userName: string, + userId: number, ): Promise; }; @@ -287,6 +288,7 @@ export default class ExportImportService environment: cleanedDto.environment, type: FEATURES_IMPORTED, createdBy: extractUsernameFromUser(user), + createdByUserId: user.id, }); } @@ -457,6 +459,7 @@ export default class ExportImportService rest as FeatureToggleDTO, username, feature.name, + user.id, ); } else { await this.featureToggleService.validateName(feature.name); @@ -465,6 +468,7 @@ export default class ExportImportService dto.project, rest as FeatureToggleDTO, username, + user.id, ); } } @@ -777,6 +781,7 @@ export default class ExportImportService async export( query: ExportQuerySchema, userName: string, + userId: number, ): Promise { const featureNames = typeof query.tag === 'string' @@ -901,6 +906,7 @@ export default class ExportImportService await this.eventService.storeEvent({ type: FEATURES_EXPORTED, createdBy: userName, + createdByUserId: userId, data: result, }); diff --git a/src/lib/features/export-import-toggles/export-import.e2e.test.ts b/src/lib/features/export-import-toggles/export-import.e2e.test.ts index 13ec831f0b9b..21abaeed8e22 100644 --- a/src/lib/features/export-import-toggles/export-import.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import.e2e.test.ts @@ -20,7 +20,6 @@ import { import { DEFAULT_ENV } from '../../util'; import { ContextFieldSchema, - CreateDependentFeatureSchema, ImportTogglesSchema, UpsertSegmentSchema, VariantsSchema, @@ -68,6 +67,7 @@ const createToggle = async ( projectId, toggle, username, + -9999, ); if (strategy) { await app.services.featureToggleServiceV2.createStrategy( diff --git a/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts b/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts index ba530f5d5303..8d3312c07d11 100644 --- a/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts +++ b/src/lib/features/feature-toggle/archive-feature-toggle-controller.ts @@ -176,7 +176,7 @@ export default class ArchiveController extends Controller { ): Promise { const { featureName } = req.params; const user = extractUsername(req); - await this.featureService.deleteFeature(featureName, user); + await this.featureService.deleteFeature(featureName, user, req.user.id); res.status(200).end(); } @@ -191,6 +191,7 @@ export default class ArchiveController extends Controller { this.transactionalFeatureToggleService(tx).reviveFeature( featureName, userName, + req.user.id, ), ); res.status(200).end(); diff --git a/src/lib/features/feature-toggle/feature-toggle-controller.ts b/src/lib/features/feature-toggle/feature-toggle-controller.ts index 04fd67fd8f4e..5801ebbb8d9e 100644 --- a/src/lib/features/feature-toggle/feature-toggle-controller.ts +++ b/src/lib/features/feature-toggle/feature-toggle-controller.ts @@ -659,6 +659,7 @@ export default class ProjectFeaturesController extends Controller { projectId, name, userName, + req.user.id, replaceGroupId, ); diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index 7e993bb87761..8f67997d7656 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -44,6 +44,7 @@ import { SKIP_CHANGE_REQUEST, StrategiesOrderChangedEvent, StrategyIds, + SYSTEM_USER_ID, Unsaved, WeightType, } from '../../types'; @@ -403,6 +404,7 @@ class FeatureToggleService { featureName: string, createdBy: string, operations: Operation[], + createdByUserId: number, ): Promise { const featureToggle = await this.getFeatureMetadata(featureName); @@ -421,6 +423,7 @@ class FeatureToggleService { newDocument, createdBy, featureName, + createdByUserId, ); if (featureToggle.stale !== newDocument.stale) { @@ -430,6 +433,7 @@ class FeatureToggleService { project, featureName, createdBy, + createdByUserId, }), ); } @@ -472,6 +476,7 @@ class FeatureToggleService { context, sortOrders, createdBy, + user?.id || SYSTEM_USER_ID, ); } @@ -479,6 +484,7 @@ class FeatureToggleService { context: IFeatureStrategyContext, sortOrders: SetStrategySortOrderSchema, createdBy: string, + createdByUserId: number, ): Promise> { const { featureName, environment, projectId: project } = context; const existingOrder = ( @@ -536,6 +542,7 @@ class FeatureToggleService { createdBy, preData: eventPreData, data: eventData, + createdByUserId, }); await this.eventService.storeEvent(event); } @@ -555,6 +562,7 @@ class FeatureToggleService { strategyConfig, context, createdBy, + user?.id || SYSTEM_USER_ID, ); } @@ -562,6 +570,7 @@ class FeatureToggleService { strategyConfig: Unsaved, context: IFeatureStrategyContext, createdBy: string, + createdByUserId: number, ): Promise> { const { featureName, projectId, environment } = context; await this.validateFeatureBelongsToProject(context); @@ -638,6 +647,7 @@ class FeatureToggleService { createdBy, environment, data: strategy, + createdByUserId, }), ); return strategy; @@ -674,7 +684,13 @@ class FeatureToggleService { context.environment, user, ); - return this.unprotectedUpdateStrategy(id, updates, context, userName); + return this.unprotectedUpdateStrategy( + id, + updates, + context, + userName, + user, + ); } async optionallyDisableFeature( @@ -682,6 +698,7 @@ class FeatureToggleService { environment: string, projectId: string, userName: string, + user?: IUser, ): Promise { const feature = await this.getFeature({ featureName }); @@ -696,6 +713,7 @@ class FeatureToggleService { environment, false, userName, + user, ); } } @@ -705,6 +723,7 @@ class FeatureToggleService { updates: Partial, context: IFeatureStrategyContext, userName: string, + user?: IUser, ): Promise> { const { projectId, environment, featureName } = context; const existingStrategy = await this.featureStrategiesStore.get(id); @@ -760,6 +779,7 @@ class FeatureToggleService { createdBy: userName, data, preData, + createdByUserId: user?.id || SYSTEM_USER_ID, }), ); await this.optionallyDisableFeature( @@ -767,6 +787,7 @@ class FeatureToggleService { environment, projectId, userName, + user, ); return data; } @@ -779,6 +800,7 @@ class FeatureToggleService { value: string | number, context: IFeatureStrategyContext, userName: string, + createdByUserId: number, ): Promise> { const { projectId, environment, featureName } = context; @@ -809,6 +831,7 @@ class FeatureToggleService { createdBy: userName, data, preData, + createdByUserId, }), ); return data; @@ -837,13 +860,14 @@ class FeatureToggleService { context.environment, user, ); - return this.unprotectedDeleteStrategy(id, context, createdBy); + return this.unprotectedDeleteStrategy(id, context, createdBy, user); } async unprotectedDeleteStrategy( id: string, context: IFeatureStrategyContext, createdBy: string, + createdByUser?: IUser, ): Promise { const existingStrategy = await this.featureStrategiesStore.get(id); const { featureName, projectId, environment } = context; @@ -870,6 +894,7 @@ class FeatureToggleService { environment, false, createdBy, + createdByUser, ); } @@ -881,6 +906,7 @@ class FeatureToggleService { project: projectId, environment, createdBy, + createdByUserId: createdByUser?.id || SYSTEM_USER_ID, preData, }), ); @@ -1053,6 +1079,7 @@ class FeatureToggleService { * * Used to retrieve metadata of all feature toggles defined in Unleash. * @param query - Allow you to limit search based on criteria such as project, tags, namePrefix. See @IFeatureToggleQuery + * @param userId - Used to find / mark features as favorite based on users preferences * @param archived - Return archived or active toggles * @returns */ @@ -1106,6 +1133,7 @@ class FeatureToggleService { projectId: string, value: FeatureToggleDTO, createdBy: string, + createdByUserId: number, isValidated: boolean = false, ): Promise { this.logger.info(`${createdBy} creates feature toggle ${value.name}`); @@ -1155,6 +1183,7 @@ class FeatureToggleService { createdBy, project: projectId, data: createdToggle, + createdByUserId, }), ); @@ -1232,6 +1261,7 @@ class FeatureToggleService { projectId: string, newFeatureName: string, userName: string, + userId: number, replaceGroupId: boolean = true, ): Promise { const changeRequestEnabled = @@ -1262,6 +1292,7 @@ class FeatureToggleService { projectId, newToggle, userName, + userId, ); const variantTasks = newToggle.environments.map((e) => { @@ -1294,6 +1325,7 @@ class FeatureToggleService { this.dependentFeaturesService.cloneDependencies( { featureName, newFeatureName, projectId }, userName, + userId, ); await Promise.all([ @@ -1310,6 +1342,7 @@ class FeatureToggleService { updatedFeature: FeatureToggleDTO, userName: string, featureName: string, + userId: number, ): Promise { await this.validateFeatureBelongsToProject({ featureName, projectId }); @@ -1328,6 +1361,7 @@ class FeatureToggleService { await this.eventService.storeEvent( new FeatureMetadataUpdateEvent({ createdBy: userName, + createdByUserId: userId, data: featureToggle, preData, featureName, @@ -1452,6 +1486,7 @@ class FeatureToggleService { featureName: string, isStale: boolean, createdBy: string, + createdByUserId: number, ): Promise { const feature = await this.featureToggleStore.get(featureName); const { project } = feature; @@ -1464,6 +1499,7 @@ class FeatureToggleService { project, featureName, createdBy, + createdByUserId, }), ); @@ -1485,6 +1521,7 @@ class FeatureToggleService { await this.unprotectedArchiveToggle( featureName, extractUsernameFromUser(user), + user.id, projectId, ); } @@ -1492,6 +1529,7 @@ class FeatureToggleService { async unprotectedArchiveToggle( featureName: string, createdBy: string, + createdByUserId: number, projectId?: string, ): Promise { const feature = await this.featureToggleStore.get(featureName); @@ -1512,6 +1550,7 @@ class FeatureToggleService { [featureName], projectId, createdBy, + createdByUserId, ); } @@ -1519,6 +1558,7 @@ class FeatureToggleService { new FeatureArchivedEvent({ featureName, createdBy, + createdByUserId, project: feature.project, }), ); @@ -1534,6 +1574,7 @@ class FeatureToggleService { featureNames, extractUsernameFromUser(user), projectId, + user.id, ); } @@ -1559,6 +1600,7 @@ class FeatureToggleService { featureNames: string[], createdBy: string, projectId: string, + createdByUserId: number, ): Promise { await Promise.all([ this.validateFeaturesContext(featureNames, projectId), @@ -1572,6 +1614,7 @@ class FeatureToggleService { featureNames, projectId, createdBy, + createdByUserId, ); await this.eventService.storeEvents( @@ -1580,6 +1623,7 @@ class FeatureToggleService { new FeatureArchivedEvent({ featureName: feature.name, createdBy, + createdByUserId, project: feature.project, }), ), @@ -1591,6 +1635,7 @@ class FeatureToggleService { stale: boolean, createdBy: string, projectId: string, + createdByUserId: number, ): Promise { await this.validateFeaturesContext(featureNames, projectId); @@ -1612,6 +1657,7 @@ class FeatureToggleService { project: projectId, featureName: feature.name, createdBy, + createdByUserId, }), ), ); @@ -1666,6 +1712,7 @@ class FeatureToggleService { environment, enabled, createdBy, + user, shouldActivateDisabledStrategies, ); } @@ -1676,6 +1723,7 @@ class FeatureToggleService { environment: string, enabled: boolean, createdBy: string, + user?: IUser, shouldActivateDisabledStrategies = false, ): Promise { const hasEnvironment = @@ -1712,6 +1760,7 @@ class FeatureToggleService { featureName, }, createdBy, + user, ), ), ); @@ -1746,6 +1795,7 @@ class FeatureToggleService { featureName, }, createdBy, + user?.id || SYSTEM_USER_ID, ); } } @@ -1765,6 +1815,7 @@ class FeatureToggleService { featureName, environment, createdBy, + createdByUserId: user?.id || SYSTEM_USER_ID, }), ); } @@ -1775,6 +1826,7 @@ class FeatureToggleService { async storeFeatureUpdatedEventLegacy( featureName: string, createdBy: string, + createdByUserId: number, ): Promise { const feature = await this.getFeatureToggleLegacy(featureName); @@ -1783,6 +1835,7 @@ class FeatureToggleService { await this.eventService.storeEvent({ type: FEATURE_UPDATED, createdBy, + createdByUserId, featureName, data: feature, project: feature.project, @@ -1831,6 +1884,7 @@ class FeatureToggleService { featureName: string, newProject: string, createdBy: string, + createdByUserId: number, ): Promise { const changeRequestEnabled = await this.changeRequestAccessReadModel.isChangeRequestsEnabledForProject( @@ -1858,6 +1912,7 @@ class FeatureToggleService { await this.eventService.storeEvent( new FeatureChangeProjectEvent({ createdBy, + createdByUserId, oldProject, newProject, featureName, @@ -1870,7 +1925,11 @@ class FeatureToggleService { } // TODO: add project id. - async deleteFeature(featureName: string, createdBy: string): Promise { + async deleteFeature( + featureName: string, + createdBy: string, + createdByUserId: number, + ): Promise { await this.validateNoChildren(featureName); const toggle = await this.featureToggleStore.get(featureName); const tags = await this.tagStore.getAllTagsForFeature(featureName); @@ -1881,6 +1940,7 @@ class FeatureToggleService { featureName, project: toggle.project, createdBy, + createdByUserId, preData: toggle, tags, }), @@ -1891,6 +1951,7 @@ class FeatureToggleService { featureNames: string[], projectId: string, createdBy: string, + createdByUserId: number, ): Promise { await this.validateFeaturesContext(featureNames, projectId); await this.validateNoOrphanParents(featureNames); @@ -1912,6 +1973,7 @@ class FeatureToggleService { new FeatureDeletedEvent({ featureName: feature.name, createdBy, + createdByUserId, project: feature.project, preData: feature, tags: tags @@ -1929,6 +1991,7 @@ class FeatureToggleService { featureNames: string[], projectId: string, createdBy: string, + createdByUserId: number, ): Promise { await this.validateFeaturesContext(featureNames, projectId); @@ -1952,6 +2015,7 @@ class FeatureToggleService { new FeatureRevivedEvent({ featureName: feature.name, createdBy, + createdByUserId, project: feature.project, }), ), @@ -1959,7 +2023,11 @@ class FeatureToggleService { } // TODO: add project id. - async reviveFeature(featureName: string, createdBy: string): Promise { + async reviveFeature( + featureName: string, + createdBy: string, + createdByUserId: number, + ): Promise { const toggle = await this.featureToggleStore.revive(featureName); await this.featureToggleStore.disableAllEnvironmentsForFeatures([ featureName, @@ -1967,6 +2035,7 @@ class FeatureToggleService { await this.eventService.storeEvent( new FeatureRevivedEvent({ createdBy, + createdByUserId, featureName, project: toggle.project, }), @@ -2072,6 +2141,7 @@ class FeatureToggleService { project: string, newVariants: IVariant[], createdBy: string, + createdByUserId: number, ): Promise { await variantsArraySchema.validateAsync(newVariants); const fixedVariants = this.fixVariantWeights(newVariants); @@ -2088,6 +2158,7 @@ class FeatureToggleService { project, featureName, createdBy, + createdByUserId, oldVariants, newVariants: featureToggle.variants as IVariant[], }), @@ -2119,6 +2190,7 @@ class FeatureToggleService { new EnvironmentVariantEvent({ featureName, environment, + createdByUserId: user.id, project: projectId, createdBy: user, oldVariants: theOldVariants, @@ -2199,6 +2271,7 @@ class FeatureToggleService { createdBy: user, oldVariants: oldVariants[environment], newVariants: fixedVariants, + createdByUserId: user.id, }), ), ); @@ -2317,6 +2390,7 @@ class FeatureToggleService { ({ name, project }) => new PotentiallyStaleOnEvent({ featureName: name, + createdByUserId: SYSTEM_USER_ID, project, }), ), diff --git a/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts index 9864394efefe..fec458ec8ae2 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggle-service.e2e.test.ts @@ -9,6 +9,7 @@ import { IUnleashStores, IVariant, SKIP_CHANGE_REQUEST, + SYSTEM_USER_ID, } from '../../../types'; import EnvironmentService from '../../project-environments/environment-service'; import { ForbiddenError, PatternError, PermissionError } from '../../../error'; @@ -322,6 +323,7 @@ test('cloning a feature toggle copies variant environments correctly', async () 'default', clonedToggleName, 'test-user', + SYSTEM_USER_ID, true, ); @@ -350,6 +352,7 @@ test('cloning a feature toggle not allowed for change requests enabled', async ( 'default', 'clonedToggleName', 'test-user', + SYSTEM_USER_ID, true, ), ).rejects.toEqual( @@ -413,6 +416,7 @@ test('Cloning a feature toggle also clones segments correctly', async () => { 'default', clonedFeatureName, 'test-user', + SYSTEM_USER_ID, true, ); diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 724934712387..4674e65bda0a 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -273,7 +273,7 @@ export default class ProjectService { // updated project contains instructions to update the project but it may not represent a whole project const afterData = await this.projectStore.get(updatedProject.id); - await this.eventStore.store({ + await this.eventService.storeEvent({ type: PROJECT_UPDATED, project: updatedProject.id, createdBy: getCreatedBy(user), diff --git a/src/lib/services/user-service.ts b/src/lib/services/user-service.ts index 49866ce8d39b..bf56b75437e7 100644 --- a/src/lib/services/user-service.ts +++ b/src/lib/services/user-service.ts @@ -239,6 +239,7 @@ class UserService { await this.eventService.storeEvent( new UserCreatedEvent({ createdBy: this.getCreatedBy(updatedBy), + createdByUserId: user.id, userCreated, }), ); @@ -281,6 +282,7 @@ class UserService { createdBy: this.getCreatedBy(updatedBy), preUser: preUser, postUser: storedUser, + createdByUserId: user.id, }), ); @@ -298,6 +300,7 @@ class UserService { new UserDeletedEvent({ createdBy: this.getCreatedBy(updatedBy), deletedUser: user, + createdByUserId: updatedBy?.id || -1337, }), ); } diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index 2cc34a588f3c..16836e910597 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -173,6 +173,7 @@ export const BANNER_CREATED = 'banner-created' as const; export const BANNER_UPDATED = 'banner-updated' as const; export const BANNER_DELETED = 'banner-deleted' as const; +export const SYSTEM_USER_ID: number = -1337; export const IEventTypes = [ APPLICATION_CREATED, FEATURE_CREATED, @@ -312,6 +313,7 @@ export type IEventType = (typeof IEventTypes)[number]; export interface IBaseEvent { type: IEventType; createdBy: string; + createdByUserId: number; project?: string; environment?: string; featureName?: string; @@ -335,15 +337,24 @@ class BaseEvent implements IBaseEvent { readonly createdBy: string; + readonly createdByUserId: number; + /** + * @param type the type of the event we're creating. * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization + * @param createdByUserId accepts a number representing the internal id of the user creating this event */ - constructor(type: IEventType, createdBy: string | IUser) { + constructor( + type: IEventType, + createdBy: string | IUser, + createdByUserId: number, + ) { this.type = type; this.createdBy = typeof createdBy === 'string' ? createdBy : extractUsernameFromUser(createdBy); + this.createdByUserId = createdByUserId || SYSTEM_USER_ID; } } @@ -360,8 +371,13 @@ export class FeatureStaleEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; + createdByUserId: number; }) { - super(p.stale ? FEATURE_STALE_ON : FEATURE_STALE_OFF, p.createdBy); + super( + p.stale ? FEATURE_STALE_ON : FEATURE_STALE_OFF, + p.createdBy, + p.createdByUserId, + ); this.project = p.project; this.featureName = p.featureName; } @@ -383,12 +399,14 @@ export class FeatureEnvironmentEvent extends BaseEvent { featureName: string; environment: string; createdBy: string | IUser; + createdByUserId: number; }) { super( p.enabled ? FEATURE_ENVIRONMENT_ENABLED : FEATURE_ENVIRONMENT_DISABLED, p.createdBy, + p.createdByUserId, ); this.project = p.project; this.featureName = p.featureName; @@ -417,8 +435,9 @@ export class StrategiesOrderChangedEvent extends BaseEvent { createdBy: string | IUser; data: StrategyIds; preData: StrategyIds; + createdByUserId: number; }) { - super(STRATEGY_ORDER_CHANGED, p.createdBy); + super(STRATEGY_ORDER_CHANGED, p.createdBy, p.createdByUserId); const { project, featureName, environment, data, preData } = p; this.project = project; this.featureName = featureName; @@ -446,8 +465,9 @@ export class FeatureVariantEvent extends BaseEvent { createdBy: string | IUser; newVariants: IVariant[]; oldVariants: IVariant[]; + createdByUserId: number; }) { - super(FEATURE_VARIANTS_UPDATED, p.createdBy); + super(FEATURE_VARIANTS_UPDATED, p.createdBy, p.createdByUserId); this.project = p.project; this.featureName = p.featureName; this.data = { variants: p.newVariants }; @@ -476,8 +496,13 @@ export class EnvironmentVariantEvent extends BaseEvent { createdBy: string | IUser; newVariants: IVariant[]; oldVariants: IVariant[]; + createdByUserId: number; }) { - super(FEATURE_ENVIRONMENT_VARIANTS_UPDATED, p.createdBy); + super( + FEATURE_ENVIRONMENT_VARIANTS_UPDATED, + p.createdBy, + p.createdByUserId, + ); this.featureName = p.featureName; this.environment = p.environment; this.project = p.project; @@ -504,8 +529,9 @@ export class FeatureChangeProjectEvent extends BaseEvent { newProject: string; featureName: string; createdBy: string | IUser; + createdByUserId: number; }) { - super(FEATURE_PROJECT_CHANGE, p.createdBy); + super(FEATURE_PROJECT_CHANGE, p.createdBy, p.createdByUserId); const { newProject, oldProject, featureName } = p; this.project = newProject; this.featureName = featureName; @@ -528,8 +554,9 @@ export class FeatureCreatedEvent extends BaseEvent { featureName: string; createdBy: string | IUser; data: FeatureToggle; + createdByUserId: number; }) { - super(FEATURE_CREATED, p.createdBy); + super(FEATURE_CREATED, p.createdBy, p.createdByUserId); const { project, featureName, data } = p; this.project = project; this.featureName = featureName; @@ -549,8 +576,9 @@ export class FeatureArchivedEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; + createdByUserId: number; }) { - super(FEATURE_ARCHIVED, p.createdBy); + super(FEATURE_ARCHIVED, p.createdBy, p.createdByUserId); const { project, featureName } = p; this.project = project; this.featureName = featureName; @@ -569,8 +597,9 @@ export class FeatureRevivedEvent extends BaseEvent { project: string; featureName: string; createdBy: string | IUser; + createdByUserId: number; }) { - super(FEATURE_REVIVED, p.createdBy); + super(FEATURE_REVIVED, p.createdBy, p.createdByUserId); const { project, featureName } = p; this.project = project; this.featureName = featureName; @@ -595,8 +624,9 @@ export class FeatureDeletedEvent extends BaseEvent { preData: FeatureToggle; createdBy: string | IUser; tags: ITag[]; + createdByUserId: number; }) { - super(FEATURE_DELETED, p.createdBy); + super(FEATURE_DELETED, p.createdBy, p.createdByUserId); const { project, featureName, preData } = p; this.project = project; this.featureName = featureName; @@ -623,8 +653,9 @@ export class FeatureMetadataUpdateEvent extends BaseEvent { project: string; data: FeatureToggle; preData: FeatureToggle; + createdByUserId: number; }) { - super(FEATURE_METADATA_UPDATED, p.createdBy); + super(FEATURE_METADATA_UPDATED, p.createdBy, p.createdByUserId); const { project, featureName, data, preData } = p; this.project = project; this.featureName = featureName; @@ -651,8 +682,9 @@ export class FeatureStrategyAddEvent extends BaseEvent { environment: string; createdBy: string | IUser; data: IStrategyConfig; + createdByUserId: number; }) { - super(FEATURE_STRATEGY_ADD, p.createdBy); + super(FEATURE_STRATEGY_ADD, p.createdBy, p.createdByUserId); const { project, featureName, environment, data } = p; this.project = project; this.featureName = featureName; @@ -682,8 +714,9 @@ export class FeatureStrategyUpdateEvent extends BaseEvent { createdBy: string | IUser; data: IStrategyConfig; preData: IStrategyConfig; + createdByUserId: number; }) { - super(FEATURE_STRATEGY_UPDATE, p.createdBy); + super(FEATURE_STRATEGY_UPDATE, p.createdBy, p.createdByUserId); const { project, featureName, environment, data, preData } = p; this.project = project; this.featureName = featureName; @@ -711,8 +744,9 @@ export class FeatureStrategyRemoveEvent extends BaseEvent { environment: string; createdBy: string | IUser; preData: IStrategyConfig; + createdByUserId: number; }) { - super(FEATURE_STRATEGY_REMOVE, p.createdBy); + super(FEATURE_STRATEGY_REMOVE, p.createdBy, p.createdByUserId); const { project, featureName, environment, preData } = p; this.project = project; this.featureName = featureName; @@ -731,8 +765,13 @@ export class ProjectUserAddedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(p: { project: string; createdBy: string | IUser; data: any }) { - super(PROJECT_USER_ADDED, p.createdBy); + constructor(p: { + project: string; + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super(PROJECT_USER_ADDED, p.createdBy, p.createdByUserId); const { project, data } = p; this.project = project; this.data = data; @@ -754,8 +793,9 @@ export class ProjectUserRemovedEvent extends BaseEvent { project: string; createdBy: string | IUser; preData: any; + createdByUserId: number; }) { - super(PROJECT_USER_REMOVED, p.createdBy); + super(PROJECT_USER_REMOVED, p.createdBy, p.createdByUserId); const { project, preData } = p; this.project = project; this.data = null; @@ -778,8 +818,13 @@ export class ProjectUserUpdateRoleEvent extends BaseEvent { createdBy: string | IUser; data: any; preData: any; + createdByUserId: number; }) { - super(PROJECT_USER_ROLE_CHANGED, eventData.createdBy); + super( + PROJECT_USER_ROLE_CHANGED, + eventData.createdBy, + eventData.createdByUserId, + ); const { project, data, preData } = eventData; this.project = project; this.data = data; @@ -797,8 +842,13 @@ export class ProjectGroupAddedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(p: { project: string; createdBy: string | IUser; data: any }) { - super(PROJECT_GROUP_ADDED, p.createdBy); + constructor(p: { + project: string; + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super(PROJECT_GROUP_ADDED, p.createdBy, p.createdByUserId); const { project, data } = p; this.project = project; this.data = data; @@ -820,8 +870,9 @@ export class ProjectGroupRemovedEvent extends BaseEvent { project: string; createdBy: string | IUser; preData: any; + createdByUserId: number; }) { - super(PROJECT_GROUP_REMOVED, p.createdBy); + super(PROJECT_GROUP_REMOVED, p.createdBy, p.createdByUserId); const { project, preData } = p; this.project = project; this.data = null; @@ -844,8 +895,13 @@ export class ProjectGroupUpdateRoleEvent extends BaseEvent { createdBy: string | IUser; data: any; preData: any; + createdByUserId: number; }) { - super(PROJECT_GROUP_ROLE_CHANGED, eventData.createdBy); + super( + PROJECT_GROUP_ROLE_CHANGED, + eventData.createdBy, + eventData.createdByUserId, + ); const { project, data, preData } = eventData; this.project = project; this.data = data; @@ -863,8 +919,13 @@ export class ProjectAccessAddedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(p: { project: string; createdBy: string | IUser; data: any }) { - super(PROJECT_ACCESS_ADDED, p.createdBy); + constructor(p: { + project: string; + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super(PROJECT_ACCESS_ADDED, p.createdBy, p.createdByUserId); const { project, data } = p; this.project = project; this.data = data; @@ -887,8 +948,13 @@ export class ProjectAccessUserRolesUpdated extends BaseEvent { createdBy: string | IUser; data: any; preData: any; + createdByUserId: number; }) { - super(PROJECT_ACCESS_USER_ROLES_UPDATED, p.createdBy); + super( + PROJECT_ACCESS_USER_ROLES_UPDATED, + p.createdBy, + p.createdByUserId, + ); const { project, data, preData } = p; this.project = project; this.data = data; @@ -911,8 +977,13 @@ export class ProjectAccessGroupRolesUpdated extends BaseEvent { createdBy: string | IUser; data: any; preData: any; + createdByUserId: number; }) { - super(PROJECT_ACCESS_GROUP_ROLES_UPDATED, p.createdBy); + super( + PROJECT_ACCESS_GROUP_ROLES_UPDATED, + p.createdBy, + p.createdByUserId, + ); const { project, data, preData } = p; this.project = project; this.data = data; @@ -934,8 +1005,13 @@ export class ProjectAccessUserRolesDeleted extends BaseEvent { project: string; createdBy: string | IUser; preData: any; + createdByUserId: number; }) { - super(PROJECT_ACCESS_USER_ROLES_DELETED, p.createdBy); + super( + PROJECT_ACCESS_USER_ROLES_DELETED, + p.createdBy, + p.createdByUserId, + ); const { project, preData } = p; this.project = project; this.data = null; @@ -957,8 +1033,13 @@ export class ProjectAccessGroupRolesDeleted extends BaseEvent { project: string; createdBy: string | IUser; preData: any; + createdByUserId: number; }) { - super(PROJECT_ACCESS_GROUP_ROLES_DELETED, p.createdBy); + super( + PROJECT_ACCESS_GROUP_ROLES_DELETED, + p.createdBy, + p.createdByUserId, + ); const { project, preData } = p; this.project = project; this.data = null; @@ -972,8 +1053,12 @@ export class SettingCreatedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(eventData: { createdBy: string | IUser; data: any }) { - super(SETTING_CREATED, eventData.createdBy); + constructor(eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super(SETTING_CREATED, eventData.createdBy, eventData.createdByUserId); this.data = eventData.data; } } @@ -984,8 +1069,12 @@ export class SettingDeletedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(eventData: { createdBy: string | IUser; data: any }) { - super(SETTING_DELETED, eventData.createdBy); + constructor(eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super(SETTING_DELETED, eventData.createdBy, eventData.createdByUserId); this.data = eventData.data; } } @@ -998,10 +1087,14 @@ export class SettingUpdatedEvent extends BaseEvent { * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ constructor( - eventData: { createdBy: string | IUser; data: any }, + eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }, preData: any, ) { - super(SETTING_UPDATED, eventData.createdBy); + super(SETTING_UPDATED, eventData.createdBy, eventData.createdByUserId); this.data = eventData.data; this.preData = preData; } @@ -1013,8 +1106,16 @@ export class PublicSignupTokenCreatedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(eventData: { createdBy: string | IUser; data: any }) { - super(PUBLIC_SIGNUP_TOKEN_CREATED, eventData.createdBy); + constructor(eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super( + PUBLIC_SIGNUP_TOKEN_CREATED, + eventData.createdBy, + eventData.createdByUserId, + ); this.data = eventData.data; } } @@ -1025,8 +1126,16 @@ export class PublicSignupTokenUpdatedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(eventData: { createdBy: string | IUser; data: any }) { - super(PUBLIC_SIGNUP_TOKEN_TOKEN_UPDATED, eventData.createdBy); + constructor(eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super( + PUBLIC_SIGNUP_TOKEN_TOKEN_UPDATED, + eventData.createdBy, + eventData.createdByUserId, + ); this.data = eventData.data; } } @@ -1037,8 +1146,16 @@ export class PublicSignupTokenUserAddedEvent extends BaseEvent { /** * @param createdBy accepts a string for backward compatibility. Prefer using IUser for standardization */ - constructor(eventData: { createdBy: string | IUser; data: any }) { - super(PUBLIC_SIGNUP_TOKEN_USER_ADDED, eventData.createdBy); + constructor(eventData: { + createdBy: string | IUser; + data: any; + createdByUserId: number; + }) { + super( + PUBLIC_SIGNUP_TOKEN_USER_ADDED, + eventData.createdBy, + eventData.createdByUserId, + ); this.data = eventData.data; } } @@ -1056,8 +1173,13 @@ export class ApiTokenCreatedEvent extends BaseEvent { constructor(eventData: { createdBy: string | IUser; apiToken: Omit; + createdByUserId: number; }) { - super(API_TOKEN_CREATED, eventData.createdBy); + super( + API_TOKEN_CREATED, + eventData.createdBy, + eventData.createdByUserId, + ); this.data = eventData.apiToken; this.environment = eventData.apiToken.environment; this.project = eventData.apiToken.project; @@ -1077,8 +1199,13 @@ export class ApiTokenDeletedEvent extends BaseEvent { constructor(eventData: { createdBy: string | IUser; apiToken: Omit; + createdByUserId: number; }) { - super(API_TOKEN_DELETED, eventData.createdBy); + super( + API_TOKEN_DELETED, + eventData.createdBy, + eventData.createdByUserId, + ); this.preData = eventData.apiToken; this.environment = eventData.apiToken.environment; this.project = eventData.apiToken.project; @@ -1101,8 +1228,13 @@ export class ApiTokenUpdatedEvent extends BaseEvent { createdBy: string | IUser; previousToken: Omit; apiToken: Omit; + createdByUserId: number; }) { - super(API_TOKEN_UPDATED, eventData.createdBy); + super( + API_TOKEN_UPDATED, + eventData.createdBy, + eventData.createdByUserId, + ); this.preData = eventData.previousToken; this.data = eventData.apiToken; this.environment = eventData.apiToken.environment; @@ -1115,8 +1247,16 @@ export class PotentiallyStaleOnEvent extends BaseEvent { readonly project: string; - constructor(eventData: { featureName: string; project: string }) { - super(FEATURE_POTENTIALLY_STALE_ON, 'unleash-system'); + constructor(eventData: { + featureName: string; + project: string; + createdByUserId: number; + }) { + super( + FEATURE_POTENTIALLY_STALE_ON, + 'unleash-system', + eventData.createdByUserId, + ); this.featureName = eventData.featureName; this.project = eventData.project; } @@ -1128,8 +1268,9 @@ export class UserCreatedEvent extends BaseEvent { constructor(eventData: { createdBy: string | IUser; userCreated: IUserWithRootRole; + createdByUserId: number; }) { - super(USER_CREATED, eventData.createdBy); + super(USER_CREATED, eventData.createdBy, eventData.createdByUserId); this.data = mapUserToData(eventData.userCreated); } } @@ -1142,8 +1283,9 @@ export class UserUpdatedEvent extends BaseEvent { createdBy: string | IUser; preUser: IUserWithRootRole; postUser: IUserWithRootRole; + createdByUserId: number; }) { - super(USER_UPDATED, eventData.createdBy); + super(USER_UPDATED, eventData.createdBy, eventData.createdByUserId); this.preData = mapUserToData(eventData.preUser); this.data = mapUserToData(eventData.postUser); } @@ -1155,8 +1297,9 @@ export class UserDeletedEvent extends BaseEvent { constructor(eventData: { createdBy: string | IUser; deletedUser: IUserWithRootRole; + createdByUserId: number; }) { - super(USER_DELETED, eventData.createdBy); + super(USER_DELETED, eventData.createdBy, eventData.createdByUserId); this.preData = mapUserToData(eventData.deletedUser); } }