mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: client api with proper client segments and strategy variants (#4244)
This commit is contained in:
parent
4cd4153412
commit
e8ea79c967
@ -6,6 +6,7 @@ import {
|
|||||||
IFeatureToggleClient,
|
IFeatureToggleClient,
|
||||||
IFeatureToggleClientStore,
|
IFeatureToggleClientStore,
|
||||||
IFeatureToggleQuery,
|
IFeatureToggleQuery,
|
||||||
|
IFlagResolver,
|
||||||
IStrategyConfig,
|
IStrategyConfig,
|
||||||
ITag,
|
ITag,
|
||||||
PartialDeep,
|
PartialDeep,
|
||||||
@ -38,7 +39,14 @@ export default class FeatureToggleClientStore
|
|||||||
|
|
||||||
private timer: Function;
|
private timer: Function;
|
||||||
|
|
||||||
constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) {
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
db: Db,
|
||||||
|
eventBus: EventEmitter,
|
||||||
|
getLogger: LogProvider,
|
||||||
|
flagResolver: IFlagResolver,
|
||||||
|
) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.logger = getLogger('feature-toggle-client-store.ts');
|
this.logger = getLogger('feature-toggle-client-store.ts');
|
||||||
this.timer = (action) =>
|
this.timer = (action) =>
|
||||||
@ -46,6 +54,7 @@ export default class FeatureToggleClientStore
|
|||||||
store: 'feature-toggle',
|
store: 'feature-toggle',
|
||||||
action,
|
action,
|
||||||
});
|
});
|
||||||
|
this.flagResolver = flagResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAll({
|
private async getAll({
|
||||||
@ -78,6 +87,7 @@ export default class FeatureToggleClientStore
|
|||||||
'fs.parameters as parameters',
|
'fs.parameters as parameters',
|
||||||
'fs.constraints as constraints',
|
'fs.constraints as constraints',
|
||||||
'fs.sort_order as sort_order',
|
'fs.sort_order as sort_order',
|
||||||
|
'fs.variants as strategy_variants',
|
||||||
'segments.id as segment_id',
|
'segments.id as segment_id',
|
||||||
'segments.constraints as segment_constraints',
|
'segments.constraints as segment_constraints',
|
||||||
] as (string | Raw<any>)[];
|
] as (string | Raw<any>)[];
|
||||||
@ -170,9 +180,7 @@ export default class FeatureToggleClientStore
|
|||||||
strategies: [],
|
strategies: [],
|
||||||
};
|
};
|
||||||
if (this.isUnseenStrategyRow(feature, r) && !r.strategy_disabled) {
|
if (this.isUnseenStrategyRow(feature, r) && !r.strategy_disabled) {
|
||||||
feature.strategies?.push(
|
feature.strategies?.push(this.rowToStrategy(r));
|
||||||
FeatureToggleClientStore.rowToStrategy(r),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (this.isNewTag(feature, r)) {
|
if (this.isNewTag(feature, r)) {
|
||||||
this.addTag(feature, r);
|
this.addTag(feature, r);
|
||||||
@ -233,8 +241,8 @@ export default class FeatureToggleClientStore
|
|||||||
return cleanedFeatures;
|
return cleanedFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static rowToStrategy(row: Record<string, any>): IStrategyConfig {
|
private rowToStrategy(row: Record<string, any>): IStrategyConfig {
|
||||||
return {
|
const strategy: IStrategyConfig = {
|
||||||
id: row.strategy_id,
|
id: row.strategy_id,
|
||||||
name: row.strategy_name,
|
name: row.strategy_name,
|
||||||
title: row.strategy_title,
|
title: row.strategy_title,
|
||||||
@ -242,6 +250,10 @@ export default class FeatureToggleClientStore
|
|||||||
parameters: mapValues(row.parameters || {}, ensureStringValue),
|
parameters: mapValues(row.parameters || {}, ensureStringValue),
|
||||||
sortOrder: row.sort_order,
|
sortOrder: row.sort_order,
|
||||||
};
|
};
|
||||||
|
if (this.flagResolver.isEnabled('strategyVariant')) {
|
||||||
|
strategy.variants = row.strategy_variants || [];
|
||||||
|
}
|
||||||
|
return strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static rowToTag(row: Record<string, any>): ITag {
|
private static rowToTag(row: Record<string, any>): ITag {
|
||||||
|
@ -93,6 +93,7 @@ export const createStores = (
|
|||||||
db,
|
db,
|
||||||
eventBus,
|
eventBus,
|
||||||
getLogger,
|
getLogger,
|
||||||
|
config.flagResolver,
|
||||||
),
|
),
|
||||||
environmentStore: new EnvironmentStore(db, eventBus, getLogger),
|
environmentStore: new EnvironmentStore(db, eventBus, getLogger),
|
||||||
featureTagStore: new FeatureTagStore(db, eventBus, getLogger),
|
featureTagStore: new FeatureTagStore(db, eventBus, getLogger),
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { ISegmentStore } from '../types/stores/segment-store';
|
import { ISegmentStore } from '../types/stores/segment-store';
|
||||||
import { IConstraint, IFeatureStrategySegment, ISegment } from '../types/model';
|
import {
|
||||||
|
IClientSegment,
|
||||||
|
IConstraint,
|
||||||
|
IFeatureStrategySegment,
|
||||||
|
ISegment,
|
||||||
|
} from '../types/model';
|
||||||
import { Logger, LogProvider } from '../logger';
|
import { Logger, LogProvider } from '../logger';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import NotFoundError from '../error/notfound-error';
|
import NotFoundError from '../error/notfound-error';
|
||||||
@ -150,6 +155,16 @@ export default class SegmentStore implements ISegmentStore {
|
|||||||
return rows.map(this.mapRow);
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async getByStrategy(strategyId: string): Promise<ISegment[]> {
|
async getByStrategy(strategyId: string): Promise<ISegment[]> {
|
||||||
const rows = await this.db
|
const rows = await this.db
|
||||||
.select(this.prefixColumns())
|
.select(this.prefixColumns())
|
||||||
|
@ -55,6 +55,7 @@ export const createFeatureToggleService = (
|
|||||||
db,
|
db,
|
||||||
eventBus,
|
eventBus,
|
||||||
getLogger,
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
);
|
);
|
||||||
const projectStore = new ProjectStore(
|
const projectStore = new ProjectStore(
|
||||||
db,
|
db,
|
||||||
|
@ -150,6 +150,7 @@ import {
|
|||||||
telemetrySettingsSchema,
|
telemetrySettingsSchema,
|
||||||
strategyVariantSchema,
|
strategyVariantSchema,
|
||||||
createStrategyVariantSchema,
|
createStrategyVariantSchema,
|
||||||
|
clientSegmentSchema,
|
||||||
} from './spec';
|
} from './spec';
|
||||||
import { IServerOption } from '../types';
|
import { IServerOption } from '../types';
|
||||||
import { mapValues, omitKeys } from '../util';
|
import { mapValues, omitKeys } from '../util';
|
||||||
@ -357,6 +358,7 @@ export const schemas: UnleashSchemas = {
|
|||||||
telemetrySettingsSchema,
|
telemetrySettingsSchema,
|
||||||
strategyVariantSchema,
|
strategyVariantSchema,
|
||||||
createStrategyVariantSchema,
|
createStrategyVariantSchema,
|
||||||
|
clientSegmentSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove JSONSchema keys that would result in an invalid OpenAPI spec.
|
// Remove JSONSchema keys that would result in an invalid OpenAPI spec.
|
||||||
|
@ -151,7 +151,6 @@ test('clientFeaturesSchema unleash-proxy expected response', () => {
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "some-name",
|
"name": "some-name",
|
||||||
"description": null,
|
|
||||||
"constraints": [
|
"constraints": [
|
||||||
{
|
{
|
||||||
"contextName": "some-name",
|
"contextName": "some-name",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { clientFeaturesQuerySchema } from './client-features-query-schema';
|
import { clientFeaturesQuerySchema } from './client-features-query-schema';
|
||||||
import { segmentSchema } from './segment-schema';
|
import { clientSegmentSchema } from './client-segment-schema';
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
import { environmentSchema } from './environment-schema';
|
import { environmentSchema } from './environment-schema';
|
||||||
import { overrideSchema } from './override-schema';
|
import { overrideSchema } from './override-schema';
|
||||||
@ -36,7 +36,7 @@ export const clientFeaturesSchema = {
|
|||||||
'A list of [Segments](https://docs.getunleash.io/reference/segments) configured for this Unleash instance',
|
'A list of [Segments](https://docs.getunleash.io/reference/segments) configured for this Unleash instance',
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
$ref: '#/components/schemas/segmentSchema',
|
$ref: '#/components/schemas/clientSegmentSchema',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
@ -50,7 +50,7 @@ export const clientFeaturesSchema = {
|
|||||||
constraintSchema,
|
constraintSchema,
|
||||||
clientFeatureSchema,
|
clientFeatureSchema,
|
||||||
environmentSchema,
|
environmentSchema,
|
||||||
segmentSchema,
|
clientSegmentSchema,
|
||||||
clientFeaturesQuerySchema,
|
clientFeaturesQuerySchema,
|
||||||
overrideSchema,
|
overrideSchema,
|
||||||
parametersSchema,
|
parametersSchema,
|
||||||
|
37
src/lib/openapi/spec/client-segment-schema.ts
Normal file
37
src/lib/openapi/spec/client-segment-schema.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
import { constraintSchema } from './constraint-schema';
|
||||||
|
|
||||||
|
export const clientSegmentSchema = {
|
||||||
|
$id: '#/components/schemas/clientSegmentSchema',
|
||||||
|
type: 'object',
|
||||||
|
description:
|
||||||
|
'Represents a client API segment of users defined by a set of constraints.',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['id', 'constraints'],
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'number',
|
||||||
|
description: "The segment's id.",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The name of the segment.',
|
||||||
|
example: 'segment A',
|
||||||
|
},
|
||||||
|
constraints: {
|
||||||
|
type: 'array',
|
||||||
|
description:
|
||||||
|
'List of constraints that determine which users are part of the segment',
|
||||||
|
items: {
|
||||||
|
$ref: '#/components/schemas/constraintSchema',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
constraintSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type ClientSegmentSchema = FromSchema<typeof clientSegmentSchema>;
|
@ -149,3 +149,4 @@ export * from './advanced-playground-request-schema';
|
|||||||
export * from './telemetry-settings-schema';
|
export * from './telemetry-settings-schema';
|
||||||
export * from './create-strategy-variant-schema';
|
export * from './create-strategy-variant-schema';
|
||||||
export * from './strategy-variant-schema';
|
export * from './strategy-variant-schema';
|
||||||
|
export * from './client-segment-schema';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { constraintSchema } from './constraint-schema';
|
import { constraintSchema } from './constraint-schema';
|
||||||
|
import { clientSegmentSchema } from './client-segment-schema';
|
||||||
|
|
||||||
export const segmentSchema = {
|
export const segmentSchema = {
|
||||||
$id: '#/components/schemas/segmentSchema',
|
$id: '#/components/schemas/segmentSchema',
|
||||||
@ -9,28 +10,30 @@ export const segmentSchema = {
|
|||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['id', 'constraints'],
|
required: ['id', 'constraints'],
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
...clientSegmentSchema.properties,
|
||||||
type: 'number',
|
|
||||||
description: "The segment's id.",
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'The name of the segment.',
|
|
||||||
example: 'segment A',
|
|
||||||
},
|
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: 'The description of the segment.',
|
description: 'The description of the segment.',
|
||||||
example: 'Segment A description',
|
example: 'Segment A description',
|
||||||
},
|
},
|
||||||
constraints: {
|
createdAt: {
|
||||||
type: 'array',
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
description:
|
description:
|
||||||
'List of constraints that determine which users are part of the segment',
|
'The time the segment was created as a RFC 3339-conformant timestamp.',
|
||||||
items: {
|
example: '2023-07-05T12:56:00.000Z',
|
||||||
$ref: '#/components/schemas/constraintSchema',
|
},
|
||||||
},
|
createdBy: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Which user created this segment',
|
||||||
|
example: 'johndoe',
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The project the segment relates to, if applicable.',
|
||||||
|
example: 'default',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -34,7 +34,10 @@ const callGetAll = async (controller: FeatureController) => {
|
|||||||
await controller.getAll(
|
await controller.getAll(
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
{ query: {}, header: () => undefined, headers: {} },
|
{ query: {}, header: () => undefined, headers: {} },
|
||||||
{ json: () => {}, setHeader: () => undefined },
|
{
|
||||||
|
json: () => {},
|
||||||
|
setHeader: () => undefined,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,12 +79,13 @@ test('should get empty getFeatures via client', () => {
|
|||||||
test('if caching is enabled should memoize', async () => {
|
test('if caching is enabled should memoize', async () => {
|
||||||
const getClientFeatures = jest.fn().mockReturnValue([]);
|
const getClientFeatures = jest.fn().mockReturnValue([]);
|
||||||
const getActive = jest.fn().mockReturnValue([]);
|
const getActive = jest.fn().mockReturnValue([]);
|
||||||
|
const getActiveForClient = jest.fn().mockReturnValue([]);
|
||||||
const respondWithValidation = jest.fn().mockReturnValue({});
|
const respondWithValidation = jest.fn().mockReturnValue({});
|
||||||
const validPath = jest.fn().mockReturnValue(jest.fn());
|
const validPath = jest.fn().mockReturnValue(jest.fn());
|
||||||
const clientSpecService = new ClientSpecService({ getLogger });
|
const clientSpecService = new ClientSpecService({ getLogger });
|
||||||
const openApiService = { respondWithValidation, validPath };
|
const openApiService = { respondWithValidation, validPath };
|
||||||
const featureToggleServiceV2 = { getClientFeatures };
|
const featureToggleServiceV2 = { getClientFeatures };
|
||||||
const segmentService = { getActive };
|
const segmentService = { getActive, getActiveForClient };
|
||||||
const configurationRevisionService = { getMaxRevisionId: () => 1 };
|
const configurationRevisionService = { getMaxRevisionId: () => 1 };
|
||||||
|
|
||||||
const controller = new FeatureController(
|
const controller = new FeatureController(
|
||||||
@ -114,11 +118,12 @@ test('if caching is enabled should memoize', async () => {
|
|||||||
test('if caching is not enabled all calls goes to service', async () => {
|
test('if caching is not enabled all calls goes to service', async () => {
|
||||||
const getClientFeatures = jest.fn().mockReturnValue([]);
|
const getClientFeatures = jest.fn().mockReturnValue([]);
|
||||||
const getActive = jest.fn().mockReturnValue([]);
|
const getActive = jest.fn().mockReturnValue([]);
|
||||||
|
const getActiveForClient = jest.fn().mockReturnValue([]);
|
||||||
const respondWithValidation = jest.fn().mockReturnValue({});
|
const respondWithValidation = jest.fn().mockReturnValue({});
|
||||||
const validPath = jest.fn().mockReturnValue(jest.fn());
|
const validPath = jest.fn().mockReturnValue(jest.fn());
|
||||||
const clientSpecService = new ClientSpecService({ getLogger });
|
const clientSpecService = new ClientSpecService({ getLogger });
|
||||||
const featureToggleServiceV2 = { getClientFeatures };
|
const featureToggleServiceV2 = { getClientFeatures };
|
||||||
const segmentService = { getActive };
|
const segmentService = { getActive, getActiveForClient };
|
||||||
const openApiService = { respondWithValidation, validPath };
|
const openApiService = { respondWithValidation, validPath };
|
||||||
const configurationRevisionService = { getMaxRevisionId: () => 1 };
|
const configurationRevisionService = { getMaxRevisionId: () => 1 };
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ import { Response } from 'express';
|
|||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import hashSum from 'hash-sum';
|
import hashSum from 'hash-sum';
|
||||||
import Controller from '../controller';
|
import Controller from '../controller';
|
||||||
import { IUnleashConfig, IUnleashServices } from '../../types';
|
import { IClientSegment, IUnleashConfig, IUnleashServices } from '../../types';
|
||||||
import FeatureToggleService from '../../services/feature-toggle-service';
|
import FeatureToggleService from '../../services/feature-toggle-service';
|
||||||
import { Logger } from '../../logger';
|
import { Logger } from '../../logger';
|
||||||
import { querySchema } from '../../schema/feature-schema';
|
import { querySchema } from '../../schema/feature-schema';
|
||||||
import { IFeatureToggleQuery, ISegment } from '../../types/model';
|
import { IFeatureToggleQuery } from '../../types/model';
|
||||||
import NotFoundError from '../../error/notfound-error';
|
import NotFoundError from '../../error/notfound-error';
|
||||||
import { IAuthRequest } from '../unleash-types';
|
import { IAuthRequest } from '../unleash-types';
|
||||||
import ApiUser from '../../types/api-user';
|
import ApiUser from '../../types/api-user';
|
||||||
@ -58,7 +58,7 @@ export default class FeatureController extends Controller {
|
|||||||
private featuresAndSegments: (
|
private featuresAndSegments: (
|
||||||
query: IFeatureToggleQuery,
|
query: IFeatureToggleQuery,
|
||||||
etag: string,
|
etag: string,
|
||||||
) => Promise<[FeatureConfigurationClient[], ISegment[]]>;
|
) => Promise<[FeatureConfigurationClient[], IClientSegment[]]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@ -145,10 +145,10 @@ export default class FeatureController extends Controller {
|
|||||||
|
|
||||||
private async resolveFeaturesAndSegments(
|
private async resolveFeaturesAndSegments(
|
||||||
query?: IFeatureToggleQuery,
|
query?: IFeatureToggleQuery,
|
||||||
): Promise<[FeatureConfigurationClient[], ISegment[]]> {
|
): Promise<[FeatureConfigurationClient[], IClientSegment[]]> {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.featureToggleServiceV2.getClientFeatures(query),
|
this.featureToggleServiceV2.getClientFeatures(query),
|
||||||
this.segmentService.getActive(),
|
this.segmentService.getActiveForClient(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { UpsertSegmentSchema } from 'lib/openapi';
|
import { UpsertSegmentSchema } from 'lib/openapi';
|
||||||
import { IFeatureStrategy, ISegment, IUser } from 'lib/types';
|
import { IClientSegment, IFeatureStrategy, ISegment, IUser } from 'lib/types';
|
||||||
|
|
||||||
export interface ISegmentService {
|
export interface ISegmentService {
|
||||||
updateStrategySegments: (
|
updateStrategySegments: (
|
||||||
@ -19,6 +19,8 @@ export interface ISegmentService {
|
|||||||
|
|
||||||
getActive(): Promise<ISegment[]>;
|
getActive(): Promise<ISegment[]>;
|
||||||
|
|
||||||
|
getActiveForClient(): Promise<IClientSegment[]>;
|
||||||
|
|
||||||
getAll(): Promise<ISegment[]>;
|
getAll(): Promise<ISegment[]>;
|
||||||
|
|
||||||
create(
|
create(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { IUnleashConfig } from '../types/option';
|
import { IUnleashConfig } from '../types/option';
|
||||||
import { IEventStore } from '../types/stores/event-store';
|
import { IEventStore } from '../types/stores/event-store';
|
||||||
import { IUnleashStores } from '../types';
|
import { IClientSegment, IUnleashStores } from '../types';
|
||||||
import { Logger } from '../logger';
|
import { Logger } from '../logger';
|
||||||
import NameExistsError from '../error/name-exists-error';
|
import NameExistsError from '../error/name-exists-error';
|
||||||
import { ISegmentStore } from '../types/stores/segment-store';
|
import { ISegmentStore } from '../types/stores/segment-store';
|
||||||
@ -57,6 +57,10 @@ export class SegmentService implements ISegmentService {
|
|||||||
return this.segmentStore.getActive();
|
return this.segmentStore.getActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getActiveForClient(): Promise<IClientSegment[]> {
|
||||||
|
return this.segmentStore.getActiveForClient();
|
||||||
|
}
|
||||||
|
|
||||||
// Used by unleash-enterprise.
|
// Used by unleash-enterprise.
|
||||||
async getByStrategy(strategyId: string): Promise<ISegment[]> {
|
async getByStrategy(strategyId: string): Promise<ISegment[]> {
|
||||||
return this.segmentStore.getByStrategy(strategyId);
|
return this.segmentStore.getByStrategy(strategyId);
|
||||||
|
@ -418,6 +418,12 @@ export interface IProjectWithCount extends IProject {
|
|||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IClientSegment {
|
||||||
|
id: number;
|
||||||
|
constraints: IConstraint[];
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ISegment {
|
export interface ISegment {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IFeatureStrategySegment, ISegment } from '../model';
|
import { IClientSegment, IFeatureStrategySegment, ISegment } from '../model';
|
||||||
import { Store } from './store';
|
import { Store } from './store';
|
||||||
import User from '../user';
|
import User from '../user';
|
||||||
|
|
||||||
@ -7,6 +7,8 @@ export interface ISegmentStore extends Store<ISegment, number> {
|
|||||||
|
|
||||||
getActive(): Promise<ISegment[]>;
|
getActive(): Promise<ISegment[]>;
|
||||||
|
|
||||||
|
getActiveForClient(): Promise<IClientSegment[]>;
|
||||||
|
|
||||||
getByStrategy(strategyId: string): Promise<ISegment[]>;
|
getByStrategy(strategyId: string): Promise<ISegment[]>;
|
||||||
|
|
||||||
create(
|
create(
|
||||||
|
10
src/test/fixtures/fake-segment-store.ts
vendored
10
src/test/fixtures/fake-segment-store.ts
vendored
@ -1,5 +1,9 @@
|
|||||||
import { ISegmentStore } from '../../lib/types/stores/segment-store';
|
import { ISegmentStore } from '../../lib/types/stores/segment-store';
|
||||||
import { IFeatureStrategySegment, ISegment } from '../../lib/types/model';
|
import {
|
||||||
|
IClientSegment,
|
||||||
|
IFeatureStrategySegment,
|
||||||
|
ISegment,
|
||||||
|
} from '../../lib/types/model';
|
||||||
|
|
||||||
export default class FakeSegmentStore implements ISegmentStore {
|
export default class FakeSegmentStore implements ISegmentStore {
|
||||||
count(): Promise<number> {
|
count(): Promise<number> {
|
||||||
@ -34,6 +38,10 @@ export default class FakeSegmentStore implements ISegmentStore {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getActiveForClient(): Promise<IClientSegment[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
async getByStrategy(): Promise<ISegment[]> {
|
async getByStrategy(): Promise<ISegment[]> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user