From 138ba35d7afd3571bd066519d21232ea07a64048 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Tue, 17 Dec 2024 10:56:09 +0200 Subject: [PATCH] feat: segment delta (#8990) Now the delta endpoint also always returns all the segments. --- .../delta/client-feature-toggle-delta.ts | 27 +++++++++++- .../delta/createClientFeatureToggleDelta.ts | 4 ++ .../client-feature-toggles.e2e.test.ts.snap | 12 +++++- .../tests/client-feature-toggles.e2e.test.ts | 41 +++++++++++-------- src/lib/metric-events.ts | 1 - .../spec/client-features-delta-schema.ts | 8 ++++ 6 files changed, 73 insertions(+), 20 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 9237b32ba6..271221426d 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 @@ -1,8 +1,10 @@ import type { + IClientSegment, IEventStore, IFeatureToggleDeltaQuery, IFeatureToggleQuery, IFlagResolver, + ISegmentReadModel, } from '../../../types'; import type ConfigurationRevisionService from '../../feature-toggle/configuration-revision-service'; import { UPDATE_REVISION } from '../../feature-toggle/configuration-revision-service'; @@ -21,6 +23,7 @@ export type RevisionDeltaEntry = { updated: FeatureConfigurationDeltaClient[]; revisionId: number; removed: DeletedFeature[]; + segments: IClientSegment[]; }; export type Revision = { @@ -96,6 +99,8 @@ export class ClientFeatureToggleDelta { private delta: Revisions = {}; + private segments: IClientSegment[]; + private eventStore: IEventStore; private currentRevisionId: number = 0; @@ -106,8 +111,11 @@ export class ClientFeatureToggleDelta { private configurationRevisionService: ConfigurationRevisionService; + private readonly segmentReadModel: ISegmentReadModel; + constructor( clientFeatureToggleDeltaReadModel: IClientFeatureToggleDeltaReadModel, + segmentReadModel: ISegmentReadModel, eventStore: IEventStore, configurationRevisionService: ConfigurationRevisionService, flagResolver: IFlagResolver, @@ -117,10 +125,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, @@ -146,6 +156,10 @@ export class ClientFeatureToggleDelta { if (!hasDelta) { await this.initEnvironmentDelta(environment); } + const hasSegments = this.segments; + if (!hasSegments) { + await this.updateSegments(); + } // Should get the latest state if revision does not exist or if sdkRevision is not present // We should be able to do this without going to the database by merging revisions from the delta with @@ -162,6 +176,7 @@ export class ClientFeatureToggleDelta { revisionId: this.currentRevisionId, // @ts-ignore updated: await this.getClientFeatures({ environment }), + segments: this.segments, removed: [], }; } @@ -178,12 +193,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 +290,8 @@ export class ClientFeatureToggleDelta { await this.clientFeatureToggleDeltaReadModel.getAll(query); return result; } + + private async updateSegments(): Promise { + this.segments = await this.segmentReadModel.getActiveForClient(); + } } diff --git a/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts b/src/lib/features/client-feature-toggles/delta/createClientFeatureToggleDelta.ts index 78f0d52f5a..9252357b2f 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 6cdb40d1a1..e0565419a7 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 e8166a4f29..5e9a608c6c 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', @@ -323,19 +346,3 @@ test('should match snapshot from /api/client/features', async () => { expect(result.body).toMatchSnapshot(); }); - -test('should match with /api/client/delta', async () => { - await setupFeatures(db, app); - - const { body } = await app.request - .get('/api/client/features') - .expect('Content-Type', /json/) - .expect(200); - - const { body: deltaBody } = await app.request - .get('/api/client/delta') - .expect('Content-Type', /json/) - .expect(200); - - expect(body.features).toMatchObject(deltaBody.updated); -}); diff --git a/src/lib/metric-events.ts b/src/lib/metric-events.ts index 366bc5c4b1..280f66c981 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 b7eb60444c..8effe5579c 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: {