Skip to content

Commit a986478

Browse files
authored
feat: Add support for inspectors. (#625)
The flag usage and identify were able to be implemented in terms of hooks. The flag configuration related inspectors had to be implemented directly. It would be ideal to figure out how to gauge demand for this functionality and simply remove it when we deprecated hooks if demand is low.
1 parent f6fc40b commit a986478

File tree

18 files changed

+847
-9
lines changed

18 files changed

+847
-9
lines changed

packages/sdk/browser/__tests__/BrowserDataManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
9494
userAgentHeaderName: 'user-agent',
9595
trackEventModifier: (event) => event,
9696
hooks: [],
97+
inspectors: [],
9798
};
9899
const mockedFetch = mockFetch('{"flagA": true}', 200);
99100
platform = {

packages/sdk/react-native/__tests__/MobileDataManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ describe('given a MobileDataManager with mocked dependencies', () => {
8282
userAgentHeaderName: 'user-agent',
8383
trackEventModifier: (event) => event,
8484
hooks: [],
85+
inspectors: [],
8586
};
8687
const mockedFetch = mockFetch('{"flagA": true}', 200);
8788
platform = {
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import { AsyncQueue } from 'launchdarkly-js-test-helpers';
2+
3+
import { AutoEnvAttributes, clone } from '@launchdarkly/js-sdk-common';
4+
5+
import { LDInspection } from '../src/api/LDInspection';
6+
import LDClientImpl from '../src/LDClientImpl';
7+
import { Flags, PatchFlag } from '../src/types';
8+
import { createBasicPlatform } from './createBasicPlatform';
9+
import * as mockResponseJson from './evaluation/mockResponse.json';
10+
import { MockEventSource } from './streaming/LDClientImpl.mocks';
11+
import { makeTestDataManagerFactory } from './TestDataManager';
12+
13+
it('calls flag-used inspectors', async () => {
14+
const flagUsedInspector: LDInspection = {
15+
type: 'flag-used',
16+
name: 'test flag used inspector',
17+
method: jest.fn(),
18+
};
19+
const platform = createBasicPlatform();
20+
const factory = makeTestDataManagerFactory('sdk-key', platform, {
21+
disableNetwork: true,
22+
});
23+
const client = new LDClientImpl(
24+
'sdk-key',
25+
AutoEnvAttributes.Disabled,
26+
platform,
27+
{
28+
sendEvents: false,
29+
inspectors: [flagUsedInspector],
30+
logger: {
31+
debug: jest.fn(),
32+
info: jest.fn(),
33+
warn: jest.fn(),
34+
error: jest.fn(),
35+
},
36+
},
37+
factory,
38+
);
39+
40+
await client.identify({ key: 'user-key' });
41+
await client.variation('flag-key', false);
42+
43+
expect(flagUsedInspector.method).toHaveBeenCalledWith(
44+
'flag-key',
45+
{
46+
value: false,
47+
variationIndex: null,
48+
reason: {
49+
kind: 'ERROR',
50+
errorKind: 'FLAG_NOT_FOUND',
51+
},
52+
},
53+
{ key: 'user-key' },
54+
);
55+
});
56+
57+
it('calls client-identity-changed inspectors', async () => {
58+
const identifyInspector: LDInspection = {
59+
type: 'client-identity-changed',
60+
name: 'test client identity inspector',
61+
method: jest.fn(),
62+
};
63+
64+
const platform = createBasicPlatform();
65+
const factory = makeTestDataManagerFactory('sdk-key', platform, {
66+
disableNetwork: true,
67+
});
68+
const client = new LDClientImpl(
69+
'sdk-key',
70+
AutoEnvAttributes.Disabled,
71+
platform,
72+
{
73+
sendEvents: false,
74+
inspectors: [identifyInspector],
75+
logger: {
76+
debug: jest.fn(),
77+
info: jest.fn(),
78+
warn: jest.fn(),
79+
error: jest.fn(),
80+
},
81+
},
82+
factory,
83+
);
84+
85+
await client.identify({ key: 'user-key' });
86+
87+
expect(identifyInspector.method).toHaveBeenCalledWith({ key: 'user-key' });
88+
});
89+
90+
it('calls flag-detail-changed inspector for individial flag changes on patch', async () => {
91+
const eventQueue = new AsyncQueue();
92+
const flagDetailChangedInspector: LDInspection = {
93+
type: 'flag-detail-changed',
94+
name: 'test flag detail changed inspector',
95+
method: jest.fn(() => eventQueue.add({})),
96+
};
97+
const platform = createBasicPlatform();
98+
const factory = makeTestDataManagerFactory('sdk-key', platform);
99+
const client = new LDClientImpl(
100+
'sdk-key',
101+
AutoEnvAttributes.Disabled,
102+
platform,
103+
{
104+
sendEvents: false,
105+
inspectors: [flagDetailChangedInspector],
106+
logger: {
107+
debug: jest.fn(),
108+
info: jest.fn(),
109+
warn: jest.fn(),
110+
error: jest.fn(),
111+
},
112+
},
113+
factory,
114+
);
115+
let mockEventSource: MockEventSource;
116+
117+
const putResponse = clone<Flags>(mockResponseJson);
118+
const putEvents = [{ data: JSON.stringify(putResponse) }];
119+
platform.requests.createEventSource.mockImplementation(
120+
(streamUri: string = '', options: any = {}) => {
121+
mockEventSource = new MockEventSource(streamUri, options);
122+
const patchResponse = clone<PatchFlag>(putResponse['dev-test-flag']);
123+
patchResponse.key = 'dev-test-flag';
124+
patchResponse.value = false;
125+
patchResponse.version += 1;
126+
const patchEvents = [{ data: JSON.stringify(patchResponse) }];
127+
128+
// @ts-ignore
129+
mockEventSource.simulateEvents('patch', patchEvents);
130+
mockEventSource.simulateEvents('put', putEvents);
131+
return mockEventSource;
132+
},
133+
);
134+
135+
await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });
136+
137+
await eventQueue.take();
138+
expect(flagDetailChangedInspector.method).toHaveBeenCalledWith('dev-test-flag', {
139+
reason: null,
140+
value: false,
141+
variationIndex: 0,
142+
});
143+
});
144+
145+
it('calls flag-details-changed inspectors when all flag values change', async () => {
146+
const flagDetailsChangedInspector: LDInspection = {
147+
type: 'flag-details-changed',
148+
name: 'test flag details changed inspector',
149+
method: jest.fn(),
150+
};
151+
const platform = createBasicPlatform();
152+
const factory = makeTestDataManagerFactory('sdk-key', platform);
153+
const client = new LDClientImpl(
154+
'sdk-key',
155+
AutoEnvAttributes.Disabled,
156+
platform,
157+
{
158+
sendEvents: false,
159+
inspectors: [flagDetailsChangedInspector],
160+
logger: {
161+
debug: jest.fn(),
162+
info: jest.fn(),
163+
warn: jest.fn(),
164+
error: jest.fn(),
165+
},
166+
},
167+
factory,
168+
);
169+
let mockEventSource: MockEventSource;
170+
171+
platform.requests.createEventSource.mockImplementation(
172+
(streamUri: string = '', options: any = {}) => {
173+
mockEventSource = new MockEventSource(streamUri, options);
174+
const simulatedEvents = [{ data: JSON.stringify(mockResponseJson) }];
175+
mockEventSource.simulateEvents('put', simulatedEvents);
176+
return mockEventSource;
177+
},
178+
);
179+
180+
await client.identify({ key: 'user-key' }, { waitForNetworkResults: true });
181+
expect(flagDetailsChangedInspector.method).toHaveBeenCalledWith({
182+
'dev-test-flag': { reason: null, value: true, variationIndex: 0 },
183+
'easter-i-tunes-special': { reason: null, value: false, variationIndex: 1 },
184+
'easter-specials': { reason: null, value: 'no specials', variationIndex: 3 },
185+
fdsafdsafdsafdsa: { reason: null, value: true, variationIndex: 0 },
186+
'log-level': { reason: null, value: 'warn', variationIndex: 3 },
187+
'moonshot-demo': { reason: null, value: true, variationIndex: 0 },
188+
test1: { reason: null, value: 's1', variationIndex: 0 },
189+
'this-is-a-test': { reason: null, value: true, variationIndex: 0 },
190+
});
191+
});

0 commit comments

Comments
 (0)