Skip to content

Commit 6524030

Browse files
authored
fix: Ensure flag-detail-changed is called for deleted flags. (#695)
Noticed this difference in behavior when auditing the code for browser telemetry.
1 parent db74a99 commit 6524030

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

packages/shared/sdk-client/__tests__/LDCLientImpl.inspections.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,60 @@ it('calls flag-detail-changed inspector for individial flag changes on patch', a
142142
});
143143
});
144144

145+
it('calls flag-detail-changed inspector when a flag is deleted', async () => {
146+
const eventQueue = new AsyncQueue();
147+
const flagDetailChangedInspector: LDInspection = {
148+
type: 'flag-detail-changed',
149+
name: 'test flag detail changed inspector',
150+
method: jest.fn(() => eventQueue.add({})),
151+
};
152+
const platform = createBasicPlatform();
153+
const factory = makeTestDataManagerFactory('sdk-key', platform);
154+
const client = new LDClientImpl(
155+
'sdk-key',
156+
AutoEnvAttributes.Disabled,
157+
platform,
158+
{
159+
sendEvents: false,
160+
inspectors: [flagDetailChangedInspector],
161+
logger: {
162+
debug: jest.fn(),
163+
info: jest.fn(),
164+
warn: jest.fn(),
165+
error: jest.fn(),
166+
},
167+
},
168+
factory,
169+
);
170+
let mockEventSource: MockEventSource;
171+
172+
const putResponse = clone<Flags>(mockResponseJson);
173+
const putEvents = [{ data: JSON.stringify(putResponse) }];
174+
const deleteResponse = {
175+
key: 'dev-test-flag',
176+
version: putResponse['dev-test-flag'].version + 1,
177+
};
178+
const deleteEvents = [{ data: JSON.stringify(deleteResponse) }];
179+
180+
platform.requests.createEventSource.mockImplementation(
181+
(streamUri: string = '', options: any = {}) => {
182+
mockEventSource = new MockEventSource(streamUri, options);
183+
mockEventSource.simulateEvents('put', putEvents);
184+
mockEventSource.simulateEvents('delete', deleteEvents);
185+
return mockEventSource;
186+
},
187+
);
188+
189+
await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });
190+
191+
await eventQueue.take();
192+
expect(flagDetailChangedInspector.method).toHaveBeenCalledWith('dev-test-flag', {
193+
reason: null,
194+
value: undefined,
195+
variationIndex: null,
196+
});
197+
});
198+
145199
it('calls flag-details-changed inspectors when all flag values change', async () => {
146200
const flagDetailsChangedInspector: LDInspection = {
147201
type: 'flag-details-changed',

packages/shared/sdk-client/src/LDClientImpl.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,14 @@ export default class LDClientImpl implements LDClient {
509509
if (item?.flag && !item.flag.deleted) {
510510
const { reason, value, variation } = item.flag;
511511
details[flagKey] = createSuccessEvaluationDetail(value, variation, reason);
512+
} else {
513+
details[flagKey] = {
514+
value: undefined,
515+
// For backwards compatibility purposes reason and variationIndex are null instead of
516+
// being undefined.
517+
reason: null,
518+
variationIndex: null,
519+
};
512520
}
513521
});
514522
if (type === 'init') {

packages/shared/sdk-client/src/api/LDInspection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ export interface LDInspectionFlagDetailsChangedHandler {
7171
*
7272
* This interface should not be used by the application to access flags for the purpose of controlling application
7373
* flow. It is intended for monitoring, analytics, or debugging purposes.
74+
*
75+
* When a flag is deleted the `value` in the {@link LDEvaluationDetail} will be `undefined`.
7476
*/
7577
export interface LDInspectionFlagDetailChangedHandler {
7678
type: 'flag-detail-changed';

0 commit comments

Comments
 (0)