From 6beba7753e4c7884d9e4e316d5c7a3577ec72b41 Mon Sep 17 00:00:00 2001 From: sjaanus Date: Mon, 16 Dec 2024 15:39:32 +0200 Subject: [PATCH] feat: segment delta --- .../delta/client-feature-toggle-delta.ts | 26 +++++++++++++++++- .../delta/createClientFeatureToggleDelta.ts | 4 +++ .../client-feature-toggles.e2e.test.ts.snap | 12 ++++++++- .../tests/client-feature-toggles.e2e.test.ts | 27 ++++++++++++++++++- src/lib/metric-events.ts | 1 - .../spec/client-features-delta-schema.ts | 8 ++++++ 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/lib/features/client-feature-toggles/delta/client-feature-toggle-delta.ts b/src/lib/features/client-feature-toggles/delta/client-feature-toggle-delta.ts index 9237b32ba692..5b49e131f36d 100644 --- a/src/lib/features/client-feature-toggles/delta/client-feature-toggle-delta.ts +++ b/src/lib/features/client-feature-toggles/delta/client-feature-toggle-delta.ts @@ -3,6 +3,7 @@ import type { IFeatureToggleDeltaQuery, IFeatureToggleQuery, IFlagResolver, + ISegmentReadModel, } from '../../../types'; import type ConfigurationRevisionService from '../../feature-toggle/configuration-revision-service'; import { UPDATE_REVISION } from '../../feature-toggle/configuration-revision-service'; @@ -11,6 +12,8 @@ import type { FeatureConfigurationDeltaClient, IClientFeatureToggleDeltaReadModel, } from './client-feature-toggle-delta-read-model-type'; +import type { Segment } from 'unleash-client/lib/strategy/strategy'; +import { mapSegmentsForClient } from '../../playground/offline-unleash-client'; type DeletedFeature = { name: string; @@ -21,6 +24,7 @@ export type RevisionDeltaEntry = { updated: FeatureConfigurationDeltaClient[]; revisionId: number; removed: DeletedFeature[]; + segments: Segment[]; }; export type Revision = { @@ -96,6 +100,8 @@ export class ClientFeatureToggleDelta { private delta: Revisions = {}; + private segments: Segment[] = []; + private eventStore: IEventStore; private currentRevisionId: number = 0; @@ -106,8 +112,11 @@ export class ClientFeatureToggleDelta { private configurationRevisionService: ConfigurationRevisionService; + private readonly segmentReadModel: ISegmentReadModel; + constructor( clientFeatureToggleDeltaReadModel: IClientFeatureToggleDeltaReadModel, + segmentReadModel: ISegmentReadModel, eventStore: IEventStore, configurationRevisionService: ConfigurationRevisionService, flagResolver: IFlagResolver, @@ -117,10 +126,12 @@ export class ClientFeatureToggleDelta { this.clientFeatureToggleDeltaReadModel = clientFeatureToggleDeltaReadModel; this.flagResolver = flagResolver; + this.segmentReadModel = segmentReadModel; this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this); this.delta = {}; this.initRevisionId(); + this.updateSegments(); this.configurationRevisionService.on( UPDATE_REVISION, this.onUpdateRevisionEvent, @@ -162,6 +173,7 @@ export class ClientFeatureToggleDelta { revisionId: this.currentRevisionId, // @ts-ignore updated: await this.getClientFeatures({ environment }), + segments: this.segments, removed: [], }; } @@ -178,12 +190,18 @@ export class ClientFeatureToggleDelta { projects, ); - return Promise.resolve(compressedRevision); + const revisionResponse = { + ...compressedRevision, + segments: this.segments, + }; + + return Promise.resolve(revisionResponse); } private async onUpdateRevisionEvent() { if (this.flagResolver.isEnabled('deltaApi')) { await this.listenToRevisionChange(); + await this.updateSegments(); } } @@ -269,4 +287,10 @@ export class ClientFeatureToggleDelta { await this.clientFeatureToggleDeltaReadModel.getAll(query); return result; } + + private async updateSegments(): Promise { + this.segments = mapSegmentsForClient( + await this.segmentReadModel.getAll(), + ); + } } diff --git a/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts b/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts index 78f0d52f5aba..9252357b2f32 100644 --- a/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts +++ b/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts @@ -4,6 +4,7 @@ import ConfigurationRevisionService from '../../feature-toggle/configuration-rev import type { IUnleashConfig } from '../../../types'; import type { Db } from '../../../db/db'; import ClientFeatureToggleDeltaReadModel from './client-feature-toggle-delta-read-model'; +import { SegmentReadModel } from '../../segment/segment-read-model'; export const createClientFeatureToggleDelta = ( db: Db, @@ -19,8 +20,11 @@ export const createClientFeatureToggleDelta = ( const configurationRevisionService = ConfigurationRevisionService.getInstance({ eventStore }, config); + const segmentReadModel = new SegmentReadModel(db); + const clientFeatureToggleDelta = new ClientFeatureToggleDelta( clientFeatureToggleDeltaReadModel, + segmentReadModel, eventStore, configurationRevisionService, flagResolver, diff --git a/src/lib/features/client-feature-toggles/tests/__snapshots__/client-feature-toggles.e2e.test.ts.snap b/src/lib/features/client-feature-toggles/tests/__snapshots__/client-feature-toggles.e2e.test.ts.snap index 6cdb40d1a110..e0565419a7ef 100644 --- a/src/lib/features/client-feature-toggles/tests/__snapshots__/client-feature-toggles.e2e.test.ts.snap +++ b/src/lib/features/client-feature-toggles/tests/__snapshots__/client-feature-toggles.e2e.test.ts.snap @@ -12,7 +12,17 @@ exports[`should match snapshot from /api/client/features 1`] = ` "stale": false, "strategies": [ { - "constraints": [], + "constraints": [ + { + "caseInsensitive": false, + "contextName": "appName", + "inverted": false, + "operator": "IN", + "values": [ + "test", + ], + }, + ], "name": "flexibleRollout", "parameters": { "groupId": "test1", diff --git a/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts b/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts index e8166a4f29bc..3826716f1353 100644 --- a/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts +++ b/src/lib/features/client-feature-toggles/tests/client-feature-toggles.e2e.test.ts @@ -30,7 +30,15 @@ const getApiClientResponse = (project = 'default') => [ strategies: [ { name: 'flexibleRollout', - constraints: [], + constraints: [ + { + contextName: 'appName', + operator: 'IN', + values: ['test'], + caseInsensitive: false, + inverted: false, + }, + ], parameters: { rollout: '100', stickiness: 'default', @@ -82,6 +90,7 @@ const cleanup = async (db: ITestDb, app: IUnleashTest) => { ), ), ); + await db.stores.segmentStore.deleteAll(); }; const setupFeatures = async ( @@ -94,10 +103,24 @@ const setupFeatures = async ( await app.createFeature('test1', project); await app.createFeature('test2', project); + const { body: segmentBody } = await app.createSegment({ + name: 'a', + constraints: [ + { + contextName: 'appName', + operator: 'IN', + values: ['test'], + caseInsensitive: false, + inverted: false, + }, + ], + }); + await app.addStrategyToFeatureEnv( { name: 'flexibleRollout', constraints: [], + segments: [segmentBody.id], parameters: { rollout: '100', stickiness: 'default', @@ -329,6 +352,7 @@ test('should match with /api/client/delta', async () => { const { body } = await app.request .get('/api/client/features') + .set('Unleash-Client-Spec', '4.2.0') .expect('Content-Type', /json/) .expect(200); @@ -338,4 +362,5 @@ test('should match with /api/client/delta', async () => { .expect(200); expect(body.features).toMatchObject(deltaBody.updated); + expect(body.segments).toMatchObject(deltaBody.segments); }); diff --git a/src/lib/metric-events.ts b/src/lib/metric-events.ts index 366bc5c4b160..280f66c981e9 100644 --- a/src/lib/metric-events.ts +++ b/src/lib/metric-events.ts @@ -1,5 +1,4 @@ import type EventEmitter from 'events'; -import { CLIENT_METRICS } from './internals'; const REQUEST_TIME = 'request_time'; const DB_TIME = 'db_time'; diff --git a/src/lib/openapi/spec/client-features-delta-schema.ts b/src/lib/openapi/spec/client-features-delta-schema.ts index b7eb60444c17..8effe5579c8b 100644 --- a/src/lib/openapi/spec/client-features-delta-schema.ts +++ b/src/lib/openapi/spec/client-features-delta-schema.ts @@ -34,6 +34,14 @@ export const clientFeaturesDeltaSchema = { type: 'string', }, }, + segments: { + description: + 'A list of [Segments](https://docs.getunleash.io/reference/segments) configured for this Unleash instance', + type: 'array', + items: { + $ref: '#/components/schemas/clientSegmentSchema', + }, + }, }, components: { schemas: {