1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-19 01:17:18 +02:00

refactor: segment read model used in export-import (#6418)

This commit is contained in:
Mateusz Kwasniewski 2024-03-04 13:25:16 +01:00 committed by GitHub
parent 19fbd7a0c4
commit 14796aedc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 110 additions and 33 deletions

View File

@ -25,12 +25,10 @@ import {
createFakeFeatureToggleService, createFakeFeatureToggleService,
createFeatureToggleService, createFeatureToggleService,
} from '../feature-toggle/createFeatureToggleService'; } from '../feature-toggle/createFeatureToggleService';
import SegmentStore from '../../db/segment-store';
import { FeatureEnvironmentStore } from '../../db/feature-environment-store'; import { FeatureEnvironmentStore } from '../../db/feature-environment-store';
import FakeFeatureToggleStore from '../feature-toggle/fakes/fake-feature-toggle-store'; import FakeFeatureToggleStore from '../feature-toggle/fakes/fake-feature-toggle-store';
import FakeTagStore from '../../../test/fixtures/fake-tag-store'; import FakeTagStore from '../../../test/fixtures/fake-tag-store';
import FakeTagTypeStore from '../tag-type/fake-tag-type-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 FakeProjectStore from '../../../test/fixtures/fake-project-store';
import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store';
import FakeContextFieldStore from '../../../test/fixtures/fake-context-field-store'; import FakeContextFieldStore from '../../../test/fixtures/fake-context-field-store';
@ -46,15 +44,13 @@ import {
import { DeferredServiceFactory } from '../../db/transaction'; import { DeferredServiceFactory } from '../../db/transaction';
import { DependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model'; import { DependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model';
import { FakeDependentFeaturesReadModel } from '../dependent-features/fake-dependent-features-read-model'; import { FakeDependentFeaturesReadModel } from '../dependent-features/fake-dependent-features-read-model';
import {
createFakeSegmentService,
createSegmentService,
} from '../segment/createSegmentService';
import { import {
createDependentFeaturesService, createDependentFeaturesService,
createFakeDependentFeaturesService, createFakeDependentFeaturesService,
} from '../dependent-features/createDependentFeaturesService'; } from '../dependent-features/createDependentFeaturesService';
import { createEventsService } from '../events/createEventsService'; import { createEventsService } from '../events/createEventsService';
import { SegmentReadModel } from '../segment/segment-read-model';
import { FakeSegmentReadModel } from '../segment/fake-segment-read-model';
export const createFakeExportImportTogglesService = ( export const createFakeExportImportTogglesService = (
config: IUnleashConfig, config: IUnleashConfig,
@ -64,7 +60,6 @@ export const createFakeExportImportTogglesService = (
const featureToggleStore = new FakeFeatureToggleStore(); const featureToggleStore = new FakeFeatureToggleStore();
const tagStore = new FakeTagStore(); const tagStore = new FakeTagStore();
const tagTypeStore = new FakeTagTypeStore(); const tagTypeStore = new FakeTagTypeStore();
const segmentStore = new FakeSegmentStore();
const projectStore = new FakeProjectStore(); const projectStore = new FakeProjectStore();
const featureTagStore = new FakeFeatureTagStore(); const featureTagStore = new FakeFeatureTagStore();
const strategyStore = new FakeStrategiesStore(); const strategyStore = new FakeStrategiesStore();
@ -115,7 +110,7 @@ export const createFakeExportImportTogglesService = (
); );
const dependentFeaturesReadModel = new FakeDependentFeaturesReadModel(); const dependentFeaturesReadModel = new FakeDependentFeaturesReadModel();
const segmentService = createFakeSegmentService(config); const segmentReadModel = new FakeSegmentReadModel();
const dependentFeaturesService = createFakeDependentFeaturesService(config); const dependentFeaturesService = createFakeDependentFeaturesService(config);
@ -126,7 +121,6 @@ export const createFakeExportImportTogglesService = (
contextFieldStore, contextFieldStore,
featureToggleStore, featureToggleStore,
featureTagStore, featureTagStore,
segmentStore,
tagTypeStore, tagTypeStore,
featureEnvironmentStore, featureEnvironmentStore,
}, },
@ -139,10 +133,10 @@ export const createFakeExportImportTogglesService = (
contextService, contextService,
strategyService, strategyService,
tagTypeService, tagTypeService,
segmentService,
dependentFeaturesService, dependentFeaturesService,
}, },
dependentFeaturesReadModel, dependentFeaturesReadModel,
segmentReadModel,
); );
return exportImportService; return exportImportService;
@ -162,12 +156,6 @@ export const deferredExportImportTogglesService = (
); );
const tagStore = new TagStore(db, eventBus, getLogger); const tagStore = new TagStore(db, eventBus, getLogger);
const tagTypeStore = new TagTypeStore(db, eventBus, getLogger); const tagTypeStore = new TagTypeStore(db, eventBus, getLogger);
const segmentStore = new SegmentStore(
db,
eventBus,
getLogger,
flagResolver,
);
const projectStore = new ProjectStore( const projectStore = new ProjectStore(
db, db,
eventBus, eventBus,
@ -230,7 +218,7 @@ export const deferredExportImportTogglesService = (
); );
const dependentFeaturesReadModel = new DependentFeaturesReadModel(db); const dependentFeaturesReadModel = new DependentFeaturesReadModel(db);
const segmentService = createSegmentService(db, config); const segmentReadModel = new SegmentReadModel(db);
const dependentFeaturesService = const dependentFeaturesService =
createDependentFeaturesService(config)(db); createDependentFeaturesService(config)(db);
@ -242,7 +230,6 @@ export const deferredExportImportTogglesService = (
contextFieldStore, contextFieldStore,
featureToggleStore, featureToggleStore,
featureTagStore, featureTagStore,
segmentStore,
tagTypeStore, tagTypeStore,
featureEnvironmentStore, featureEnvironmentStore,
}, },
@ -255,10 +242,10 @@ export const deferredExportImportTogglesService = (
contextService, contextService,
strategyService, strategyService,
tagTypeService, tagTypeService,
segmentService,
dependentFeaturesService, dependentFeaturesService,
}, },
dependentFeaturesReadModel, dependentFeaturesReadModel,
segmentReadModel,
); );
return exportImportService; return exportImportService;

View File

@ -12,7 +12,6 @@ import {
IFeatureStrategySegment, IFeatureStrategySegment,
IFeatureTagStore, IFeatureTagStore,
IFlagResolver, IFlagResolver,
ISegmentStore,
ITagTypeStore, ITagTypeStore,
IUnleashConfig, IUnleashConfig,
IUnleashServices, IUnleashServices,
@ -51,8 +50,8 @@ import { findDuplicates } from '../../util/findDuplicates';
import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feature-toggle-service'; import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feature-toggle-service';
import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type'; import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type';
import groupBy from 'lodash.groupby'; import groupBy from 'lodash.groupby';
import { ISegmentService } from '../../segments/segment-service-interface';
import { allSettledWithRejection } from '../../util/allSettledWithRejection'; import { allSettledWithRejection } from '../../util/allSettledWithRejection';
import { ISegmentReadModel } from '../segment/segment-read-model-type';
export type IImportService = { export type IImportService = {
validate( validate(
@ -88,8 +87,6 @@ export default class ExportImportService
private featureTagStore: IFeatureTagStore; private featureTagStore: IFeatureTagStore;
private segmentStore: ISegmentStore;
private flagResolver: IFlagResolver; private flagResolver: IFlagResolver;
private featureToggleService: FeatureToggleService; private featureToggleService: FeatureToggleService;
@ -106,7 +103,7 @@ export default class ExportImportService
private tagTypeService: TagTypeService; private tagTypeService: TagTypeService;
private segmentService: ISegmentService; private segmentReadModel: ISegmentReadModel;
private featureTagService: FeatureTagService; private featureTagService: FeatureTagService;
@ -125,7 +122,6 @@ export default class ExportImportService
| 'featureEnvironmentStore' | 'featureEnvironmentStore'
| 'tagTypeStore' | 'tagTypeStore'
| 'featureTagStore' | 'featureTagStore'
| 'segmentStore'
| 'contextFieldStore' | 'contextFieldStore'
>, >,
{ {
@ -140,7 +136,6 @@ export default class ExportImportService
eventService, eventService,
tagTypeService, tagTypeService,
featureTagService, featureTagService,
segmentService,
dependentFeaturesService, dependentFeaturesService,
}: Pick< }: Pick<
IUnleashServices, IUnleashServices,
@ -151,10 +146,10 @@ export default class ExportImportService
| 'eventService' | 'eventService'
| 'tagTypeService' | 'tagTypeService'
| 'featureTagService' | 'featureTagService'
| 'segmentService'
| 'dependentFeaturesService' | 'dependentFeaturesService'
>, >,
dependentFeaturesReadModel: IDependentFeaturesReadModel, dependentFeaturesReadModel: IDependentFeaturesReadModel,
segmentReadModel: ISegmentReadModel,
) { ) {
this.toggleStore = stores.featureToggleStore; this.toggleStore = stores.featureToggleStore;
this.importTogglesStore = stores.importTogglesStore; this.importTogglesStore = stores.importTogglesStore;
@ -162,14 +157,12 @@ export default class ExportImportService
this.featureEnvironmentStore = stores.featureEnvironmentStore; this.featureEnvironmentStore = stores.featureEnvironmentStore;
this.tagTypeStore = stores.tagTypeStore; this.tagTypeStore = stores.tagTypeStore;
this.featureTagStore = stores.featureTagStore; this.featureTagStore = stores.featureTagStore;
this.segmentStore = stores.segmentStore;
this.flagResolver = flagResolver; this.flagResolver = flagResolver;
this.featureToggleService = featureToggleService; this.featureToggleService = featureToggleService;
this.contextFieldStore = stores.contextFieldStore; this.contextFieldStore = stores.contextFieldStore;
this.strategyService = strategyService; this.strategyService = strategyService;
this.contextService = contextService; this.contextService = contextService;
this.accessService = accessService; this.accessService = accessService;
this.segmentService = segmentService;
this.eventService = eventService; this.eventService = eventService;
this.tagTypeService = tagTypeService; this.tagTypeService = tagTypeService;
this.featureTagService = featureTagService; this.featureTagService = featureTagService;
@ -181,6 +174,7 @@ export default class ExportImportService
this.contextService, this.contextService,
); );
this.dependentFeaturesReadModel = dependentFeaturesReadModel; this.dependentFeaturesReadModel = dependentFeaturesReadModel;
this.segmentReadModel = segmentReadModel;
this.logger = getLogger('services/state-service.js'); this.logger = getLogger('services/state-service.js');
} }
@ -479,7 +473,7 @@ export default class ExportImportService
private async getUnsupportedSegments( private async getUnsupportedSegments(
dto: ImportTogglesSchema, dto: ImportTogglesSchema,
): Promise<string[]> { ): Promise<string[]> {
const supportedSegments = await this.segmentService.getAll(); const supportedSegments = await this.segmentReadModel.getAll();
const targetProject = dto.project; const targetProject = dto.project;
return dto.data.segments return dto.data.segments
? dto.data.segments ? dto.data.segments
@ -583,7 +577,7 @@ export default class ExportImportService
} }
private async remapSegments(dto: ImportTogglesSchema) { private async remapSegments(dto: ImportTogglesSchema) {
const existingSegments = await this.segmentService.getAll(); const existingSegments = await this.segmentReadModel.getAll();
const segmentMapping = new Map( const segmentMapping = new Map(
dto.data.segments?.map((segment) => [ dto.data.segments?.map((segment) => [
@ -820,10 +814,10 @@ export default class ExportImportService
featureNames, featureNames,
query.environment, query.environment,
), ),
this.segmentStore.getAllFeatureStrategySegments(), this.segmentReadModel.getAllFeatureStrategySegments(),
this.contextFieldStore.getAll(), this.contextFieldStore.getAll(),
this.featureTagStore.getAllByFeatures(featureNames), this.featureTagStore.getAllByFeatures(featureNames),
this.segmentStore.getAll(), this.segmentReadModel.getAll(),
this.tagTypeStore.getAll(), this.tagTypeStore.getAll(),
this.dependentFeaturesReadModel.getDependencies(featureNames), this.dependentFeaturesReadModel.getDependencies(featureNames),
]); ]);

View File

@ -0,0 +1,12 @@
import { IFeatureStrategySegment, ISegment } from '../../types';
import { ISegmentReadModel } from './segment-read-model-type';
export class FakeSegmentReadModel implements ISegmentReadModel {
async getAll(): Promise<ISegment[]> {
return [];
}
async getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]> {
return [];
}
}

View File

@ -0,0 +1,6 @@
import { IFeatureStrategySegment, ISegment } from '../../types';
export interface ISegmentReadModel {
getAll(): Promise<ISegment[]>;
getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]>;
}

View File

@ -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<ISegment[]> {
const rows: ISegmentRow[] = await this.db
.select(this.prefixColumns())
.from('segments')
.orderBy('segments.name', 'asc');
return rows.map(this.mapRow);
}
async getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]> {
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,
}));
}
}