launchdarkly-js-sdk-common 4.0.2 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/__tests__/LDClient-streaming-test.js +33 -0
- package/src/errors.js +2 -0
- package/src/index.js +23 -4
- package/src/messages.js +5 -0
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as messages from '../messages';
|
|
1
2
|
import * as utils from '../utils';
|
|
2
3
|
|
|
3
4
|
import { AsyncQueue, eventSink, sleepAsync, withCloseable } from 'launchdarkly-js-test-helpers';
|
|
@@ -650,6 +651,38 @@ describe('LDClient streaming', () => {
|
|
|
650
651
|
});
|
|
651
652
|
});
|
|
652
653
|
|
|
654
|
+
describe('emits error if malformed JSON is received', () => {
|
|
655
|
+
const doMalformedJsonEventTest = async (eventName, eventData) => {
|
|
656
|
+
// First, verify that there isn't an unhandled rejection if we're not listening for an error
|
|
657
|
+
await withClientAndServer({}, async client => {
|
|
658
|
+
await client.waitForInitialization();
|
|
659
|
+
client.setStreaming(true);
|
|
660
|
+
|
|
661
|
+
const stream = await expectStreamConnecting(fullStreamUrlWithUser);
|
|
662
|
+
stream.eventSource.mockEmit(eventName, { data: eventData });
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// Then, repeat the test using a listener to observe the error event
|
|
666
|
+
await withClientAndServer({}, async client => {
|
|
667
|
+
const errorEvents = new AsyncQueue();
|
|
668
|
+
client.on('error', e => errorEvents.add(e));
|
|
669
|
+
|
|
670
|
+
await client.waitForInitialization();
|
|
671
|
+
client.setStreaming(true);
|
|
672
|
+
|
|
673
|
+
const stream = await expectStreamConnecting(fullStreamUrlWithUser);
|
|
674
|
+
stream.eventSource.mockEmit(eventName, { data: eventData });
|
|
675
|
+
|
|
676
|
+
const e = await errorEvents.take();
|
|
677
|
+
expect(e.message).toEqual(messages.invalidData());
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
it('in put event', async () => doMalformedJsonEventTest('put', '{no'));
|
|
682
|
+
it('in patch event', async () => doMalformedJsonEventTest('patch', '{no'));
|
|
683
|
+
it('in delete event', async () => doMalformedJsonEventTest('delete', '{no'));
|
|
684
|
+
});
|
|
685
|
+
|
|
653
686
|
it('reconnects to stream if the user changes', async () => {
|
|
654
687
|
const user2 = { key: 'user2' };
|
|
655
688
|
const encodedUser2 = 'eyJrZXkiOiJ1c2VyMiJ9';
|
package/src/errors.js
CHANGED
|
@@ -18,6 +18,7 @@ const LDInvalidUserError = createCustomError('LaunchDarklyInvalidUserError');
|
|
|
18
18
|
const LDInvalidEventKeyError = createCustomError('LaunchDarklyInvalidEventKeyError');
|
|
19
19
|
const LDInvalidArgumentError = createCustomError('LaunchDarklyInvalidArgumentError');
|
|
20
20
|
const LDFlagFetchError = createCustomError('LaunchDarklyFlagFetchError');
|
|
21
|
+
const LDInvalidDataError = createCustomError('LaunchDarklyInvalidDataError');
|
|
21
22
|
|
|
22
23
|
function isHttpErrorRecoverable(status) {
|
|
23
24
|
if (status >= 400 && status < 500) {
|
|
@@ -32,6 +33,7 @@ module.exports = {
|
|
|
32
33
|
LDInvalidUserError,
|
|
33
34
|
LDInvalidEventKeyError,
|
|
34
35
|
LDInvalidArgumentError,
|
|
36
|
+
LDInvalidDataError,
|
|
35
37
|
LDFlagFetchError,
|
|
36
38
|
isHttpErrorRecoverable,
|
|
37
39
|
};
|
package/src/index.js
CHANGED
|
@@ -386,6 +386,14 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
386
386
|
if (!ident.getUser()) {
|
|
387
387
|
return;
|
|
388
388
|
}
|
|
389
|
+
const tryParseData = jsonData => {
|
|
390
|
+
try {
|
|
391
|
+
return JSON.parse(jsonData);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
emitter.maybeReportError(new errors.LDInvalidDataError(messages.invalidData()));
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
389
397
|
stream.connect(ident.getUser(), hash, {
|
|
390
398
|
ping: function() {
|
|
391
399
|
logger.debug(messages.debugStreamPing());
|
|
@@ -404,12 +412,20 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
404
412
|
});
|
|
405
413
|
},
|
|
406
414
|
put: function(e) {
|
|
407
|
-
const data =
|
|
415
|
+
const data = tryParseData(e.data);
|
|
416
|
+
if (!data) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
408
419
|
logger.debug(messages.debugStreamPut());
|
|
409
|
-
replaceAllFlags(data);
|
|
420
|
+
replaceAllFlags(data);
|
|
421
|
+
// Don't wait for this Promise to be resolved; note that replaceAllFlags is guaranteed
|
|
422
|
+
// never to have an unhandled rejection
|
|
410
423
|
},
|
|
411
424
|
patch: function(e) {
|
|
412
|
-
const data =
|
|
425
|
+
const data = tryParseData(e.data);
|
|
426
|
+
if (!data) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
413
429
|
// If both the flag and the patch have a version property, then the patch version must be
|
|
414
430
|
// greater than the flag version for us to accept the patch. If either one has no version
|
|
415
431
|
// then the patch always succeeds.
|
|
@@ -432,7 +448,10 @@ function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
|
|
|
432
448
|
}
|
|
433
449
|
},
|
|
434
450
|
delete: function(e) {
|
|
435
|
-
const data =
|
|
451
|
+
const data = tryParseData(e.data);
|
|
452
|
+
if (!data) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
436
455
|
if (!flags[data.key] || flags[data.key].version < data.version) {
|
|
437
456
|
logger.debug(messages.debugStreamDelete(data.key));
|
|
438
457
|
const mods = {};
|
package/src/messages.js
CHANGED
|
@@ -68,6 +68,10 @@ const invalidUser = function() {
|
|
|
68
68
|
return 'Invalid user specified.' + docLink;
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
const invalidData = function() {
|
|
72
|
+
return 'Invalid data received from LaunchDarkly; connection may have been interrupted';
|
|
73
|
+
};
|
|
74
|
+
|
|
71
75
|
const bootstrapOldFormat = function() {
|
|
72
76
|
return (
|
|
73
77
|
'LaunchDarkly client was initialized with bootstrap data that did not include flag metadata. ' +
|
|
@@ -201,6 +205,7 @@ module.exports = {
|
|
|
201
205
|
httpUnavailable,
|
|
202
206
|
identifyDisabled,
|
|
203
207
|
invalidContentType,
|
|
208
|
+
invalidData,
|
|
204
209
|
invalidKey,
|
|
205
210
|
invalidUser,
|
|
206
211
|
localStorageUnavailable,
|