mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
refactor: playground uses segment read model (#6424)
This commit is contained in:
parent
62dc77db16
commit
095b6eca84
@ -4,22 +4,28 @@ import { IUnleashServices } from '../../types/services';
|
|||||||
import { ALL } from '../../types/models/api-token';
|
import { ALL } from '../../types/models/api-token';
|
||||||
import { PlaygroundFeatureSchema } from '../../openapi/spec/playground-feature-schema';
|
import { PlaygroundFeatureSchema } from '../../openapi/spec/playground-feature-schema';
|
||||||
import { Logger } from '../../logger';
|
import { Logger } from '../../logger';
|
||||||
import { IFlagResolver, ISegment, IUnleashConfig } from '../../types';
|
import {
|
||||||
|
IFlagResolver,
|
||||||
|
ISegment,
|
||||||
|
ISegmentReadModel,
|
||||||
|
IUnleashConfig,
|
||||||
|
} from '../../types';
|
||||||
import { offlineUnleashClient } from './offline-unleash-client';
|
import { offlineUnleashClient } from './offline-unleash-client';
|
||||||
import { FeatureInterface } from '../../features/playground/feature-evaluator/feature';
|
import { FeatureInterface } from '../../features/playground/feature-evaluator/feature';
|
||||||
import {
|
import {
|
||||||
EvaluatedPlaygroundStrategy,
|
EvaluatedPlaygroundStrategy,
|
||||||
FeatureStrategiesEvaluationResult,
|
FeatureStrategiesEvaluationResult,
|
||||||
} from '../../features/playground/feature-evaluator/client';
|
} 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 { FeatureConfigurationClient } from '../feature-toggle/types/feature-toggle-strategies-store-type';
|
||||||
import { generateObjectCombinations } from './generateObjectCombinations';
|
import { generateObjectCombinations } from './generateObjectCombinations';
|
||||||
import groupBy from 'lodash.groupby';
|
import groupBy from 'lodash.groupby';
|
||||||
import { omitKeys } from '../../util';
|
import { omitKeys } from '../../util';
|
||||||
import { AdvancedPlaygroundFeatureSchema } from '../../openapi';
|
import {
|
||||||
|
AdvancedPlaygroundFeatureSchema,
|
||||||
|
playgroundStrategyEvaluation,
|
||||||
|
} from '../../openapi';
|
||||||
import { AdvancedPlaygroundEnvironmentFeatureSchema } from '../../openapi/spec/advanced-playground-environment-feature-schema';
|
import { AdvancedPlaygroundEnvironmentFeatureSchema } from '../../openapi/spec/advanced-playground-environment-feature-schema';
|
||||||
import { validateQueryComplexity } from './validateQueryComplexity';
|
import { validateQueryComplexity } from './validateQueryComplexity';
|
||||||
import { playgroundStrategyEvaluation } from '../../openapi';
|
|
||||||
import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
|
import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
|
||||||
import { getDefaultVariant } from './feature-evaluator/variant';
|
import { getDefaultVariant } from './feature-evaluator/variant';
|
||||||
|
|
||||||
@ -66,30 +72,28 @@ export class PlaygroundService {
|
|||||||
|
|
||||||
private readonly featureToggleService: FeatureToggleService;
|
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(
|
constructor(
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
{
|
{
|
||||||
featureToggleServiceV2,
|
featureToggleServiceV2,
|
||||||
segmentService,
|
|
||||||
privateProjectChecker,
|
privateProjectChecker,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashServices,
|
IUnleashServices,
|
||||||
| 'featureToggleServiceV2'
|
'featureToggleServiceV2' | 'privateProjectChecker'
|
||||||
| 'segmentService'
|
|
||||||
| 'privateProjectChecker'
|
|
||||||
>,
|
>,
|
||||||
|
segmentReadModel: ISegmentReadModel,
|
||||||
) {
|
) {
|
||||||
this.logger = config.getLogger('services/playground-service.ts');
|
this.logger = config.getLogger('services/playground-service.ts');
|
||||||
this.flagResolver = config.flagResolver;
|
this.flagResolver = config.flagResolver;
|
||||||
this.featureToggleService = featureToggleServiceV2;
|
this.featureToggleService = featureToggleServiceV2;
|
||||||
this.segmentService = segmentService;
|
|
||||||
this.privateProjectChecker = privateProjectChecker;
|
this.privateProjectChecker = privateProjectChecker;
|
||||||
|
this.segmentReadModel = segmentReadModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateAdvancedQuery(
|
async evaluateAdvancedQuery(
|
||||||
@ -99,7 +103,7 @@ export class PlaygroundService {
|
|||||||
limit: number,
|
limit: number,
|
||||||
userId: number,
|
userId: number,
|
||||||
): Promise<AdvancedPlaygroundFeatureEvaluationResult[]> {
|
): Promise<AdvancedPlaygroundFeatureEvaluationResult[]> {
|
||||||
const segments = await this.segmentService.getActive();
|
const segments = await this.segmentReadModel.getActive();
|
||||||
|
|
||||||
let filteredProjects: typeof projects = projects;
|
let filteredProjects: typeof projects = projects;
|
||||||
|
|
||||||
@ -266,7 +270,7 @@ export class PlaygroundService {
|
|||||||
): Promise<PlaygroundFeatureEvaluationResult[]> {
|
): Promise<PlaygroundFeatureEvaluationResult[]> {
|
||||||
const [{ features, featureProject }, segments] = await Promise.all([
|
const [{ features, featureProject }, segments] = await Promise.all([
|
||||||
this.resolveFeatures(projects, environment),
|
this.resolveFeatures(projects, environment),
|
||||||
this.segmentService.getActive(),
|
this.segmentReadModel.getActive(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await this.evaluate({
|
const result = await this.evaluate({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IFeatureStrategySegment, ISegment } from '../../types';
|
import { IClientSegment, IFeatureStrategySegment, ISegment } from '../../types';
|
||||||
import { ISegmentReadModel } from './segment-read-model-type';
|
import { ISegmentReadModel } from './segment-read-model-type';
|
||||||
|
|
||||||
export class FakeSegmentReadModel implements ISegmentReadModel {
|
export class FakeSegmentReadModel implements ISegmentReadModel {
|
||||||
@ -9,4 +9,12 @@ export class FakeSegmentReadModel implements ISegmentReadModel {
|
|||||||
async getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]> {
|
async getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getActive(): Promise<ISegment[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getActiveForClient(): Promise<IClientSegment[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { IFeatureStrategySegment, ISegment } from '../../types';
|
import { IClientSegment, IFeatureStrategySegment, ISegment } from '../../types';
|
||||||
|
|
||||||
export interface ISegmentReadModel {
|
export interface ISegmentReadModel {
|
||||||
getAll(): Promise<ISegment[]>;
|
getAll(): Promise<ISegment[]>;
|
||||||
getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]>;
|
getAllFeatureStrategySegments(): Promise<IFeatureStrategySegment[]>;
|
||||||
|
getActive(): Promise<ISegment[]>;
|
||||||
|
getActiveForClient(): Promise<IClientSegment[]>;
|
||||||
}
|
}
|
||||||
|
@ -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 { ISegmentReadModel } from './segment-read-model-type';
|
||||||
import NotFoundError from '../../error/notfound-error';
|
import NotFoundError from '../../error/notfound-error';
|
||||||
import { Db } from '../../db/db';
|
import { Db } from '../../db/db';
|
||||||
@ -75,4 +80,28 @@ export class SegmentReadModel implements ISegmentReadModel {
|
|||||||
segmentId: row.segment_id,
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,6 @@ export interface ISegmentService {
|
|||||||
|
|
||||||
validateName(name: string): Promise<void>;
|
validateName(name: string): Promise<void>;
|
||||||
|
|
||||||
getActive(): Promise<ISegment[]>;
|
|
||||||
|
|
||||||
getActiveForClient(): Promise<IClientSegment[]>;
|
getActiveForClient(): Promise<IClientSegment[]>;
|
||||||
|
|
||||||
getAll(): Promise<ISegment[]>;
|
getAll(): Promise<ISegment[]>;
|
||||||
|
@ -109,6 +109,8 @@ import {
|
|||||||
createInstanceStatsService,
|
createInstanceStatsService,
|
||||||
} from '../features/instance-stats/createInstanceStatsService';
|
} from '../features/instance-stats/createInstanceStatsService';
|
||||||
import { InactiveUsersService } from '../users/inactive/inactive-users-service';
|
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 = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
@ -138,6 +140,9 @@ export const createServices = (
|
|||||||
const dependentFeaturesReadModel = db
|
const dependentFeaturesReadModel = db
|
||||||
? new DependentFeaturesReadModel(db)
|
? new DependentFeaturesReadModel(db)
|
||||||
: new FakeDependentFeaturesReadModel();
|
: new FakeDependentFeaturesReadModel();
|
||||||
|
const segmentReadModel = db
|
||||||
|
? new SegmentReadModel(db)
|
||||||
|
: new FakeSegmentReadModel();
|
||||||
|
|
||||||
const contextService = new ContextService(
|
const contextService = new ContextService(
|
||||||
stores,
|
stores,
|
||||||
@ -270,11 +275,14 @@ export const createServices = (
|
|||||||
const userSplashService = new UserSplashService(stores, config);
|
const userSplashService = new UserSplashService(stores, config);
|
||||||
const openApiService = new OpenApiService(config);
|
const openApiService = new OpenApiService(config);
|
||||||
const clientSpecService = new ClientSpecService(config);
|
const clientSpecService = new ClientSpecService(config);
|
||||||
const playgroundService = new PlaygroundService(config, {
|
const playgroundService = new PlaygroundService(
|
||||||
|
config,
|
||||||
|
{
|
||||||
featureToggleServiceV2,
|
featureToggleServiceV2,
|
||||||
segmentService,
|
|
||||||
privateProjectChecker,
|
privateProjectChecker,
|
||||||
});
|
},
|
||||||
|
segmentReadModel,
|
||||||
|
);
|
||||||
|
|
||||||
const configurationRevisionService = new ConfigurationRevisionService(
|
const configurationRevisionService = new ConfigurationRevisionService(
|
||||||
stores,
|
stores,
|
||||||
|
@ -79,10 +79,6 @@ export class SegmentService implements ISegmentService {
|
|||||||
return this.segmentStore.getAll(this.config.isEnterprise);
|
return this.segmentStore.getAll(this.config.isEnterprise);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getActive(): Promise<ISegment[]> {
|
|
||||||
return this.segmentStore.getActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getActiveForClient(): Promise<IClientSegment[]> {
|
async getActiveForClient(): Promise<IClientSegment[]> {
|
||||||
return this.segmentStore.getActiveForClient();
|
return this.segmentStore.getActiveForClient();
|
||||||
}
|
}
|
||||||
|
@ -377,7 +377,7 @@ test(`should import segments and connect them to feature strategies`, async () =
|
|||||||
.expect(202);
|
.expect(202);
|
||||||
|
|
||||||
const allSegments = await app.services.segmentService.getAll();
|
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(allSegments.length).toEqual(2);
|
||||||
expect(collectIds(allSegments)).toEqual([1, 2]);
|
expect(collectIds(allSegments)).toEqual([1, 2]);
|
||||||
|
@ -20,18 +20,14 @@ import { SdkContextSchema } from '../../../lib/openapi/spec/sdk-context-schema';
|
|||||||
import { SegmentSchema } from '../../../lib/openapi/spec/segment-schema';
|
import { SegmentSchema } from '../../../lib/openapi/spec/segment-schema';
|
||||||
import { playgroundStrategyEvaluation } from '../../../lib/openapi/spec/playground-strategy-schema';
|
import { playgroundStrategyEvaluation } from '../../../lib/openapi/spec/playground-strategy-schema';
|
||||||
import { PlaygroundSegmentSchema } from '../../../lib/openapi/spec/playground-segment-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 { createPrivateProjectChecker } from '../../../lib/features/private-project/createPrivateProjectChecker';
|
||||||
import {
|
import { createFeatureToggleService } from '../../../lib/features';
|
||||||
createFeatureToggleService,
|
import { SegmentReadModel } from '../../../lib/features/segment/segment-read-model';
|
||||||
createSegmentService,
|
|
||||||
} from '../../../lib/features';
|
|
||||||
|
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
let service: PlaygroundService;
|
let service: PlaygroundService;
|
||||||
let featureToggleService: FeatureToggleService;
|
let featureToggleService: FeatureToggleService;
|
||||||
let segmentService: ISegmentService;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const config = createTestConfig();
|
const config = createTestConfig();
|
||||||
@ -41,14 +37,17 @@ beforeAll(async () => {
|
|||||||
db.rawDatabase,
|
db.rawDatabase,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
segmentService = createSegmentService(db.rawDatabase, config);
|
const segmentReadModel = new SegmentReadModel(db.rawDatabase);
|
||||||
|
|
||||||
featureToggleService = createFeatureToggleService(db.rawDatabase, config);
|
featureToggleService = createFeatureToggleService(db.rawDatabase, config);
|
||||||
service = new PlaygroundService(config, {
|
service = new PlaygroundService(
|
||||||
|
config,
|
||||||
|
{
|
||||||
featureToggleServiceV2: featureToggleService,
|
featureToggleServiceV2: featureToggleService,
|
||||||
segmentService,
|
|
||||||
privateProjectChecker,
|
privateProjectChecker,
|
||||||
});
|
},
|
||||||
|
segmentReadModel,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user