Skip to content

Commit

Permalink
feat: Adds support for REPORT. (#575)
Browse files Browse the repository at this point in the history
**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 <[email protected]>
  • Loading branch information
tanderson-ld and kinyoklion authored Sep 18, 2024
1 parent a55be06 commit 916b724
Show file tree
Hide file tree
Showing 25 changed files with 1,297 additions and 430 deletions.
26 changes: 22 additions & 4 deletions packages/sdk/browser/src/BrowserClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
AutoEnvAttributes,
base64UrlEncode,
LDClient as CommonClient,
DataSourcePaths,
Encoding,
LDClientImpl,
LDContext,
LDOptions,
Expand Down Expand Up @@ -33,11 +35,27 @@ export class BrowserClient extends LDClientImpl {
return base64UrlEncode(JSON.stringify(context), this.platform.encoding!);
}

override createStreamUriPath(context: LDContext) {
return `/eval/${this.clientSideId}/${this.encodeContext(context)}`;
override getStreamingPaths(): DataSourcePaths {
const parentThis = this;
return {
pathGet(encoding: Encoding, _plainContextString: string): string {
return `/eval/${parentThis.clientSideId}/${base64UrlEncode(_plainContextString, encoding)}`;
},
pathReport(_encoding: Encoding, _plainContextString: string): string {
return `/eval/${parentThis.clientSideId}`;
},
};
}

override createPollUriPath(context: LDContext): string {
return `/sdk/evalx/${this.clientSideId}/contexts/${this.encodeContext(context)}`;
override getPollingPaths(): DataSourcePaths {
const parentThis = this;
return {
pathGet(encoding: Encoding, _plainContextString: string): string {
return `/sdk/evalx/${parentThis.clientSideId}/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
},
pathReport(_encoding: Encoding, _plainContextString: string): string {
return `/sdk/evalx/${parentThis.clientSideId}/context`;
},
};
}
}
4 changes: 1 addition & 3 deletions packages/sdk/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@
"prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore",
"test": "jest",
"coverage": "yarn test --coverage",
"check": "yarn prettier && yarn lint && yarn build && yarn test",
"android": "yarn && yarn ./example && yarn build && (cd example/ && yarn android-release)",
"ios": "yarn && yarn ./example && yarn build && (cd example/ && yarn ios-go)"
"check": "yarn prettier && yarn lint && yarn build && yarn test"
},
"peerDependencies": {
"react": "*",
Expand Down
24 changes: 20 additions & 4 deletions packages/sdk/react-native/src/ReactNativeLDClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
base64UrlEncode,
BasicLogger,
ConnectionMode,
DataSourcePaths,
Encoding,
internal,
LDClientImpl,
type LDContext,
Expand Down Expand Up @@ -103,12 +105,26 @@ export default class ReactNativeLDClient extends LDClientImpl {
return base64UrlEncode(JSON.stringify(context), this.platform.encoding!);
}

override createStreamUriPath(context: LDContext) {
return `/meval/${this.encodeContext(context)}`;
override getStreamingPaths(): DataSourcePaths {
return {
pathGet(encoding: Encoding, _plainContextString: string): string {
return `/meval/${base64UrlEncode(_plainContextString, encoding)}`;
},
pathReport(_encoding: Encoding, _plainContextString: string): string {
return `/meval`;
},
};
}

override createPollUriPath(context: LDContext): string {
return `/msdk/evalx/contexts/${this.encodeContext(context)}`;
override getPollingPaths(): DataSourcePaths {
return {
pathGet(encoding: Encoding, _plainContextString: string): string {
return `/msdk/evalx/contexts/${base64UrlEncode(_plainContextString, encoding)}`;
},
pathReport(_encoding: Encoding, _plainContextString: string): string {
return `/msdk/evalx/context`;
},
};
}

override async setConnectionMode(mode: ConnectionMode): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions packages/sdk/react-native/src/platform/PlatformRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export default class PlatformRequests implements Requests {

createEventSource(url: string, eventSourceInitDict: EventSourceInitDict): EventSource {
return new RNEventSource<EventName>(url, {
method: eventSourceInitDict.method ?? 'GET',
headers: eventSourceInitDict.headers,
body: eventSourceInitDict.body,
retryAndHandleError: eventSourceInitDict.errorFilter,
logger: this.logger,
});
Expand Down
4 changes: 3 additions & 1 deletion packages/shared/common/src/api/platform/EventSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ export interface EventSource {
}

export interface EventSourceInitDict {
errorFilter: (err: HttpErrorResponse) => boolean;
method?: string;
headers: { [key: string]: string | string[] };
body?: string;
errorFilter: (err: HttpErrorResponse) => boolean;
initialRetryDelayMillis: number;
readTimeoutMillis: number;
retryResetIntervalMillis: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const reportJsonError = (
errorHandler?.(new LDStreamingError('Malformed JSON data in event stream'));
};

// TODO: SDK-156 - Move to Server SDK specific location
class StreamingProcessor implements LDStreamProcessor {
private readonly headers: { [key: string]: string | string[] };
private readonly streamUri: string;
Expand Down
29 changes: 23 additions & 6 deletions packages/shared/sdk-client/__tests__/LDClientImpl.events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AutoEnvAttributes,
ClientContext,
clone,
Encoding,
internal,
LDContext,
subsystem,
Expand All @@ -10,12 +11,12 @@ import {
createBasicPlatform,
createLogger,
MockEventProcessor,
setupMockStreamingProcessor,
} from '@launchdarkly/private-js-mocks';

import LDClientImpl from '../src/LDClientImpl';
import { Flags } from '../src/types';
import * as mockResponseJson from './evaluation/mockResponse.json';
import { MockEventSource } from './streaming/LDClientImpl.mocks';

type InputCustomEvent = internal.InputCustomEvent;
type InputIdentifyEvent = internal.InputIdentifyEvent;
Expand All @@ -36,7 +37,6 @@ jest.mock('@launchdarkly/js-sdk-common', () => {
...{
internal: {
...actual.internal,
StreamingProcessor: m.MockStreamingProcessor,
EventProcessor: m.MockEventProcessor,
},
},
Expand All @@ -45,6 +45,7 @@ jest.mock('@launchdarkly/js-sdk-common', () => {

const testSdkKey = 'test-sdk-key';
let ldc: LDClientImpl;
let mockEventSource: MockEventSource;
let defaultPutResponse: Flags;
const carContext: LDContext = { kind: 'car', key: 'test-car' };

Expand All @@ -66,15 +67,31 @@ describe('sdk-client object', () => {
sendEvent: mockedSendEvent,
}),
);
setupMockStreamingProcessor(false, defaultPutResponse);

const simulatedEvents = [{ data: JSON.stringify(defaultPutResponse) }];
mockPlatform.storage.get.mockImplementation(() => undefined);
mockPlatform.requests.createEventSource.mockImplementation(
(streamUri: string = '', options: any = {}) => {
mockEventSource = new MockEventSource(streamUri, options);
mockEventSource.simulateEvents('put', simulatedEvents);
return mockEventSource;
},
);

mockPlatform.crypto.randomUUID.mockReturnValue('random1');

ldc = new LDClientImpl(testSdkKey, AutoEnvAttributes.Enabled, mockPlatform, {
logger,
});
jest
.spyOn(LDClientImpl.prototype as any, 'createStreamUriPath')
.mockReturnValue('/stream/path');

jest.spyOn(LDClientImpl.prototype as any, 'getStreamingPaths').mockReturnValue({
pathGet(_encoding: Encoding, _plainContextString: string): string {
return '/stream/path';
},
pathReport(_encoding: Encoding, _plainContextString: string): string {
return '/stream/path';
},
});
});

afterEach(() => {
Expand Down
Loading

0 comments on commit 916b724

Please sign in to comment.