Skip to content

Commit aa78b6e

Browse files
authored
Merge pull request #92 from launchdarkly/eb/ch21387/bootstrap-metadata
support extended bootstrap data format
2 parents 1cb4b79 + 4a188b1 commit aa78b6e

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

src/__tests__/LDClient-test.js

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ describe('LDClient', () => {
158158
requests[0].respond(404);
159159
});
160160

161-
it('should not fetch flag settings since bootstrap is provided', () => {
161+
it('should not fetch flag settings if bootstrap is provided', () => {
162162
LDClient.initialize(envName, user, {
163163
bootstrap: {},
164164
});
@@ -167,6 +167,38 @@ describe('LDClient', () => {
167167
expect(/sdk\/eval/.test(settingsRequest.url)).toEqual(false);
168168
});
169169

170+
it('sets flag values from bootstrap object with old format', () => {
171+
const client = LDClient.initialize(envName, user, {
172+
bootstrap: { foo: 'bar' },
173+
});
174+
175+
expect(client.variation('foo')).toEqual('bar');
176+
});
177+
178+
it('logs warning when bootstrap object uses old format', () => {
179+
LDClient.initialize(envName, user, {
180+
bootstrap: { foo: 'bar' },
181+
});
182+
183+
expect(warnSpy).toHaveBeenCalledWith(messages.bootstrapOldFormat());
184+
});
185+
186+
it('sets flag values from bootstrap object with new format', () => {
187+
const client = LDClient.initialize(envName, user, {
188+
bootstrap: { foo: 'bar', $flagsState: { foo: { version: 1 } } },
189+
});
190+
191+
expect(client.variation('foo')).toEqual('bar');
192+
});
193+
194+
it('does not log warning when bootstrap object uses new format', () => {
195+
LDClient.initialize(envName, user, {
196+
bootstrap: { foo: 'bar', $flagsState: { foo: { version: 1 } } },
197+
});
198+
199+
expect(warnSpy).not.toHaveBeenCalled();
200+
});
201+
170202
it('should contain package version', () => {
171203
// Arrange
172204
const version = LDClient.version;
@@ -420,13 +452,15 @@ describe('LDClient', () => {
420452
expect(e.user).toEqual(user);
421453
}
422454

423-
function expectFeatureEvent(e, key, value, variation, version, defaultVal) {
455+
function expectFeatureEvent(e, key, value, variation, version, defaultVal, trackEvents, debugEventsUntilDate) {
424456
expect(e.kind).toEqual('feature');
425457
expect(e.key).toEqual(key);
426458
expect(e.value).toEqual(value);
427459
expect(e.variation).toEqual(variation);
428460
expect(e.version).toEqual(version);
429461
expect(e.default).toEqual(defaultVal);
462+
expect(e.trackEvents).toEqual(trackEvents);
463+
expect(e.debugEventsUntilDate).toEqual(debugEventsUntilDate);
430464
}
431465

432466
it('sends an identify event at startup', done => {
@@ -513,6 +547,32 @@ describe('LDClient', () => {
513547

514548
server.respond();
515549
});
550+
551+
it('can get metadata for events from bootstrap object', done => {
552+
const ep = stubEventProcessor();
553+
const bootstrapData = {
554+
foo: 'bar',
555+
$flagsState: {
556+
foo: {
557+
variation: 1,
558+
version: 2,
559+
trackEvents: true,
560+
debugEventsUntilDate: 1000,
561+
},
562+
},
563+
};
564+
const client = LDClient.initialize(envName, user, { eventProcessor: ep, bootstrap: bootstrapData });
565+
566+
client.on('ready', () => {
567+
client.variation('foo', 'x');
568+
569+
expect(ep.events.length).toEqual(2);
570+
expectIdentifyEvent(ep.events[0], user);
571+
expectFeatureEvent(ep.events[1], 'foo', 'bar', 1, 2, 'x', true, 1000);
572+
573+
done();
574+
});
575+
});
516576
});
517577

518578
describe('event listening', () => {

src/index.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,38 @@ export function initialize(env, user, options = {}) {
2828
const events = options.eventProcessor || EventProcessor(eventsUrl, environment, options, emitter);
2929
const requestor = Requestor(baseUrl, environment, options.useReport);
3030
const seenRequests = {};
31-
let flags = typeof options.bootstrap === 'object' ? utils.transformValuesToVersionedValues(options.bootstrap) : {};
31+
let flags = typeof options.bootstrap === 'object' ? readFlagsFromBootstrap(options.bootstrap) : {};
3232
let goalTracker;
3333
let useLocalStorage;
3434
let goals;
3535
let subscribedToChangeEvents;
3636
let firstEvent = true;
3737

38+
function readFlagsFromBootstrap(data) {
39+
// If the bootstrap data came from an older server-side SDK, we'll have just a map of keys to values.
40+
// Newer SDKs that have an allFlagsState method will provide an extra "$flagsState" key that contains
41+
// the rest of the metadata we want. We do it this way for backward compatibility with older JS SDKs.
42+
const keys = Object.keys(data);
43+
const metadataKey = '$flagsState';
44+
const metadata = data[metadataKey];
45+
if (!metadata && keys.length) {
46+
console.warn(messages.bootstrapOldFormat());
47+
}
48+
const ret = {};
49+
keys.forEach(key => {
50+
if (key !== metadataKey) {
51+
let flag = { value: data[key] };
52+
if (metadata && metadata[key]) {
53+
flag = utils.extend(flag, metadata[key]);
54+
} else {
55+
flag.version = 0;
56+
}
57+
ret[key] = flag;
58+
}
59+
});
60+
return ret;
61+
}
62+
3863
function shouldEnqueueEvent() {
3964
return sendEvents && !doNotTrack();
4065
}

src/messages.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ export const invalidUser = function() {
3737
return 'Invalid user specified.' + docLink;
3838
};
3939

40+
export const bootstrapOldFormat = function() {
41+
return (
42+
'LaunchDarkly client was initialized with bootstrap data that did not include flag metadata. ' +
43+
'Events may not be sent correctly.' +
44+
docLink
45+
);
46+
};
47+
4048
export const deprecated = function(oldName, newName) {
4149
return '[LaunchDarkly] "' + oldName + '" is deprecated, please use "' + newName + '"';
4250
};

0 commit comments

Comments
 (0)