From 14796aedc19d446dd54dc61d7c7280b0b32fe636 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Mon, 4 Mar 2024 13:25:16 +0100 Subject: [PATCH] refactor: segment read model used in export-import (#6418) --- .../createExportImportService.ts | 25 ++---- .../export-import-service.ts | 22 ++---- .../segment/fake-segment-read-model.ts | 12 +++ .../segment/segment-read-model-type.ts | 6 ++ .../features/segment/segment-read-model.ts | 78 +++++++++++++++++++ 5 files changed, 110 insertions(+), 33 deletions(-) create mode 100644 src/lib/features/segment/fake-segment-read-model.ts create mode 100644 src/lib/features/segment/segment-read-model-type.ts create mode 100644 src/lib/features/segment/segment-read-model.ts diff --git a/src/lib/features/export-import-toggles/createExportImportService.ts b/src/lib/features/export-import-toggles/createExportImportService.ts index dba9f1bf6e26..ff75d77cdf7f 100644 --- a/src/lib/features/export-import-toggles/createExportImportService.ts +++ b/src/lib/features/export-import-toggles/createExportImportService.ts @@ -25,12 +25,10 @@ import { createFakeFeatureToggleService, createFeatureToggleService, } from '../feature-toggle/createFeatureToggleService'; -import SegmentStore from '../../db/segment-store'; import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; import FakeFeatureToggleStore from '../feature-toggle/fakes/fake-feature-toggle-store'; import FakeTagStore from '../../../test/fixtures/fake-tag-store'; import FakeTagTypeStore from '../tag-type/fake-tag-type-store'; -import FakeSegmentStore from '../../../test/fixtures/fake-segment-store'; import FakeProjectStore from '../../../test/fixtures/fake-project-store'; import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; import FakeContextFieldStore from '../../../test/fixtures/fake-context-field-store'; @@ -46,15 +44,13 @@ import { import { DeferredServiceFactory } from '../../db/transaction'; import { DependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model'; import { FakeDependentFeaturesReadModel } from '../dependent-features/fake-dependent-features-read-model'; -import { - createFakeSegmentService, - createSegmentService, -} from '../segment/createSegmentService'; import { createDependentFeaturesService, createFakeDependentFeaturesService, } from '../dependent-features/createDependentFeaturesService'; import { createEventsService } from '../events/createEventsService'; +import { SegmentReadModel } from '../segment/segment-read-model'; +import { FakeSegmentReadModel } from '../segment/fake-segment-read-model'; export const createFakeExportImportTogglesService = ( config: IUnleashConfig, @@ -64,7 +60,6 @@ export const createFakeExportImportTogglesService = ( const featureToggleStore = new FakeFeatureToggleStore(); const tagStore = new FakeTagStore(); const tagTypeStore = new FakeTagTypeStore(); - const segmentStore = new FakeSegmentStore(); const projectStore = new FakeProjectStore(); const featureTagStore = new FakeFeatureTagStore(); const strategyStore = new FakeStrategiesStore(); @@ -115,7 +110,7 @@ export const createFakeExportImportTogglesService = ( ); const dependentFeaturesReadModel = new FakeDependentFeaturesReadModel(); - const segmentService = createFakeSegmentService(config); + const segmentReadModel = new FakeSegmentReadModel(); const dependentFeaturesService = createFakeDependentFeaturesService(config); @@ -126,7 +121,6 @@ export const createFakeExportImportTogglesService = ( contextFieldStore, featureToggleStore, featureTagStore, - segmentStore, tagTypeStore, featureEnvironmentStore, }, @@ -139,10 +133,10 @@ export const createFakeExportImportTogglesService = ( contextService, strategyService, tagTypeService, - segmentService, dependentFeaturesService, }, dependentFeaturesReadModel, + segmentReadModel, ); return exportImportService; @@ -162,12 +156,6 @@ export const deferredExportImportTogglesService = ( ); const tagStore = new TagStore(db, eventBus, getLogger); const tagTypeStore = new TagTypeStore(db, eventBus, getLogger); - const segmentStore = new SegmentStore( - db, - eventBus, - getLogger, - flagResolver, - ); const projectStore = new ProjectStore( db, eventBus, @@ -230,7 +218,7 @@ export const deferredExportImportTogglesService = ( ); const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); - const segmentService = createSegmentService(db, config); + const segmentReadModel = new SegmentReadModel(db); const dependentFeaturesService = createDependentFeaturesService(config)(db); @@ -242,7 +230,6 @@ export const deferredExportImportTogglesService = ( contextFieldStore, featureToggleStore, featureTagStore, - segmentStore, tagTypeStore, featureEnvironmentStore, }, @@ -255,10 +242,10 @@ export const deferredExportImportTogglesService = ( contextService, strategyService, tagTypeService, - segmentService, dependentFeaturesService, }, dependentFeaturesReadModel, + segmentReadModel, ); return exportImportService; 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 dc5b95152fc5..21d04df466e3 100644 --- a/src/lib/features/export-import-toggles/export-import-service.ts +++ b/src/lib/features/export-import-toggles/export-import-service.ts @@ -12,7 +12,6 @@ import { IFeatureStrategySegment, IFeatureTagStore, IFlagResolver, - ISegmentStore, ITagTypeStore, IUnleashConfig, IUnleashServices, @@ -51,8 +50,8 @@ import { findDuplicates } from '../../util/findDuplicates'; import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feature-toggle-service'; import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type'; import groupBy from 'lodash.groupby'; -import { ISegmentService } from '../../segments/segment-service-interface'; import { allSettledWithRejection } from '../../util/allSettledWithRejection'; +import { ISegmentReadModel } from '../segment/segment-read-model-type'; export type IImportService = { validate( @@ -88,8 +87,6 @@ export default class ExportImportService private featureTagStore: IFeatureTagStore; - private segmentStore: ISegmentStore; - private flagResolver: IFlagResolver; private featureToggleService: FeatureToggleService; @@ -106,7 +103,7 @@ export default class ExportImportService private tagTypeService: TagTypeService; - private segmentService: ISegmentService; + private segmentReadModel: ISegmentReadModel; private featureTagService: FeatureTagService; @@ -125,7 +122,6 @@ export default class ExportImportService | 'featureEnvironmentStore' | 'tagTypeStore' | 'featureTagStore' - | 'segmentStore' | 'contextFieldStore' >, { @@ -140,7 +136,6 @@ export default class ExportImportService eventService, tagTypeService, featureTagService, - segmentService, dependentFeaturesService, }: Pick< IUnleashServices, @@ -151,10 +146,10 @@ export default class ExportImportService | 'eventService' | 'tagTypeService' | 'featureTagService' - | 'segmentService' | 'dependentFeaturesService' >, dependentFeaturesReadModel: IDependentFeaturesReadModel, + segmentReadModel: ISegmentReadModel, ) { this.toggleStore = stores.featureToggleStore; this.importTogglesStore = stores.importTogglesStore; @@ -162,14 +157,12 @@ export default class ExportImportService this.featureEnvironmentStore = stores.featureEnvironmentStore; this.tagTypeStore = stores.tagTypeStore; this.featureTagStore = stores.featureTagStore; - this.segmentStore = stores.segmentStore; this.flagResolver = flagResolver; this.featureToggleService = featureToggleService; this.contextFieldStore = stores.contextFieldStore; this.strategyService = strategyService; this.contextService = contextService; this.accessService = accessService; - this.segmentService = segmentService; this.eventService = eventService; this.tagTypeService = tagTypeService; this.featureTagService = featureTagService; @@ -181,6 +174,7 @@ export default class ExportImportService this.contextService, ); this.dependentFeaturesReadModel = dependentFeaturesReadModel; + this.segmentReadModel = segmentReadModel; this.logger = getLogger('services/state-service.js'); } @@ -479,7 +473,7 @@ export default class ExportImportService private async getUnsupportedSegments( dto: ImportTogglesSchema, ): Promise { - const supportedSegments = await this.segmentService.getAll(); + const supportedSegments = await this.segmentReadModel.getAll(); const targetProject = dto.project; return dto.data.segments ? dto.data.segments @@ -583,7 +577,7 @@ export default class ExportImportService } private async remapSegments(dto: ImportTogglesSchema) { - const existingSegments = await this.segmentService.getAll(); + const existingSegments = await this.segmentReadModel.getAll(); const segmentMapping = new Map( dto.data.segments?.map((segment) => [ @@ -820,10 +814,10 @@ export default class ExportImportService featureNames, query.environment, ), - this.segmentStore.getAllFeatureStrategySegments(), + this.segmentReadModel.getAllFeatureStrategySegments(), this.contextFieldStore.getAll(), this.featureTagStore.getAllByFeatures(featureNames), - this.segmentStore.getAll(), + this.segmentReadModel.getAll(), this.tagTypeStore.getAll(), this.dependentFeaturesReadModel.getDependencies(featureNames), ]); diff --git a/src/lib/features/segment/fake-segment-read-model.ts b/src/lib/features/segment/fake-segment-read-model.ts new file mode 100644 index 000000000000..44963a071b23 --- /dev/null +++ b/src/lib/features/segment/fake-segment-read-model.ts @@ -0,0 +1,12 @@ +import { IFeatureStrategySegment, ISegment } from '../../types'; +import { ISegmentReadModel } from './segment-read-model-type'; + +export class FakeSegmentReadModel implements ISegmentReadModel { + async getAll(): Promise { + return []; + } + + async getAllFeatureStrategySegments(): Promise { + return []; + } +} diff --git a/src/lib/features/segment/segment-read-model-type.ts b/src/lib/features/segment/segment-read-model-type.ts new file mode 100644 index 000000000000..0a46fc22ef8f --- /dev/null +++ b/src/lib/features/segment/segment-read-model-type.ts @@ -0,0 +1,6 @@ +import { IFeatureStrategySegment, ISegment } from '../../types'; + +export interface ISegmentReadModel { + getAll(): Promise; + getAllFeatureStrategySegments(): Promise; +} diff --git a/src/lib/features/segment/segment-read-model.ts b/src/lib/features/segment/segment-read-model.ts new file mode 100644 index 000000000000..642288576511 --- /dev/null +++ b/src/lib/features/segment/segment-read-model.ts @@ -0,0 +1,78 @@ +import { IConstraint, IFeatureStrategySegment, ISegment } from '../../types'; +import { ISegmentReadModel } from './segment-read-model-type'; +import NotFoundError from '../../error/notfound-error'; +import { Db } from '../../db/db'; + +interface ISegmentRow { + id: number; + name: string; + description?: string; + segment_project_id?: string; + created_by?: string; + created_at: Date; + constraints: IConstraint[]; +} + +const COLUMNS = [ + 'id', + 'name', + 'description', + 'segment_project_id', + 'created_by', + 'created_at', + 'constraints', +]; + +interface IFeatureStrategySegmentRow { + feature_strategy_id: string; + segment_id: number; + created_at?: Date; +} + +export class SegmentReadModel implements ISegmentReadModel { + private db: Db; + + constructor(db: Db) { + this.db = db; + } + + prefixColumns(): string[] { + return COLUMNS.map((c) => `segments.${c}`); + } + + mapRow(row?: ISegmentRow): ISegment { + if (!row) { + throw new NotFoundError('No row'); + } + + return { + id: row.id, + name: row.name, + description: row.description, + project: row.segment_project_id || undefined, + constraints: row.constraints, + createdBy: row.created_by, + createdAt: row.created_at, + }; + } + + async getAll(): Promise { + const rows: ISegmentRow[] = await this.db + .select(this.prefixColumns()) + .from('segments') + .orderBy('segments.name', 'asc'); + + return rows.map(this.mapRow); + } + + async getAllFeatureStrategySegments(): Promise { + const rows: IFeatureStrategySegmentRow[] = await this.db + .select(['segment_id', 'feature_strategy_id']) + .from('feature_strategy_segment'); + + return rows.map((row) => ({ + featureStrategyId: row.feature_strategy_id, + segmentId: row.segment_id, + })); + } +}