Skip to content

Commit

Permalink
fix: Ensure flag-detail-changed is called for deleted flags. (#695)
Browse files Browse the repository at this point in the history
Noticed this difference in behavior when auditing the code for browser
telemetry.
  • Loading branch information
kinyoklion authored Nov 21, 2024
1 parent db74a99 commit 6524030
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,60 @@ it('calls flag-detail-changed inspector for individial flag changes on patch', a
});
});

it('calls flag-detail-changed inspector when a flag is deleted', async () => {
const eventQueue = new AsyncQueue();
const flagDetailChangedInspector: LDInspection = {
type: 'flag-detail-changed',
name: 'test flag detail changed inspector',
method: jest.fn(() => eventQueue.add({})),
};
const platform = createBasicPlatform();
const factory = makeTestDataManagerFactory('sdk-key', platform);
const client = new LDClientImpl(
'sdk-key',
AutoEnvAttributes.Disabled,
platform,
{
sendEvents: false,
inspectors: [flagDetailChangedInspector],
logger: {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
},
},
factory,
);
let mockEventSource: MockEventSource;

const putResponse = clone<Flags>(mockResponseJson);
const putEvents = [{ data: JSON.stringify(putResponse) }];
const deleteResponse = {
key: 'dev-test-flag',
version: putResponse['dev-test-flag'].version + 1,
};
const deleteEvents = [{ data: JSON.stringify(deleteResponse) }];

platform.requests.createEventSource.mockImplementation(
(streamUri: string = '', options: any = {}) => {
mockEventSource = new MockEventSource(streamUri, options);
mockEventSource.simulateEvents('put', putEvents);
mockEventSource.simulateEvents('delete', deleteEvents);
return mockEventSource;
},
);

await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });

await eventQueue.take();
expect(flagDetailChangedInspector.method).toHaveBeenCalledWith('dev-test-flag', {
reason: null,
value: undefined,
variationIndex: null,
});
});

it('calls flag-details-changed inspectors when all flag values change', async () => {
const flagDetailsChangedInspector: LDInspection = {
type: 'flag-details-changed',
Expand Down
8 changes: 8 additions & 0 deletions packages/shared/sdk-client/src/LDClientImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,14 @@ export default class LDClientImpl implements LDClient {
if (item?.flag && !item.flag.deleted) {
const { reason, value, variation } = item.flag;
details[flagKey] = createSuccessEvaluationDetail(value, variation, reason);
} else {
details[flagKey] = {
value: undefined,
// For backwards compatibility purposes reason and variationIndex are null instead of
// being undefined.
reason: null,
variationIndex: null,
};
}
});
if (type === 'init') {
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/sdk-client/src/api/LDInspection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export interface LDInspectionFlagDetailsChangedHandler {
*
* This interface should not be used by the application to access flags for the purpose of controlling application
* flow. It is intended for monitoring, analytics, or debugging purposes.
*
* When a flag is deleted the `value` in the {@link LDEvaluationDetail} will be `undefined`.
*/
export interface LDInspectionFlagDetailChangedHandler {
type: 'flag-detail-changed';
Expand Down

0 comments on commit 6524030

Please sign in to comment.