1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-21 13:47:39 +02:00

refactor: playground uses segment read model (#6424)

This commit is contained in:
Mateusz Kwasniewski 2024-03-05 08:34:26 +01:00 committed by GitHub
parent 62dc77db16
commit 095b6eca84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 85 additions and 41 deletions

View File

@ -4,22 +4,28 @@ import { IUnleashServices } from '../../types/services';
import { ALL } from '../../types/models/api-token';
import { PlaygroundFeatureSchema } from '../../openapi/spec/playground-feature-schema';
import { Logger } from '../../logger';
import { IFlagResolver, ISegment, IUnleashConfig } from '../../types';
import {
IFlagResolver,
ISegment,
ISegmentReadModel,
IUnleashConfig,
} from '../../types';
import { offlineUnleashClient } from './offline-unleash-client';
import { FeatureInterface } from '../../features/playground/feature-evaluator/feature';
import {
EvaluatedPlaygroundStrategy,
FeatureStrategiesEvaluationResult,
} from '../../features/playground/feature-evaluator/client';
import { ISegmentService } from '../../segments/segment-service-interface';
import { FeatureConfigurationClient } from '../feature-toggle/types/feature-toggle-strategies-store-type';
import { generateObjectCombinations } from './generateObjectCombinations';
import groupBy from 'lodash.groupby';
import { omitKeys } from '../../util';
import { AdvancedPlaygroundFeatureSchema } from '../../openapi';
import {
AdvancedPlaygroundFeatureSchema,
playgroundStrategyEvaluation,
} from '../../openapi';
import { AdvancedPlaygroundEnvironmentFeatureSchema } from '../../openapi/spec/advanced-playground-environment-feature-schema';
import { validateQueryComplexity } from './validateQueryComplexity';
import { playgroundStrategyEvaluation } from '../../openapi';
import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
import { getDefaultVariant } from './feature-evaluator/variant';
@ -66,30 +72,28 @@ export class PlaygroundService {
private readonly featureToggleService: FeatureToggleService;
private readonly segmentService: ISegmentService;
private readonly flagResolver: IFlagResolver;
private flagResolver: IFlagResolver;
private readonly privateProjectChecker: IPrivateProjectChecker;
private privateProjectChecker: IPrivateProjectChecker;
private readonly segmentReadModel: ISegmentReadModel;
constructor(
config: IUnleashConfig,
{
featureToggleServiceV2,
segmentService,
privateProjectChecker,
}: Pick<
IUnleashServices,
| 'featureToggleServiceV2'
| 'segmentService'
| 'privateProjectChecker'
'featureToggleServiceV2' | 'privateProjectChecker'
>,
segmentReadModel: ISegmentReadModel,
) {
this.logger = config.getLogger('services/playground-service.ts');
this.flagResolver = config.flagResolver;
this.featureToggleService = featureToggleServiceV2;
this.segmentService = segmentService;
this.privateProjectChecker = privateProjectChecker;
this.segmentReadModel = segmentReadModel;
}
async evaluateAdvancedQuery(
@ -99,7 +103,7 @@ export class PlaygroundService {
limit: number,
userId: number,
): Promise<AdvancedPlaygroundFeatureEvaluationResult[]> {
const segments = await this.segmentService.getActive();
const segments = await this.segmentReadModel.getActive();
let filteredProjects: typeof projects = projects;
@ -266,7 +270,7 @@ export class PlaygroundService {
): Promise<PlaygroundFeatureEvaluationResult[]> {
const [{ features, featureProject }, segments] = await Promise.all([
this.resolveFeatures(projects, environment),
this.segmentService.getActive(),
this.segmentReadModel.getActive(),
]);
const result = await this.evaluate({

View File

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

View File

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

View File

@ -1,4 +1,9 @@
import { IConstraint, IFeatureStrategySegment, ISegment } from '../../types';
import {
IClientSegment,
IConstraint,
IFeatureStrategySegment,
ISegment,
} from '../../types';
import { ISegmentReadModel } from './segment-read-model-type';
import NotFoundError from '../../error/notfound-error';
import { Db } from '../../db/db';
@ -75,4 +80,28 @@ export class SegmentReadModel implements ISegmentReadModel {
segmentId: row.segment_id,
}));
}
async getActive(): Promise<ISegment[]> {
const rows: ISegmentRow[] = await this.db
.distinct(this.prefixColumns())
.from('segments')
.orderBy('name', 'asc')
.join(
'feature_strategy_segment',
'feature_strategy_segment.segment_id',
'segments.id',
);
return rows.map(this.mapRow);
}
async getActiveForClient(): Promise<IClientSegment[]> {
const fullSegments = await this.getActive();
return fullSegments.map((segments) => ({
id: segments.id,
name: segments.name,
constraints: segments.constraints,
}));
}
}

View File

@ -33,8 +33,6 @@ export interface ISegmentService {
validateName(name: string): Promise<void>;
getActive(): Promise<ISegment[]>;
getActiveForClient(): Promise<IClientSegment[]>;
getAll(): Promise<ISegment[]>;

View File

@ -109,6 +109,8 @@ import {
createInstanceStatsService,
} from '../features/instance-stats/createInstanceStatsService';
import { InactiveUsersService } from '../users/inactive/inactive-users-service';
import { SegmentReadModel } from '../features/segment/segment-read-model';
import { FakeSegmentReadModel } from '../features/segment/fake-segment-read-model';
export const createServices = (
stores: IUnleashStores,
@ -138,6 +140,9 @@ export const createServices = (
const dependentFeaturesReadModel = db
? new DependentFeaturesReadModel(db)
: new FakeDependentFeaturesReadModel();
const segmentReadModel = db
? new SegmentReadModel(db)
: new FakeSegmentReadModel();
const contextService = new ContextService(
stores,
@ -270,11 +275,14 @@ export const createServices = (
const userSplashService = new UserSplashService(stores, config);
const openApiService = new OpenApiService(config);
const clientSpecService = new ClientSpecService(config);
const playgroundService = new PlaygroundService(config, {
featureToggleServiceV2,
segmentService,
privateProjectChecker,
});
const playgroundService = new PlaygroundService(
config,
{
featureToggleServiceV2,
privateProjectChecker,
},
segmentReadModel,
);
const configurationRevisionService = new ConfigurationRevisionService(
stores,

View File

@ -79,10 +79,6 @@ export class SegmentService implements ISegmentService {
return this.segmentStore.getAll(this.config.isEnterprise);
}
async getActive(): Promise<ISegment[]> {
return this.segmentStore.getActive();
}
async getActiveForClient(): Promise<IClientSegment[]> {
return this.segmentStore.getActiveForClient();
}

View File

@ -377,7 +377,7 @@ test(`should import segments and connect them to feature strategies`, async () =
.expect(202);
const allSegments = await app.services.segmentService.getAll();
const activeSegments = await app.services.segmentService.getActive();
const activeSegments = await db.stores.segmentReadModel.getActive();
expect(allSegments.length).toEqual(2);
expect(collectIds(allSegments)).toEqual([1, 2]);

View File

@ -20,18 +20,14 @@ import { SdkContextSchema } from '../../../lib/openapi/spec/sdk-context-schema';
import { SegmentSchema } from '../../../lib/openapi/spec/segment-schema';
import { playgroundStrategyEvaluation } from '../../../lib/openapi/spec/playground-strategy-schema';
import { PlaygroundSegmentSchema } from '../../../lib/openapi/spec/playground-segment-schema';
import { ISegmentService } from '../../../lib/segments/segment-service-interface';
import { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
import {
createFeatureToggleService,
createSegmentService,
} from '../../../lib/features';
import { createFeatureToggleService } from '../../../lib/features';
import { SegmentReadModel } from '../../../lib/features/segment/segment-read-model';
let stores: IUnleashStores;
let db: ITestDb;
let service: PlaygroundService;
let featureToggleService: FeatureToggleService;
let segmentService: ISegmentService;
beforeAll(async () => {
const config = createTestConfig();
@ -41,14 +37,17 @@ beforeAll(async () => {
db.rawDatabase,
config,
);
segmentService = createSegmentService(db.rawDatabase, config);
const segmentReadModel = new SegmentReadModel(db.rawDatabase);
featureToggleService = createFeatureToggleService(db.rawDatabase, config);
service = new PlaygroundService(config, {
featureToggleServiceV2: featureToggleService,
segmentService,
privateProjectChecker,
});
service = new PlaygroundService(
config,
{
featureToggleServiceV2: featureToggleService,
privateProjectChecker,
},
segmentReadModel,
);
});
afterAll(async () => {