Skip to content

Commit fe82500

Browse files
authored
feat: Add support for conditional event source capabilities. (#577)
This adds the ability to specify what capabilities an even source supports. The idea is, for something like report, the common code can check if the event source supports it. If it is configured, and the event source doesn't support it, then it can take appropriate action. The browser SDK can calculate this at runtime by checking for a polyfill and filling out the appropriate capabilities.
1 parent a2f4398 commit fe82500

File tree

11 files changed

+93
-0
lines changed

11 files changed

+93
-0
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ jest.mock('../src/platform', () => ({
4242
requests: {
4343
createEventSource: jest.fn(),
4444
fetch: jest.fn(),
45+
getEventSourceCapabilities: jest.fn(),
4546
},
4647
encoding: new PlatformEncoding(),
4748
storage: new PlatformStorage(logger),
@@ -70,6 +71,7 @@ it('uses correct default diagnostic url', () => {
7071
requests: {
7172
createEventSource: jest.fn(),
7273
fetch: mockedFetch,
74+
getEventSourceCapabilities: jest.fn(),
7375
},
7476
encoding: new PlatformEncoding(),
7577
storage: new PlatformStorage(logger),
@@ -97,6 +99,7 @@ it('uses correct default analytics event url', async () => {
9799
requests: {
98100
createEventSource: createMockEventSource,
99101
fetch: mockedFetch,
102+
getEventSourceCapabilities: jest.fn(),
100103
},
101104
encoding: new PlatformEncoding(),
102105
storage: new PlatformStorage(logger),
@@ -128,6 +131,7 @@ it('uses correct default polling url', async () => {
128131
requests: {
129132
createEventSource: jest.fn(),
130133
fetch: mockedFetch,
134+
getEventSourceCapabilities: jest.fn(),
131135
},
132136
encoding: new PlatformEncoding(),
133137
storage: new PlatformStorage(logger),
@@ -158,6 +162,7 @@ it('uses correct default streaming url', (done) => {
158162
requests: {
159163
createEventSource: mockedCreateEventSource,
160164
fetch: jest.fn(),
165+
getEventSourceCapabilities: jest.fn(),
161166
},
162167
encoding: new PlatformEncoding(),
163168
storage: new PlatformStorage(logger),
@@ -197,6 +202,7 @@ it('includes authorization header for polling', async () => {
197202
requests: {
198203
createEventSource: jest.fn(),
199204
fetch: mockedFetch,
205+
getEventSourceCapabilities: jest.fn(),
200206
},
201207
encoding: new PlatformEncoding(),
202208
storage: new PlatformStorage(logger),
@@ -231,6 +237,7 @@ it('includes authorization header for streaming', (done) => {
231237
requests: {
232238
createEventSource: mockedCreateEventSource,
233239
fetch: jest.fn(),
240+
getEventSourceCapabilities: jest.fn(),
234241
},
235242
encoding: new PlatformEncoding(),
236243
storage: new PlatformStorage(logger),

packages/sdk/react-native/src/platform/PlatformRequests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
EventName,
33
EventSource,
4+
EventSourceCapabilities,
45
EventSourceInitDict,
56
LDLogger,
67
Options,
@@ -21,6 +22,14 @@ export default class PlatformRequests implements Requests {
2122
});
2223
}
2324

25+
getEventSourceCapabilities(): EventSourceCapabilities {
26+
return {
27+
readTimeout: false,
28+
headers: true,
29+
customMethod: true,
30+
};
31+
}
32+
2433
fetch(url: string, options?: Options): Promise<Response> {
2534
// @ts-ignore
2635
return fetch(url, options);

packages/sdk/server-node/src/platform/NodeRequests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { HttpsProxyAgentOptions } from 'https-proxy-agent';
77
import { EventSource as LDEventSource } from 'launchdarkly-eventsource';
88

99
import {
10+
EventSourceCapabilities,
1011
LDLogger,
1112
LDProxyOptions,
1213
LDTLSOptions,
@@ -158,6 +159,14 @@ export default class NodeRequests implements platform.Requests {
158159
return new LDEventSource(url, expandedOptions);
159160
}
160161

162+
getEventSourceCapabilities(): EventSourceCapabilities {
163+
return {
164+
readTimeout: true,
165+
headers: true,
166+
customMethod: true,
167+
};
168+
}
169+
161170
usingProxy(): boolean {
162171
return this.hasProxy;
163172
}

packages/shared/akamai-edgeworker-sdk/src/platform/requests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Headers, NullEventSource } from '@launchdarkly/js-server-sdk-common';
33
import type {
44
EventSource,
5+
EventSourceCapabilities,
56
EventSourceInitDict,
67
Options,
78
Requests,
@@ -41,4 +42,12 @@ export default class EdgeRequests implements Requests {
4142
createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource {
4243
return new NullEventSource(url, eventSourceInitDict);
4344
}
45+
46+
getEventSourceCapabilities(): EventSourceCapabilities {
47+
return {
48+
readTimeout: false,
49+
headers: false,
50+
customMethod: false,
51+
};
52+
}
4453
}

packages/shared/common/src/api/platform/Requests.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,38 @@ export interface Options {
7878
timeout?: number;
7979
}
8080

81+
export interface EventSourceCapabilities {
82+
/**
83+
* If true the event source supports read timeouts. A read timeout for an
84+
* event source represents the maximum time between receiving any data.
85+
* If you receive 1 byte, and then a period of time greater than the read
86+
* time out elapses, and you do not receive a second byte, then that would
87+
* cause the event source to timeout.
88+
*
89+
* It is not a timeout for the read of the entire body, which should be
90+
* indefinite.
91+
*/
92+
readTimeout: boolean;
93+
94+
/**
95+
* If true the event source supports customized verbs POST/REPORT instead of
96+
* only the default GET.
97+
*/
98+
customMethod: boolean;
99+
100+
/**
101+
* If true the event source supports setting HTTP headers.
102+
*/
103+
headers: boolean;
104+
}
105+
81106
export interface Requests {
82107
fetch(url: string, options?: Options): Promise<Response>;
83108

84109
createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource;
85110

111+
getEventSourceCapabilities(): EventSourceCapabilities;
112+
86113
/**
87114
* Returns true if a proxy is configured.
88115
*/

packages/shared/mocks/src/platform.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const createBasicPlatform = () => ({
4949
requests: {
5050
fetch: jest.fn(),
5151
createEventSource: jest.fn(),
52+
getEventSourceCapabilities: jest.fn(),
5253
},
5354
storage: {
5455
get: jest.fn(),

packages/shared/sdk-client/__tests__/flag-manager/FlagPersistence.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ function makeMockPlatform(storage: Storage, crypto: Crypto): Platform {
329329
requests: {
330330
fetch: jest.fn(),
331331
createEventSource: jest.fn(),
332+
getEventSourceCapabilities: jest.fn(),
332333
},
333334
};
334335
}

packages/shared/sdk-client/__tests__/polling/PollingProcessot.test.ts renamed to packages/shared/sdk-client/__tests__/polling/PollingProcessor.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { waitFor } from '@testing-library/dom';
22

33
import {
44
EventSource,
5+
EventSourceCapabilities,
56
EventSourceInitDict,
67
Info,
78
PlatformData,
@@ -45,6 +46,13 @@ function makeRequests(): Requests {
4546
createEventSource(_url: string, _eventSourceInitDict: EventSourceInitDict): EventSource {
4647
throw new Error('Function not implemented.');
4748
},
49+
getEventSourceCapabilities(): EventSourceCapabilities {
50+
return {
51+
readTimeout: false,
52+
headers: false,
53+
customMethod: false,
54+
};
55+
},
4856
};
4957
}
5058

@@ -169,6 +177,13 @@ it('stops polling when stopped', (done) => {
169177
createEventSource(_url: string, _eventSourceInitDict: EventSourceInitDict): EventSource {
170178
throw new Error('Function not implemented.');
171179
},
180+
getEventSourceCapabilities(): EventSourceCapabilities {
181+
return {
182+
readTimeout: false,
183+
headers: false,
184+
customMethod: false,
185+
};
186+
},
172187
};
173188
const dataCallback = jest.fn();
174189
const errorCallback = jest.fn();

packages/shared/sdk-server-edge/src/platform/requests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NullEventSource } from '@launchdarkly/js-server-sdk-common';
22
import type {
33
EventSource,
4+
EventSourceCapabilities,
45
EventSourceInitDict,
56
Options,
67
Requests,
@@ -16,4 +17,12 @@ export default class EdgeRequests implements Requests {
1617
createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource {
1718
return new NullEventSource(url, eventSourceInitDict);
1819
}
20+
21+
getEventSourceCapabilities(): EventSourceCapabilities {
22+
return {
23+
readTimeout: false,
24+
headers: false,
25+
customMethod: false,
26+
};
27+
}
1928
}

packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ describe('given a requestor', () => {
7575
createEventSource(_url: string, _eventSourceInitDict: EventSourceInitDict): EventSource {
7676
throw new Error('Function not implemented.');
7777
},
78+
getEventSourceCapabilities() {
79+
throw new Error('Function not implemented.');
80+
},
7881
};
7982

8083
requestor = new Requestor(

0 commit comments

Comments
 (0)