Skip to content

Commit

Permalink
feat: segment delta (#8990)
Browse files Browse the repository at this point in the history
Now the delta endpoint also always returns all the segments.
  • Loading branch information
sjaanus authored Dec 17, 2024
1 parent 37b55ef commit 138ba35
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -21,6 +23,7 @@ export type RevisionDeltaEntry = {
updated: FeatureConfigurationDeltaClient[];
revisionId: number;
removed: DeletedFeature[];
segments: IClientSegment[];
};

export type Revision = {
Expand Down Expand Up @@ -96,6 +99,8 @@ export class ClientFeatureToggleDelta {

private delta: Revisions = {};

private segments: IClientSegment[];

private eventStore: IEventStore;

private currentRevisionId: number = 0;
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -162,6 +176,7 @@ export class ClientFeatureToggleDelta {
revisionId: this.currentRevisionId,
// @ts-ignore
updated: await this.getClientFeatures({ environment }),
segments: this.segments,
removed: [],
};
}
Expand All @@ -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();
}
}

Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -82,6 +90,7 @@ const cleanup = async (db: ITestDb, app: IUnleashTest) => {
),
),
);
await db.stores.segmentStore.deleteAll();
};

const setupFeatures = async (
Expand All @@ -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',
Expand Down Expand Up @@ -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 change: 0 additions & 1 deletion src/lib/metric-events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type EventEmitter from 'events';
import { CLIENT_METRICS } from './internals';

const REQUEST_TIME = 'request_time';
const DB_TIME = 'db_time';
Expand Down
8 changes: 8 additions & 0 deletions src/lib/openapi/spec/client-features-delta-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down

0 comments on commit 138ba35

Please sign in to comment.