From d29230cd49e845b4578ae5b22df4429da3593e06 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 26 Jun 2024 09:05:17 +0200 Subject: [PATCH] feat: transactional complete/uncomplete feature (#7451) --- .../createFeatureLifecycle.ts | 73 +++++++++---------- .../feature-lifecycle-controller.ts | 25 +++---- .../feature-lifecycle.e2e.test.ts | 3 - src/lib/services/index.ts | 10 ++- src/lib/types/services.ts | 1 + 5 files changed, 53 insertions(+), 59 deletions(-) diff --git a/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts b/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts index cf8c34461613..2ba1240ef8b7 100644 --- a/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts +++ b/src/lib/features/feature-lifecycle/createFeatureLifecycle.ts @@ -14,48 +14,41 @@ import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store'; import EventEmitter from 'events'; -export const createFeatureLifecycleService = ( - db: Db, - config: IUnleashConfig, -) => { - const { eventBus, getLogger, flagResolver } = config; - const eventStore = new EventStore(db, getLogger); - const featureLifecycleStore = new FeatureLifecycleStore(db); - const environmentStore = new EnvironmentStore(db, eventBus, getLogger); - const featureEnvironmentStore = new FeatureEnvironmentStore( - db, - eventBus, - getLogger, - ); - const featureTagStore = new FeatureTagStore( - db, - config.eventBus, - config.getLogger, - ); - const eventService = new EventService( - { eventStore, featureTagStore }, - { getLogger, eventBus: new EventEmitter() }, - ); - const featureLifecycleService = new FeatureLifecycleService( - { - eventStore, - featureLifecycleStore, - environmentStore, - featureEnvironmentStore, - }, - { - eventService, - }, - config, - ); +export const createFeatureLifecycleService = + (config: IUnleashConfig) => (db: Db) => { + const { eventBus, getLogger, flagResolver } = config; + const eventStore = new EventStore(db, getLogger); + const featureLifecycleStore = new FeatureLifecycleStore(db); + const environmentStore = new EnvironmentStore(db, eventBus, getLogger); + const featureEnvironmentStore = new FeatureEnvironmentStore( + db, + eventBus, + getLogger, + ); + const featureTagStore = new FeatureTagStore( + db, + config.eventBus, + config.getLogger, + ); + const eventService = new EventService( + { eventStore, featureTagStore }, + { getLogger, eventBus: new EventEmitter() }, + ); + const featureLifecycleService = new FeatureLifecycleService( + { + eventStore, + featureLifecycleStore, + environmentStore, + featureEnvironmentStore, + }, + { + eventService, + }, + config, + ); - return { - featureLifecycleService, - featureLifecycleStore, - eventStore, - environmentStore, + return featureLifecycleService; }; -}; export const createFakeFeatureLifecycleService = (config: IUnleashConfig) => { const eventStore = new FakeEventStore(); diff --git a/src/lib/features/feature-lifecycle/feature-lifecycle-controller.ts b/src/lib/features/feature-lifecycle/feature-lifecycle-controller.ts index 9f6cd5b47c54..1ce31bbd888e 100644 --- a/src/lib/features/feature-lifecycle/feature-lifecycle-controller.ts +++ b/src/lib/features/feature-lifecycle/feature-lifecycle-controller.ts @@ -21,6 +21,7 @@ import Controller from '../../routes/controller'; import type { Request, Response } from 'express'; import { NotFoundError } from '../../error'; import type { IAuthRequest } from '../../routes/unleash-types'; +import type { WithTransactional } from '../../db/transaction'; interface FeatureLifecycleParams { projectId: string; @@ -30,7 +31,7 @@ interface FeatureLifecycleParams { const PATH = '/:projectId/features/:featureName/lifecycle'; export default class FeatureLifecycleController extends Controller { - private featureLifecycleService: FeatureLifecycleService; + private featureLifecycleService: WithTransactional; private openApiService: OpenApiService; @@ -39,12 +40,15 @@ export default class FeatureLifecycleController extends Controller { constructor( config: IUnleashConfig, { - featureLifecycleService, + transactionalFeatureLifecycleService, openApiService, - }: Pick, + }: Pick< + IUnleashServices, + 'openApiService' | 'transactionalFeatureLifecycleService' + >, ) { super(config); - this.featureLifecycleService = featureLifecycleService; + this.featureLifecycleService = transactionalFeatureLifecycleService; this.openApiService = openApiService; this.flagResolver = config.flagResolver; @@ -147,11 +151,8 @@ export default class FeatureLifecycleController extends Controller { const status = req.body; - await this.featureLifecycleService.featureCompleted( - featureName, - projectId, - status, - req.audit, + await this.featureLifecycleService.transactional((service) => + service.featureCompleted(featureName, projectId, status, req.audit), ); res.status(200).end(); @@ -166,10 +167,8 @@ export default class FeatureLifecycleController extends Controller { } const { featureName, projectId } = req.params; - await this.featureLifecycleService.featureUncompleted( - featureName, - projectId, - req.audit, + await this.featureLifecycleService.transactional((service) => + service.featureUncompleted(featureName, projectId, req.audit), ); res.status(200).end(); diff --git a/src/lib/features/feature-lifecycle/feature-lifecycle.e2e.test.ts b/src/lib/features/feature-lifecycle/feature-lifecycle.e2e.test.ts index 70941b91462a..87c4aa38a937 100644 --- a/src/lib/features/feature-lifecycle/feature-lifecycle.e2e.test.ts +++ b/src/lib/features/feature-lifecycle/feature-lifecycle.e2e.test.ts @@ -14,7 +14,6 @@ import { type StageName, } from '../../types'; import type EventEmitter from 'events'; -import type { FeatureLifecycleService } from './feature-lifecycle-service'; import type { FeatureLifecycleCompletedSchema } from '../../openapi'; import { FeatureLifecycleReadModel } from './feature-lifecycle-read-model'; import type { IFeatureLifecycleReadModel } from './feature-lifecycle-read-model-type'; @@ -22,7 +21,6 @@ import { STAGE_ENTERED } from '../../metric-events'; let app: IUnleashTest; let db: ITestDb; -let featureLifecycleService: FeatureLifecycleService; let featureLifecycleStore: IFeatureLifecycleStore; let eventStore: IEventStore; let eventBus: EventEmitter; @@ -43,7 +41,6 @@ beforeAll(async () => { ); eventStore = db.stores.eventStore; eventBus = app.config.eventBus; - featureLifecycleService = app.services.featureLifecycleService; featureLifecycleReadModel = new FeatureLifecycleReadModel( db.rawDatabase, app.config.flagResolver, diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index d2c739edd77c..ce69c31d8798 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -362,9 +362,12 @@ export const createServices = ( config.getLogger, ); - const { featureLifecycleService } = db - ? createFeatureLifecycleService(db, config) - : createFakeFeatureLifecycleService(config); + const transactionalFeatureLifecycleService = db + ? withTransactional(createFeatureLifecycleService(config), db) + : withFakeTransactional( + createFakeFeatureLifecycleService(config).featureLifecycleService, + ); + const featureLifecycleService = transactionalFeatureLifecycleService; featureLifecycleService.listen(); return { @@ -426,6 +429,7 @@ export const createServices = ( projectInsightsService, jobService, featureLifecycleService, + transactionalFeatureLifecycleService, }; }; diff --git a/src/lib/types/services.ts b/src/lib/types/services.ts index d5f199166d66..bf302f278006 100644 --- a/src/lib/types/services.ts +++ b/src/lib/types/services.ts @@ -117,4 +117,5 @@ export interface IUnleashServices { projectInsightsService: ProjectInsightsService; jobService: JobService; featureLifecycleService: FeatureLifecycleService; + transactionalFeatureLifecycleService: WithTransactional; }