diff --git a/src/lib/features/client-feature-toggles/delta/client-feature-delta-api.e2e.test.ts b/src/lib/features/client-feature-toggles/delta/client-feature-delta-api.e2e.test.ts index 5916abbf6ffc..49919fd80172 100644 --- a/src/lib/features/client-feature-toggles/delta/client-feature-delta-api.e2e.test.ts +++ b/src/lib/features/client-feature-toggles/delta/client-feature-delta-api.e2e.test.ts @@ -270,3 +270,54 @@ test('should get segment updated and removed events', async () => { ], }); }); + +test('should return hydration if revision not in cache', async () => { + await app.createFeature('base_feature'); + await syncRevisions(); + const { body, headers } = await app.request + .get('/api/client/delta') + .expect(200); + const etag = headers.etag; + + expect(body).toMatchObject({ + events: [ + { + type: DELTA_EVENT_TYPES.HYDRATION, + features: [ + { + name: 'base_feature', + }, + ], + }, + ], + }); + + await app.createFeature('not_important1'); + await syncRevisions(); + + const { body: deltaBody } = await app.request + .get('/api/client/delta') + .set('If-None-Match', etag) + .expect(200); + + expect(deltaBody).toMatchObject({ + events: [ + { + type: DELTA_EVENT_TYPES.FEATURE_UPDATED, + }, + ], + }); + + const { body: rehydrationBody } = await app.request + .get('/api/client/delta') + .set('If-None-Match', '1') + .expect(200); + + expect(rehydrationBody).toMatchObject({ + events: [ + { + type: DELTA_EVENT_TYPES.HYDRATION, + }, + ], + }); +}); 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 805c578da20c..4b652e5a7b1a 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 @@ -154,8 +154,12 @@ export class ClientFeatureToggleDelta { if (requiredRevisionId >= this.currentRevisionId) { return undefined; } - if (requiredRevisionId === 0) { - const hydrationEvent = this.delta[environment].getHydrationEvent(); + const delta = this.delta[environment]; + if ( + requiredRevisionId === 0 || + delta.isMissingRevision(requiredRevisionId) + ) { + const hydrationEvent = delta.getHydrationEvent(); const filteredEvent = filterHydrationEventByQuery( hydrationEvent, projects, @@ -168,7 +172,7 @@ export class ClientFeatureToggleDelta { return Promise.resolve(response); } else { - const environmentEvents = this.delta[environment].getEvents(); + const environmentEvents = delta.getEvents(); const events = filterEventsByQuery( environmentEvents, requiredRevisionId, @@ -299,8 +303,7 @@ export class ClientFeatureToggleDelta { }); } - public async initEnvironmentDelta(environment: string) { - // Todo: replace with method that gets all features for an environment + private async initEnvironmentDelta(environment: string) { const baseFeatures = await this.getClientFeatures({ environment, }); diff --git a/src/lib/features/client-feature-toggles/delta/delta-cache.ts b/src/lib/features/client-feature-toggles/delta/delta-cache.ts index 8ae44114036a..9869a8fc562e 100644 --- a/src/lib/features/client-feature-toggles/delta/delta-cache.ts +++ b/src/lib/features/client-feature-toggles/delta/delta-cache.ts @@ -12,6 +12,22 @@ export class DeltaCache { constructor(hydrationEvent: DeltaHydrationEvent, maxLength: number = 20) { this.hydrationEvent = hydrationEvent; this.maxLength = maxLength; + + this.addBaseEventFromHydration(hydrationEvent); + } + + private addBaseEventFromHydration( + hydrationEvent: DeltaHydrationEvent, + ): void { + const lastFeature = + hydrationEvent.features[hydrationEvent.features.length - 1]; + this.addEvents([ + { + eventId: hydrationEvent.eventId, + type: 'feature-updated', + feature: lastFeature, + }, + ]); } public addEvents(events: DeltaEvent[]): void { @@ -27,6 +43,10 @@ export class DeltaCache { return this.events; } + public isMissingRevision(revisionId: number): boolean { + return !this.events.some((event) => event.eventId === revisionId); + } + public getHydrationEvent(): DeltaHydrationEvent { return this.hydrationEvent; }