mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: segment delta (#8990)
Now the delta endpoint also always returns all the segments.
This commit is contained in:
		
							parent
							
								
									37b55eff5a
								
							
						
					
					
						commit
						138ba35d7a
					
				| @ -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<void> { | ||||
|         this.segments = await this.segmentReadModel.getActiveForClient(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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); | ||||
| }); | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| import type EventEmitter from 'events'; | ||||
| import { CLIENT_METRICS } from './internals'; | ||||
| 
 | ||||
| const REQUEST_TIME = 'request_time'; | ||||
| const DB_TIME = 'db_time'; | ||||
|  | ||||
| @ -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: { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user