Skip to content

Commit 47b0945

Browse files
Todd AndersonTodd Anderson
authored andcommitted
chore: adds DataSource interface, CompositeDataSource, and FDv2 payload parsers.
1 parent 1519cb9 commit 47b0945

File tree

17 files changed

+1889
-87
lines changed

17 files changed

+1889
-87
lines changed

packages/sdk/browser/src/platform/DefaultBrowserEventSource.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import {
2+
DefaultBackoff,
23
EventListener,
34
EventName,
45
EventSourceInitDict,
56
HttpErrorResponse,
67
EventSource as LDEventSource,
78
} from '@launchdarkly/js-client-sdk-common';
89

9-
import Backoff from './Backoff';
10-
1110
/**
1211
* Implementation Notes:
1312
*
@@ -22,7 +21,7 @@ import Backoff from './Backoff';
2221
*/
2322
export default class DefaultBrowserEventSource implements LDEventSource {
2423
private _es?: EventSource;
25-
private _backoff: Backoff;
24+
private _backoff: DefaultBackoff;
2625
private _errorFilter: (err: HttpErrorResponse) => boolean;
2726

2827
// The type of the handle can be platform specific and we treat is opaquely.
@@ -34,7 +33,10 @@ export default class DefaultBrowserEventSource implements LDEventSource {
3433
private readonly _url: string,
3534
options: EventSourceInitDict,
3635
) {
37-
this._backoff = new Backoff(options.initialRetryDelayMillis, options.retryResetIntervalMillis);
36+
this._backoff = new DefaultBackoff(
37+
options.initialRetryDelayMillis,
38+
options.retryResetIntervalMillis,
39+
);
3840
this._errorFilter = options.errorFilter;
3941
this._openConnection();
4042
}

packages/sdk/browser/__tests__/platform/Backoff.test.ts renamed to packages/shared/common/__tests__/datasource/Backoff.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
import Backoff from '../../src/platform/Backoff';
1+
import { DefaultBackoff } from '../../src/datasource/Backoff';
22

33
const noJitter = (): number => 0;
44
const maxJitter = (): number => 1;
55
const defaultResetInterval = 60 * 1000;
66

77
it.each([1, 1000, 5000])('has the correct starting delay', (initialDelay) => {
8-
const backoff = new Backoff(initialDelay, defaultResetInterval, noJitter);
8+
const backoff = new DefaultBackoff(initialDelay, defaultResetInterval, noJitter);
99
expect(backoff.fail()).toEqual(initialDelay);
1010
});
1111

1212
it.each([1, 1000, 5000])('doubles delay on consecutive failures', (initialDelay) => {
13-
const backoff = new Backoff(initialDelay, defaultResetInterval, noJitter);
13+
const backoff = new DefaultBackoff(initialDelay, defaultResetInterval, noJitter);
1414
expect(backoff.fail()).toEqual(initialDelay);
1515
expect(backoff.fail()).toEqual(initialDelay * 2);
1616
expect(backoff.fail()).toEqual(initialDelay * 4);
1717
});
1818

1919
it('stops increasing delay when the max backoff is encountered', () => {
20-
const backoff = new Backoff(5000, defaultResetInterval, noJitter);
20+
const backoff = new DefaultBackoff(5000, defaultResetInterval, noJitter);
2121
expect(backoff.fail()).toEqual(5000);
2222
expect(backoff.fail()).toEqual(10000);
2323
expect(backoff.fail()).toEqual(20000);
2424
expect(backoff.fail()).toEqual(30000);
2525

26-
const backoff2 = new Backoff(1000, defaultResetInterval, noJitter);
26+
const backoff2 = new DefaultBackoff(1000, defaultResetInterval, noJitter);
2727
expect(backoff2.fail()).toEqual(1000);
2828
expect(backoff2.fail()).toEqual(2000);
2929
expect(backoff2.fail()).toEqual(4000);
@@ -33,12 +33,12 @@ it('stops increasing delay when the max backoff is encountered', () => {
3333
});
3434

3535
it('handles an initial retry delay longer than the maximum retry delay', () => {
36-
const backoff = new Backoff(40000, defaultResetInterval, noJitter);
36+
const backoff = new DefaultBackoff(40000, defaultResetInterval, noJitter);
3737
expect(backoff.fail()).toEqual(30000);
3838
});
3939

4040
it('jitters the backoff value', () => {
41-
const backoff = new Backoff(1000, defaultResetInterval, maxJitter);
41+
const backoff = new DefaultBackoff(1000, defaultResetInterval, maxJitter);
4242
expect(backoff.fail()).toEqual(500);
4343
expect(backoff.fail()).toEqual(1000);
4444
expect(backoff.fail()).toEqual(2000);
@@ -51,7 +51,7 @@ it.each([10 * 1000, 60 * 1000])(
5151
'resets the delay when the last successful connection was connected greater than the retry reset interval',
5252
(retryResetInterval) => {
5353
let time = 1000;
54-
const backoff = new Backoff(1000, retryResetInterval, noJitter);
54+
const backoff = new DefaultBackoff(1000, retryResetInterval, noJitter);
5555
expect(backoff.fail(time)).toEqual(1000);
5656
time += 1;
5757
backoff.success(time);
@@ -69,7 +69,7 @@ it.each([10 * 1000, 60 * 1000])(
6969
it.each([10 * 1000, 60 * 1000])(
7070
'does not reset the delay when the connection did not persist longer than the retry reset interval',
7171
(retryResetInterval) => {
72-
const backoff = new Backoff(1000, retryResetInterval, noJitter);
72+
const backoff = new DefaultBackoff(1000, retryResetInterval, noJitter);
7373

7474
let time = 1000;
7575
expect(backoff.fail(time)).toEqual(1000);
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { DataSourceList } from '../../src/datasource/dataSourceList';
2+
3+
it('replace is well behaved', async () => {
4+
const underTest = new DataSourceList(true, [1, 2, 3]);
5+
expect(underTest.next()).toEqual(1);
6+
expect(underTest.next()).toEqual(2);
7+
underTest.replace([4, 5, 6]);
8+
expect(underTest.next()).toEqual(4);
9+
expect(underTest.next()).toEqual(5);
10+
expect(underTest.next()).toEqual(6);
11+
expect(underTest.next()).toEqual(4);
12+
expect(underTest.next()).toEqual(5);
13+
});
14+
15+
it('it cycles correctly after replacing non-empty list', async () => {
16+
const underTest = new DataSourceList(true, [1, 2, 3]);
17+
expect(underTest.next()).toEqual(1);
18+
expect(underTest.next()).toEqual(2);
19+
expect(underTest.next()).toEqual(3);
20+
21+
underTest.remove(1);
22+
expect(underTest.next()).toEqual(2);
23+
expect(underTest.next()).toEqual(3);
24+
expect(underTest.next()).toEqual(2);
25+
expect(underTest.next()).toEqual(3);
26+
27+
underTest.remove(3);
28+
expect(underTest.next()).toEqual(2);
29+
expect(underTest.next()).toEqual(2);
30+
31+
underTest.remove(2);
32+
expect(underTest.next()).toBeUndefined();
33+
expect(underTest.next()).toBeUndefined();
34+
expect(underTest.next()).toBeUndefined();
35+
36+
underTest.replace([4, 5, 6]);
37+
38+
expect(underTest.next()).toEqual(4);
39+
expect(underTest.next()).toEqual(5);
40+
expect(underTest.next()).toEqual(6);
41+
42+
underTest.remove(4);
43+
expect(underTest.next()).toEqual(5);
44+
expect(underTest.next()).toEqual(6);
45+
expect(underTest.next()).toEqual(5);
46+
expect(underTest.next()).toEqual(6);
47+
48+
underTest.remove(6);
49+
expect(underTest.next()).toEqual(5);
50+
expect(underTest.next()).toEqual(5);
51+
52+
underTest.remove(5);
53+
expect(underTest.next()).toBeUndefined();
54+
expect(underTest.next()).toBeUndefined();
55+
});
56+
57+
it('cycles correctly after replacing empty list', async () => {
58+
const underTest = new DataSourceList<number>(true, []);
59+
60+
underTest.replace([1, 2, 3]);
61+
62+
expect(underTest.next()).toEqual(1);
63+
expect(underTest.next()).toEqual(2);
64+
expect(underTest.next()).toEqual(3);
65+
66+
underTest.remove(1);
67+
expect(underTest.next()).toEqual(2);
68+
expect(underTest.next()).toEqual(3);
69+
expect(underTest.next()).toEqual(2);
70+
expect(underTest.next()).toEqual(3);
71+
72+
underTest.remove(3);
73+
expect(underTest.next()).toEqual(2);
74+
expect(underTest.next()).toEqual(2);
75+
76+
underTest.remove(2);
77+
expect(underTest.next()).toBeUndefined();
78+
expect(underTest.next()).toBeUndefined();
79+
});
80+
81+
it('removing head is well behaved at start', async () => {
82+
const underTest = new DataSourceList(true, [1, 2, 3]);
83+
// head is now pointing to 1
84+
underTest.remove(1);
85+
expect(underTest.next()).toEqual(2);
86+
expect(underTest.next()).toEqual(3);
87+
expect(underTest.next()).toEqual(2);
88+
});
89+
90+
it('removing head is well behaved in middle', async () => {
91+
const underTest = new DataSourceList(true, [1, 2, 3]);
92+
expect(underTest.next()).toEqual(1);
93+
// head is now pointing to 2
94+
underTest.remove(2);
95+
expect(underTest.next()).toEqual(3);
96+
expect(underTest.next()).toEqual(1);
97+
expect(underTest.next()).toEqual(3);
98+
});
99+
100+
it('removing head is well behaved at end', async () => {
101+
const underTest = new DataSourceList(true, [1, 2, 3]);
102+
expect(underTest.next()).toEqual(1);
103+
expect(underTest.next()).toEqual(2);
104+
// head is now pointing to 3
105+
underTest.remove(3);
106+
expect(underTest.next()).toEqual(1);
107+
expect(underTest.next()).toEqual(2);
108+
expect(underTest.next()).toEqual(1);
109+
});
110+
111+
it('removing existing returns true', async () => {
112+
const underTest = new DataSourceList<number>(true, [1]);
113+
expect(underTest.remove(1)).toEqual(true);
114+
expect(underTest.next()).toBeUndefined();
115+
});
116+
117+
it('removing nonexistent returns false', async () => {
118+
const underTest = new DataSourceList<number>(true, []);
119+
expect(underTest.remove(1)).toEqual(false);
120+
expect(underTest.next()).toBeUndefined();
121+
});
122+
123+
it('single element removed and next called', async () => {
124+
const underTest = new DataSourceList<number>(true, [1]);
125+
expect(underTest.remove(1)).toEqual(true);
126+
expect(underTest.next()).toBeUndefined();
127+
});

0 commit comments

Comments
 (0)