Skip to content

Commit 1bf9087

Browse files
authored
prepare 2.7.0 release (#114)
1 parent 3160bd4 commit 1bf9087

File tree

11 files changed

+273
-54
lines changed

11 files changed

+273
-54
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
All notable changes to the LaunchDarkly client-side JavaScript SDK will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org).
55

6+
## [2.7.0] - 2018-09-19
7+
### Added:
8+
- New client method `waitForInitialization` returns a Promise, like `waitUntilReady`; but while `waitUntilReady` will be resolved as soon as client initialization either succeeds or fails, `waitForInitialization` will be resolved only if initialization succeeds, and will be rejected (with an error object) if it fails.
9+
- New config option `fetchGoals` (default: true) allows you to control whether the client will request A/B testing parameters from LaunchDarkly. If you do not use A/B testing, you may wish to disable this to reduce the number of HTTP requests.
10+
- New config option `sendLDHeaders` (default: true) allows you to control whether the client will add a custom header to LaunchDarkly HTTP requests (to indicate the SDK version). You may wish to disable this behavior if you have performance concerns, as it causes browsers to make an additional CORS preflight check (since it is no longer a [simple request](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)).
11+
612
## [2.6.0] - 2018-09-07
713
### Added:
814
- The new configuration option `evaluationReasons` causes LaunchDarkly to report information about how each feature flag value was determined; you can access this information with the new client method `variationDetail`. The new method returns an object that contains both the flag value and a "reason" object which will tell you, for instance, if the user was individually targeted for the flag or was matched by one of the flag's rules, or if the flag returned the default value due to an error.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ldclient-js",
3-
"version": "2.6.0",
3+
"version": "2.7.0",
44
"description": "LaunchDarkly SDK for JavaScript",
55
"author": "LaunchDarkly <[email protected]>",
66
"license": "Apache-2.0",

src/EventProcessor.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@ import * as errors from './errors';
55
import * as messages from './messages';
66
import * as utils from './utils';
77

8-
export default function EventProcessor(eventsUrl, environmentId, options = {}, emitter = null, sender = null) {
8+
export default function EventProcessor(
9+
eventsUrl,
10+
environmentId,
11+
options = {},
12+
emitter = null,
13+
sender = null,
14+
sendLDHeaders
15+
) {
916
const processor = {};
10-
const eventSender = sender || EventSender(eventsUrl, environmentId);
17+
const eventSender = sender || EventSender(eventsUrl, environmentId, sendLDHeaders);
1118
const summarizer = EventSummarizer();
1219
const userFilter = UserFilter(options);
1320
const inlineUsers = !!options.inlineUsersInEvents;

src/EventSender.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as utils from './utils';
33

44
const MAX_URL_LENGTH = 2000;
55

6-
export default function EventSender(eventsUrl, environmentId, forceHasCors, imageCreator) {
6+
export default function EventSender(eventsUrl, environmentId, forceHasCors, imageCreator, sendLDHeaders = true) {
77
let hasCors;
88
const postUrl = eventsUrl + '/events/bulk/' + environmentId;
99
const imageUrl = eventsUrl + '/a/' + environmentId + '.gif';
@@ -36,7 +36,9 @@ export default function EventSender(eventsUrl, environmentId, forceHasCors, imag
3636
function createRequest(canRetry) {
3737
const xhr = new XMLHttpRequest();
3838
xhr.open('POST', postUrl, !sync);
39-
utils.addLDHeaders(xhr);
39+
if (sendLDHeaders) {
40+
utils.addLDHeaders(xhr);
41+
}
4042
xhr.setRequestHeader('Content-Type', 'application/json');
4143
xhr.setRequestHeader('X-LaunchDarkly-Event-Schema', '3');
4244
if (!sync) {

src/Requestor.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import * as messages from './messages';
44

55
const json = 'application/json';
66

7-
function fetchJSON(endpoint, body, callback) {
7+
function fetchJSON(endpoint, body, callback, sendLDHeaders) {
88
const xhr = new XMLHttpRequest();
9+
let data = undefined;
910

1011
xhr.addEventListener('load', () => {
1112
if (
@@ -26,14 +27,17 @@ function fetchJSON(endpoint, body, callback) {
2627
if (body) {
2728
xhr.open('REPORT', endpoint);
2829
xhr.setRequestHeader('Content-Type', 'application/json');
29-
utils.addLDHeaders(xhr);
30-
xhr.send(JSON.stringify(body));
30+
data = JSON.stringify(body);
3131
} else {
3232
xhr.open('GET', endpoint);
33+
}
34+
35+
if (sendLDHeaders) {
3336
utils.addLDHeaders(xhr);
34-
xhr.send();
3537
}
3638

39+
xhr.send(data);
40+
3741
return xhr;
3842
}
3943

@@ -45,7 +49,7 @@ function getResponseError(xhr) {
4549
}
4650
}
4751

48-
export default function Requestor(baseUrl, environment, useReport, withReasons) {
52+
export default function Requestor(baseUrl, environment, useReport, withReasons, sendLDHeaders) {
4953
let flagSettingsRequest;
5054
let lastFlagSettingsCallback;
5155

@@ -94,12 +98,12 @@ export default function Requestor(baseUrl, environment, useReport, withReasons)
9498
}
9599

96100
lastFlagSettingsCallback = cb;
97-
flagSettingsRequest = fetchJSON(endpoint, body, cb);
101+
flagSettingsRequest = fetchJSON(endpoint, body, cb, sendLDHeaders);
98102
};
99103

100104
requestor.fetchGoals = function(callback) {
101105
const endpoint = [baseUrl, '/sdk/goals/', environment].join('');
102-
fetchJSON(endpoint, null, callback);
106+
fetchJSON(endpoint, null, callback, sendLDHeaders);
103107
};
104108

105109
return requestor;

src/__tests__/EventSender-test.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,25 @@ describe('EventSender', () => {
131131
expect(JSON.parse(r.requestBody)).toEqual(events);
132132
});
133133

134-
it('should send custom user-agent header', () => {
134+
it('should send custom user-agent header when sendLDHeaders is true', () => {
135135
const sender = EventSender(eventsUrl, envId, true);
136136
const event = { kind: 'identify', key: 'userKey' };
137137
sender.sendEvents([event], false);
138138
lastRequest().respond();
139139
expect(lastRequest().requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString());
140140
});
141141

142+
it('should not send custom user-agent header when sendLDHeaders is false', () => {
143+
const forceHasCors = true;
144+
const imageCreator = undefined;
145+
const sendLDHeaders = false;
146+
const sender = EventSender(eventsUrl, envId, forceHasCors, imageCreator, sendLDHeaders);
147+
const event = { kind: 'identify', key: 'userKey' };
148+
sender.sendEvents([event], false);
149+
lastRequest().respond();
150+
expect(lastRequest().requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(undefined);
151+
});
152+
142153
const retryableStatuses = [400, 408, 429, 500, 503];
143154
for (const i in retryableStatuses) {
144155
const status = retryableStatuses[i];

src/__tests__/LDClient-test.js

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ describe('LDClient', () => {
5454
}, 0);
5555
});
5656

57+
it('should trigger the initialized event', done => {
58+
const handleReady = jest.fn();
59+
const client = LDClient.initialize(envName, user, {
60+
bootstrap: {},
61+
});
62+
63+
client.on('initialized', handleReady);
64+
65+
setTimeout(() => {
66+
expect(handleReady).toHaveBeenCalled();
67+
done();
68+
}, 0);
69+
});
70+
5771
it('should emit an error when an invalid samplingInterval is specified', done => {
5872
const client = LDClient.initialize(envName, user, {
5973
bootstrap: {},
@@ -82,6 +96,17 @@ describe('LDClient', () => {
8296
expect(err.message).toEqual('Error fetching flag settings: ' + messages.environmentNotFound());
8397
done();
8498
});
99+
client.waitForInitialization().catch(() => {}); // jest doesn't like unhandled rejections
100+
requests[0].respond(404);
101+
});
102+
103+
it('should emit a failure event when an invalid environment key is specified', done => {
104+
const client = LDClient.initialize('abc', user);
105+
client.on('failed', err => {
106+
expect(err.message).toEqual('Error fetching flag settings: ' + messages.environmentNotFound());
107+
done();
108+
});
109+
client.waitForInitialization().catch(() => {});
85110
requests[0].respond(404);
86111
});
87112

@@ -91,6 +116,7 @@ describe('LDClient', () => {
91116
expect(client.variation('flag-key', 1)).toEqual(1);
92117
done();
93118
});
119+
client.waitForInitialization().catch(() => {});
94120
requests[0].respond(404);
95121
});
96122

@@ -114,6 +140,21 @@ describe('LDClient', () => {
114140
expect(/sdk\/eval/.test(requests[0].url)).toEqual(false); // it's the goals request
115141
});
116142

143+
it('fetches goals if fetchGoals is unspecified', () => {
144+
LDClient.initialize(envName, user);
145+
expect(/sdk\/goals/.test(requests[1].url)).toEqual(true);
146+
});
147+
148+
it('fetches goals if fetchGoals is true', () => {
149+
LDClient.initialize(envName, user, { fetchGoals: true });
150+
expect(/sdk\/goals/.test(requests[1].url)).toEqual(true);
151+
});
152+
153+
it('does not fetch goals if fetchGoals is false', () => {
154+
LDClient.initialize(envName, user, { fetchGoals: false });
155+
expect(requests.length).toEqual(1);
156+
});
157+
117158
it('logs warning when bootstrap object uses old format', () => {
118159
LDClient.initialize(envName, user, {
119160
bootstrap: { foo: 'bar' },
@@ -167,10 +208,13 @@ describe('LDClient', () => {
167208
bootstrap: 'localstorage',
168209
});
169210

170-
client.on('ready', () => {
171-
expect(window.localStorage.getItem(lsKey)).toEqual(json);
172-
done();
173-
});
211+
client
212+
.waitForInitialization()
213+
.then(() => {
214+
expect(window.localStorage.getItem(lsKey)).toEqual(json);
215+
done();
216+
})
217+
.catch(() => {});
174218
});
175219

176220
it('should start with empty flags if we tried to use cached settings and there are none', done => {
@@ -349,6 +393,8 @@ describe('LDClient', () => {
349393
client.on('error', handleError);
350394
server.respond();
351395

396+
client.waitForInitialization().catch(() => {});
397+
352398
setTimeout(() => {
353399
expect(handleError).toHaveBeenCalled();
354400
done();
@@ -422,6 +468,33 @@ describe('LDClient', () => {
422468
});
423469
});
424470

471+
describe('waitForInitialization', () => {
472+
it('resolves promise on successful init', done => {
473+
const handleReady = jest.fn();
474+
const client = LDClient.initialize(envName, user, {
475+
bootstrap: {},
476+
});
477+
478+
client.waitForInitialization().then(handleReady);
479+
480+
client.on('ready', () => {
481+
setTimeout(() => {
482+
expect(handleReady).toHaveBeenCalled();
483+
done();
484+
}, 0);
485+
});
486+
});
487+
488+
it('rejects promise if flags request fails', done => {
489+
const client = LDClient.initialize('abc', user);
490+
client.waitForInitialization().catch(err => {
491+
expect(err.message).toEqual('Error fetching flag settings: ' + messages.environmentNotFound());
492+
done();
493+
});
494+
requests[0].respond(404);
495+
});
496+
});
497+
425498
describe('variation', () => {
426499
it('returns value for an existing flag - from bootstrap', () => {
427500
const client = LDClient.initialize(envName, user, {

src/__tests__/Requestor-test.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,21 +169,43 @@ describe('Requestor', () => {
169169
expect(handleFive.calledOnce).toEqual(true);
170170
});
171171

172-
it('should send custom user-agent header in GET mode', () => {
173-
const requestor = Requestor('http://requestee', 'FAKE_ENV', false);
172+
it('should send custom user-agent header in GET mode when sendLDHeaders is true', () => {
173+
const useReport = false;
174+
const withReasons = false;
175+
const sendLDHeaders = true;
176+
const requestor = Requestor('http://requestee', 'FAKE_ENV', useReport, withReasons, sendLDHeaders);
174177
const user = { key: 'foo' };
175178
requestor.fetchFlagSettings(user, 'hash1', sinon.spy());
176179

177180
expect(server.requests.length).toEqual(1);
178181
expect(server.requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString());
179182
});
180183

181-
it('should send custom user-agent header in REPORT mode', () => {
182-
const requestor = Requestor('http://requestee', 'FAKE_ENV', true);
184+
it('should send custom user-agent header in REPORT mode when sendLDHeaders is true', () => {
185+
const useReport = true;
186+
const withReasons = false;
187+
const sendLDHeaders = true;
188+
const requestor = Requestor('http://requestee', 'FAKE_ENV', useReport, withReasons, sendLDHeaders);
183189
const user = { key: 'foo' };
184190
requestor.fetchFlagSettings(user, 'hash1', sinon.spy());
185191

186192
expect(server.requests.length).toEqual(1);
187193
expect(server.requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(utils.getLDUserAgentString());
188194
});
195+
196+
it('should NOT send custom user-agent header when sendLDHeaders is false', () => {
197+
const baseUrl = 'http://requestee';
198+
const environment = 'FAKE_ENV';
199+
const useReport = true;
200+
const withReasons = false;
201+
const sendLDHeaders = false;
202+
203+
const requestor = Requestor(baseUrl, environment, useReport, withReasons, sendLDHeaders);
204+
const user = { key: 'foo' };
205+
206+
requestor.fetchFlagSettings(user, 'hash1', sinon.spy());
207+
208+
expect(server.requests.length).toEqual(1);
209+
expect(server.requests[0].requestHeaders['X-LaunchDarkly-User-Agent']).toEqual(undefined);
210+
});
189211
});

0 commit comments

Comments
 (0)