Skip to content

Commit 824e1c7

Browse files
committed
feat: adds ping stream support
1 parent 35fa033 commit 824e1c7

File tree

16 files changed

+610
-238
lines changed

16 files changed

+610
-238
lines changed

packages/sdk/browser/__tests__/BrowserDataManager.test.ts

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,24 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
143143
browserConfig,
144144
() => ({
145145
pathGet(encoding: Encoding, _plainContextString: string): string {
146-
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
146+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
147147
},
148-
pathReport(_encoding: Encoding, _plainContextString: string): string {
149-
return `/msdk/evalx/context`;
148+
pathReport(encoding: Encoding, _plainContextString: string): string {
149+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
150+
},
151+
pathPing(encoding: Encoding, _plainContextString: string): string {
152+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
150153
},
151154
}),
152155
() => ({
153156
pathGet(encoding: Encoding, _plainContextString: string): string {
154-
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
157+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
158+
},
159+
pathReport(encoding: Encoding, _plainContextString: string): string {
160+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
155161
},
156-
pathReport(_encoding: Encoding, _plainContextString: string): string {
157-
return `/meval`;
162+
pathPing(encoding: Encoding, _plainContextString: string): string {
163+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
158164
},
159165
}),
160166
baseHeaders,
@@ -176,18 +182,24 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
176182
validateOptions({ streaming: true }, logger),
177183
() => ({
178184
pathGet(encoding: Encoding, _plainContextString: string): string {
179-
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
185+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
180186
},
181-
pathReport(_encoding: Encoding, _plainContextString: string): string {
182-
return `/msdk/evalx/context`;
187+
pathReport(encoding: Encoding, _plainContextString: string): string {
188+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
189+
},
190+
pathPing(encoding: Encoding, _plainContextString: string): string {
191+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
183192
},
184193
}),
185194
() => ({
186195
pathGet(encoding: Encoding, _plainContextString: string): string {
187-
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
196+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
197+
},
198+
pathReport(encoding: Encoding, _plainContextString: string): string {
199+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
188200
},
189-
pathReport(_encoding: Encoding, _plainContextString: string): string {
190-
return `/meval`;
201+
pathPing(encoding: Encoding, _plainContextString: string): string {
202+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
191203
},
192204
}),
193205
baseHeaders,
@@ -214,18 +226,24 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
214226
validateOptions({ streaming: true }, logger),
215227
() => ({
216228
pathGet(encoding: Encoding, _plainContextString: string): string {
217-
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
229+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
218230
},
219-
pathReport(_encoding: Encoding, _plainContextString: string): string {
220-
return `/msdk/evalx/context`;
231+
pathReport(encoding: Encoding, _plainContextString: string): string {
232+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
233+
},
234+
pathPing(encoding: Encoding, _plainContextString: string): string {
235+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
221236
},
222237
}),
223238
() => ({
224239
pathGet(encoding: Encoding, _plainContextString: string): string {
225-
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
240+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
241+
},
242+
pathReport(encoding: Encoding, _plainContextString: string): string {
243+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
226244
},
227-
pathReport(_encoding: Encoding, _plainContextString: string): string {
228-
return `/meval`;
245+
pathPing(encoding: Encoding, _plainContextString: string): string {
246+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
229247
},
230248
}),
231249
baseHeaders,
@@ -241,7 +259,7 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
241259
await dataManager.identify(identifyResolve, identifyReject, context, identifyOptions);
242260

243261
expect(platform.requests.createEventSource).toHaveBeenCalledWith(
244-
'/meval/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlciJ9?h=potato&withReasons=true',
262+
'/path/get/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlciJ9?h=potato&withReasons=true',
245263
expect.anything(),
246264
);
247265
});
@@ -255,18 +273,24 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
255273
validateOptions({ streaming: false }, logger),
256274
() => ({
257275
pathGet(encoding: Encoding, _plainContextString: string): string {
258-
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
276+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
259277
},
260-
pathReport(_encoding: Encoding, _plainContextString: string): string {
261-
return `/msdk/evalx/context`;
278+
pathReport(encoding: Encoding, _plainContextString: string): string {
279+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
280+
},
281+
pathPing(encoding: Encoding, _plainContextString: string): string {
282+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
262283
},
263284
}),
264285
() => ({
265286
pathGet(encoding: Encoding, _plainContextString: string): string {
266-
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
287+
return `/path/get/${base64UrlEncode(_plainContextString, encoding)}`;
288+
},
289+
pathReport(encoding: Encoding, _plainContextString: string): string {
290+
return `/path/report/${base64UrlEncode(_plainContextString, encoding)}`;
267291
},
268-
pathReport(_encoding: Encoding, _plainContextString: string): string {
269-
return `/meval`;
292+
pathPing(encoding: Encoding, _plainContextString: string): string {
293+
return `/path/ping/${base64UrlEncode(_plainContextString, encoding)}`;
270294
},
271295
}),
272296
baseHeaders,
@@ -282,7 +306,7 @@ describe('given a BrowserDataManager with mocked dependencies', () => {
282306
await dataManager.identify(identifyResolve, identifyReject, context, identifyOptions);
283307

284308
expect(platform.requests.fetch).toHaveBeenCalledWith(
285-
'/msdk/evalx/contexts/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlciJ9?withReasons=true&h=potato',
309+
'/path/get/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlciJ9?withReasons=true&h=potato',
286310
expect.anything(),
287311
);
288312
});

packages/sdk/browser/src/BrowserClient.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ export class BrowserClient extends LDClientImpl implements LDClient {
137137
pathReport(_encoding: Encoding, _plainContextString: string): string {
138138
return `/sdk/evalx/${clientSideId}/context`;
139139
},
140+
pathPing(_encoding: Encoding, _plainContextString: string): string {
141+
return `/ping/${clientSideId}`;
142+
},
140143
}),
141144
() => ({
142145
pathGet(encoding: Encoding, _plainContextString: string): string {
@@ -145,6 +148,9 @@ export class BrowserClient extends LDClientImpl implements LDClient {
145148
pathReport(_encoding: Encoding, _plainContextString: string): string {
146149
return `/eval/${clientSideId}`;
147150
},
151+
pathPing(_encoding: Encoding, _plainContextString: string): string {
152+
throw new Error('Ping for polling unsupported.'); // TODO: come back to think on this case more
153+
},
148154
}),
149155
baseHeaders,
150156
emitter,

packages/sdk/browser/src/BrowserDataManager.ts

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import {
66
DataSourcePaths,
77
DataSourceState,
88
FlagManager,
9-
getPollingUri,
109
internal,
1110
LDEmitter,
1211
LDHeaders,
1312
LDIdentifyOptions,
13+
makeRequestor,
1414
Platform,
15-
Requestor,
1615
} from '@launchdarkly/js-client-sdk-common';
1716

1817
import { readFlagsFromBootstrap } from './bootstrap';
@@ -92,22 +91,34 @@ export default class BrowserDataManager extends BaseDataManager {
9291
if (await this.flagManager.loadCached(context)) {
9392
this.debugLog('Identify - Flags loaded from cache. Continuing to initialize via a poll.');
9493
}
95-
const plainContextString = JSON.stringify(Context.toLDContext(context));
96-
const requestor = this.getRequestor(plainContextString);
97-
await this.finishIdentifyFromPoll(requestor, context, identifyResolve, identifyReject);
98-
}
9994

95+
await this.finishIdentifyFromPoll(context, identifyResolve, identifyReject);
96+
}
10097
this.updateStreamingState();
10198
}
10299

103100
private async finishIdentifyFromPoll(
104-
requestor: Requestor,
105101
context: Context,
106102
identifyResolve: () => void,
107103
identifyReject: (err: Error) => void,
108104
) {
109105
try {
110106
this.dataSourceStatusManager.requestStateUpdate(DataSourceState.Initializing);
107+
108+
const plainContextString = JSON.stringify(Context.toLDContext(context));
109+
const requestor = makeRequestor(
110+
plainContextString,
111+
this.config.serviceEndpoints,
112+
this.getPollingPaths(),
113+
this.platform.requests,
114+
this.platform.encoding!,
115+
this.baseHeaders,
116+
[],
117+
this.config.withReasons,
118+
this.config.useReport,
119+
this.secureModeHash,
120+
);
121+
111122
const payload = await requestor.requestPayload();
112123
try {
113124
const listeners = this.createStreamListeners(context, identifyResolve);
@@ -195,35 +206,23 @@ export default class BrowserDataManager extends BaseDataManager {
195206
const rawContext = Context.toLDContext(context)!;
196207

197208
this.updateProcessor?.close();
198-
this.createStreamingProcessor(rawContext, context, identifyResolve, identifyReject);
199-
200-
this.updateProcessor!.start();
201-
}
202209

203-
private getRequestor(plainContextString: string): Requestor {
204-
const paths = this.getPollingPaths();
205-
const path = this.config.useReport
206-
? paths.pathReport(this.platform.encoding!, plainContextString)
207-
: paths.pathGet(this.platform.encoding!, plainContextString);
208-
209-
const parameters: { key: string; value: string }[] = [];
210-
if (this.config.withReasons) {
211-
parameters.push({ key: 'withReasons', value: 'true' });
212-
}
213-
if (this.secureModeHash) {
214-
parameters.push({ key: 'h', value: this.secureModeHash });
215-
}
210+
const plainContextString = JSON.stringify(Context.toLDContext(context));
211+
const requestor = makeRequestor(
212+
plainContextString,
213+
this.config.serviceEndpoints,
214+
this.getPollingPaths(), // note: this is the polling path because the requestor is only used to make polling requests.
215+
this.platform.requests,
216+
this.platform.encoding!,
217+
this.baseHeaders,
218+
[],
219+
this.config.withReasons,
220+
this.config.useReport,
221+
this.secureModeHash,
222+
);
216223

217-
const headers: { [key: string]: string } = { ...this.baseHeaders };
218-
let body;
219-
let method = 'GET';
220-
if (this.config.useReport) {
221-
method = 'REPORT';
222-
headers['content-type'] = 'application/json';
223-
body = plainContextString; // context is in body for REPORT
224-
}
224+
this.createStreamingProcessor(rawContext, context, requestor, identifyResolve, identifyReject);
225225

226-
const uri = getPollingUri(this.config.serviceEndpoints, path, parameters);
227-
return new Requestor(this.platform.requests, uri, headers, method, body);
226+
this.updateProcessor!.start();
228227
}
229228
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ describe('given a MobileDataManager with mocked dependencies', () => {
132132
pathReport(_encoding: Encoding, _plainContextString: string): string {
133133
return `/msdk/evalx/context`;
134134
},
135+
pathPing(_encoding: Encoding, _plainContextString: string): string {
136+
return `/mping`; // TODO: test this out
137+
},
135138
}),
136139
() => ({
137140
pathGet(encoding: Encoding, _plainContextString: string): string {
@@ -140,6 +143,9 @@ describe('given a MobileDataManager with mocked dependencies', () => {
140143
pathReport(_encoding: Encoding, _plainContextString: string): string {
141144
return `/meval`;
142145
},
146+
pathPing(_encoding: Encoding, _plainContextString: string): string {
147+
throw new Error('Ping for polling unsupported.'); // TODO: come back to think on this case more
148+
},
143149
}),
144150
baseHeaders,
145151
emitter,

packages/sdk/react-native/src/MobileDataManager.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
LDEmitter,
1010
LDHeaders,
1111
LDIdentifyOptions,
12+
makeRequestor,
1213
Platform,
1314
} from '@launchdarkly/js-client-sdk-common';
1415

@@ -95,13 +96,38 @@ export default class MobileDataManager extends BaseDataManager {
9596
) {
9697
const rawContext = Context.toLDContext(context)!;
9798

99+
const plainContextString = JSON.stringify(Context.toLDContext(context));
100+
const requestor = makeRequestor(
101+
plainContextString,
102+
this.config.serviceEndpoints,
103+
this.getPollingPaths(), // note: this is the polling path because the requestor is only used to make polling requests.
104+
this.platform.requests,
105+
this.platform.encoding!,
106+
this.baseHeaders,
107+
[],
108+
this.config.useReport,
109+
this.config.withReasons,
110+
);
111+
98112
this.updateProcessor?.close();
99113
switch (this.connectionMode) {
100114
case 'streaming':
101-
this.createStreamingProcessor(rawContext, context, identifyResolve, identifyReject);
115+
this.createStreamingProcessor(
116+
rawContext,
117+
context,
118+
requestor,
119+
identifyResolve,
120+
identifyReject,
121+
);
102122
break;
103123
case 'polling':
104-
this.createPollingProcessor(rawContext, context, identifyResolve, identifyReject);
124+
this.createPollingProcessor(
125+
rawContext,
126+
context,
127+
requestor,
128+
identifyResolve,
129+
identifyReject,
130+
);
105131
break;
106132
default:
107133
break;

packages/sdk/react-native/src/ReactNativeLDClient.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export default class ReactNativeLDClient extends LDClientImpl {
8989
pathReport(_encoding: Encoding, _plainContextString: string): string {
9090
return `/msdk/evalx/context`;
9191
},
92+
pathPing(_encoding: Encoding, _plainContextString: string): string {
93+
return `/mping`; // TODO: test this out
94+
},
9295
}),
9396
() => ({
9497
pathGet(encoding: Encoding, _plainContextString: string): string {
@@ -97,6 +100,9 @@ export default class ReactNativeLDClient extends LDClientImpl {
97100
pathReport(_encoding: Encoding, _plainContextString: string): string {
98101
return `/meval`;
99102
},
103+
pathPing(_encoding: Encoding, _plainContextString: string): string {
104+
throw new Error('Ping for polling unsupported.'); // TODO: come back to think on this case more
105+
},
100106
}),
101107
baseHeaders,
102108
emitter,

0 commit comments

Comments
 (0)