Skip to content

Commit 916b724

Browse files
feat: Adds support for REPORT. (#575)
**Requirements** - [x] I have added test coverage for new or changed functionality - [x] I have followed the repository's [pull request submission guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests) - [x] I have validated my changes against all supported platform versions **Related issues** 251674 **Describe the solution you've provided** - Added useReport to LDOptions. - Piped through client to `StreamingProcessor` constructor. - Cloned `StreamingProcessor` and associated tests from common-internal to sdk-client. Refactored it to support taking in a `StreamingDataSourceConfig` to narrow provided dependencies instead of ClientContext which contained many concerns. `StreamingProcessor` now uses a paths provider to form URI and internally decides where to put the serialized context (url vs body). - Refactored LDClientImpl tests to not use MockStreamingProcessor to reduce global mock usage. - fix: polling did not include the context in the body. **Describe alternatives you've considered** Initially tried making changes in common-internal, but the existence of the client context in the URL or body for client situations made that not ideal. --------- Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
1 parent a55be06 commit 916b724

25 files changed

+1297
-430
lines changed

packages/sdk/browser/src/BrowserClient.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {
22
AutoEnvAttributes,
33
base64UrlEncode,
44
LDClient as CommonClient,
5+
DataSourcePaths,
6+
Encoding,
57
LDClientImpl,
68
LDContext,
79
LDOptions,
@@ -33,11 +35,27 @@ export class BrowserClient extends LDClientImpl {
3335
return base64UrlEncode(JSON.stringify(context), this.platform.encoding!);
3436
}
3537

36-
override createStreamUriPath(context: LDContext) {
37-
return `/eval/${this.clientSideId}/${this.encodeContext(context)}`;
38+
override getStreamingPaths(): DataSourcePaths {
39+
const parentThis = this;
40+
return {
41+
pathGet(encoding: Encoding, _plainContextString: string): string {
42+
return `/eval/${parentThis.clientSideId}/${base64UrlEncode(_plainContextString, encoding)}`;
43+
},
44+
pathReport(_encoding: Encoding, _plainContextString: string): string {
45+
return `/eval/${parentThis.clientSideId}`;
46+
},
47+
};
3848
}
3949

40-
override createPollUriPath(context: LDContext): string {
41-
return `/sdk/evalx/${this.clientSideId}/contexts/${this.encodeContext(context)}`;
50+
override getPollingPaths(): DataSourcePaths {
51+
const parentThis = this;
52+
return {
53+
pathGet(encoding: Encoding, _plainContextString: string): string {
54+
return `/sdk/evalx/${parentThis.clientSideId}/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
55+
},
56+
pathReport(_encoding: Encoding, _plainContextString: string): string {
57+
return `/sdk/evalx/${parentThis.clientSideId}/context`;
58+
},
59+
};
4260
}
4361
}

packages/sdk/react-native/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434
"prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore",
3535
"test": "jest",
3636
"coverage": "yarn test --coverage",
37-
"check": "yarn prettier && yarn lint && yarn build && yarn test",
38-
"android": "yarn && yarn ./example && yarn build && (cd example/ && yarn android-release)",
39-
"ios": "yarn && yarn ./example && yarn build && (cd example/ && yarn ios-go)"
37+
"check": "yarn prettier && yarn lint && yarn build && yarn test"
4038
},
4139
"peerDependencies": {
4240
"react": "*",

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
base64UrlEncode,
55
BasicLogger,
66
ConnectionMode,
7+
DataSourcePaths,
8+
Encoding,
79
internal,
810
LDClientImpl,
911
type LDContext,
@@ -103,12 +105,26 @@ export default class ReactNativeLDClient extends LDClientImpl {
103105
return base64UrlEncode(JSON.stringify(context), this.platform.encoding!);
104106
}
105107

106-
override createStreamUriPath(context: LDContext) {
107-
return `/meval/${this.encodeContext(context)}`;
108+
override getStreamingPaths(): DataSourcePaths {
109+
return {
110+
pathGet(encoding: Encoding, _plainContextString: string): string {
111+
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
112+
},
113+
pathReport(_encoding: Encoding, _plainContextString: string): string {
114+
return `/meval`;
115+
},
116+
};
108117
}
109118

110-
override createPollUriPath(context: LDContext): string {
111-
return `/msdk/evalx/contexts/${this.encodeContext(context)}`;
119+
override getPollingPaths(): DataSourcePaths {
120+
return {
121+
pathGet(encoding: Encoding, _plainContextString: string): string {
122+
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
123+
},
124+
pathReport(_encoding: Encoding, _plainContextString: string): string {
125+
return `/msdk/evalx/context`;
126+
},
127+
};
112128
}
113129

114130
override async setConnectionMode(mode: ConnectionMode): Promise<void> {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ export default class PlatformRequests implements Requests {
1616

1717
createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource {
1818
return new RNEventSource<EventName>(url, {
19+
method: eventSourceInitDict.method ?? 'GET',
1920
headers: eventSourceInitDict.headers,
21+
body: eventSourceInitDict.body,
2022
retryAndHandleError: eventSourceInitDict.errorFilter,
2123
logger: this.logger,
2224
});

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ export interface EventSource {
1818
}
1919

2020
export interface EventSourceInitDict {
21-
errorFilter: (err: HttpErrorResponse) => boolean;
21+
method?: string;
2222
headers: { [key: string]: string | string[] };
23+
body?: string;
24+
errorFilter: (err: HttpErrorResponse) => boolean;
2325
initialRetryDelayMillis: number;
2426
readTimeoutMillis: number;
2527
retryResetIntervalMillis: number;

packages/shared/common/src/internal/stream/StreamingProcessor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const reportJsonError = (
2525
errorHandler?.(new LDStreamingError('Malformed JSON data in event stream'));
2626
};
2727

28+
// TODO: SDK-156 - Move to Server SDK specific location
2829
class StreamingProcessor implements LDStreamProcessor {
2930
private readonly headers: { [key: string]: string | string[] };
3031
private readonly streamUri: string;

packages/shared/sdk-client/__tests__/LDClientImpl.events.test.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
AutoEnvAttributes,
33
ClientContext,
44
clone,
5+
Encoding,
56
internal,
67
LDContext,
78
subsystem,
@@ -10,12 +11,12 @@ import {
1011
createBasicPlatform,
1112
createLogger,
1213
MockEventProcessor,
13-
setupMockStreamingProcessor,
1414
} from '@launchdarkly/private-js-mocks';
1515

1616
import LDClientImpl from '../src/LDClientImpl';
1717
import { Flags } from '../src/types';
1818
import * as mockResponseJson from './evaluation/mockResponse.json';
19+
import { MockEventSource } from './streaming/LDClientImpl.mocks';
1920

2021
type InputCustomEvent = internal.InputCustomEvent;
2122
type InputIdentifyEvent = internal.InputIdentifyEvent;
@@ -36,7 +37,6 @@ jest.mock('@launchdarkly/js-sdk-common', () => {
3637
...{
3738
internal: {
3839
...actual.internal,
39-
StreamingProcessor: m.MockStreamingProcessor,
4040
EventProcessor: m.MockEventProcessor,
4141
},
4242
},
@@ -45,6 +45,7 @@ jest.mock('@launchdarkly/js-sdk-common', () => {
4545

4646
const testSdkKey = 'test-sdk-key';
4747
let ldc: LDClientImpl;
48+
let mockEventSource: MockEventSource;
4849
let defaultPutResponse: Flags;
4950
const carContext: LDContext = { kind: 'car', key: 'test-car' };
5051

@@ -66,15 +67,31 @@ describe('sdk-client object', () => {
6667
sendEvent: mockedSendEvent,
6768
}),
6869
);
69-
setupMockStreamingProcessor(false, defaultPutResponse);
70+
71+
const simulatedEvents = [{ data: JSON.stringify(defaultPutResponse) }];
72+
mockPlatform.storage.get.mockImplementation(() => undefined);
73+
mockPlatform.requests.createEventSource.mockImplementation(
74+
(streamUri: string = '', options: any = {}) => {
75+
mockEventSource = new MockEventSource(streamUri, options);
76+
mockEventSource.simulateEvents('put', simulatedEvents);
77+
return mockEventSource;
78+
},
79+
);
80+
7081
mockPlatform.crypto.randomUUID.mockReturnValue('random1');
7182

7283
ldc = new LDClientImpl(testSdkKey, AutoEnvAttributes.Enabled, mockPlatform, {
7384
logger,
7485
});
75-
jest
76-
.spyOn(LDClientImpl.prototype as any, 'createStreamUriPath')
77-
.mockReturnValue('/stream/path');
86+
87+
jest.spyOn(LDClientImpl.prototype as any, 'getStreamingPaths').mockReturnValue({
88+
pathGet(_encoding: Encoding, _plainContextString: string): string {
89+
return '/stream/path';
90+
},
91+
pathReport(_encoding: Encoding, _plainContextString: string): string {
92+
return '/stream/path';
93+
},
94+
});
7895
});
7996

8097
afterEach(() => {

0 commit comments

Comments
 (0)