Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
33ac43a
First draft of polling implementation.
kinyoklion Jul 25, 2024
ec7883c
Correct streaming URL.
kinyoklion Jul 26, 2024
d5c0132
feat: Add connection mananger.
kinyoklion Jul 26, 2024
2aa885f
Implement tests.
kinyoklion Jul 29, 2024
2c896b9
Start implementation of the state detector.
kinyoklion Jul 29, 2024
3dd2808
wip
kinyoklion Jul 29, 2024
e8e44d3
Make enums backed by string values for easier logging.
kinyoklion Jul 29, 2024
f22eb37
Merge branch 'rlamb/sc-251453/add-connection-manager' into rlamb/sc-2…
kinyoklion Jul 29, 2024
b54463a
feat: Refactor application state handling.
kinyoklion Jul 29, 2024
3de892d
Merge branch 'feat/client-connection-handling-rework' into rlamb/sc-2…
kinyoklion Jul 29, 2024
359d71d
PR Cleanup
kinyoklion Jul 29, 2024
b68e4dd
Add some blank lines.
kinyoklion Jul 29, 2024
7cf3adf
Add not implemented comment.
kinyoklion Jul 29, 2024
9559afd
PR feedback
kinyoklion Jul 30, 2024
11af6b8
Merge branch 'rlamb/sc-251453/refactor-application-state-management' …
kinyoklion Jul 30, 2024
56144a6
Merge branch 'feat/client-connection-handling-rework' into rlamb/sc-2…
kinyoklion Jul 30, 2024
3e159f5
Make debug output type for connection mode more consistent.
kinyoklion Jul 30, 2024
efe76ea
Fix merge marker.
kinyoklion Jul 30, 2024
53c0afb
Implement polling tests.
kinyoklion Jul 31, 2024
08c4a64
Lint
kinyoklion Jul 31, 2024
b6c26cb
Refactoring.
kinyoklion Jul 31, 2024
fb6e265
Address todo
kinyoklion Jul 31, 2024
9b9d8a7
Linting
kinyoklion Jul 31, 2024
a515049
More lint.
kinyoklion Jul 31, 2024
20ba4ff
Add some internal tags.
kinyoklion Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/sdk/react-native/example/src/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function Welcome() {
.catch((e: any) => console.error(`error identifying ${userKey}: ${e}`));
};


const setConnectionMode = (m: ConnectionMode) => {
ldc.setConnectionMode(m);
};
Expand Down Expand Up @@ -56,7 +57,13 @@ export default function Welcome() {
style={styles.buttonContainer}
onPress={() => setConnectionMode('streaming')}
>
<Text style={styles.buttonText}>Set online</Text>
<Text style={styles.buttonText}>Set streaming</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonContainer}
onPress={() => setConnectionMode('polling')}
>
<Text style={styles.buttonText}>Set polling</Text>
</TouchableOpacity>
</View>
);
Expand Down
6 changes: 6 additions & 0 deletions packages/sdk/react-native/src/RNStateDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { AppState, AppStateStatus } from 'react-native';

import { ApplicationState, NetworkState, StateDetector } from './platform/ConnectionManager';

/**
* @internal
*/
function translateAppState(state: AppStateStatus): ApplicationState {
switch (state) {
case 'active':
Expand All @@ -14,6 +17,9 @@ function translateAppState(state: AppStateStatus): ApplicationState {
}
}

/**
* @internal
*/
export default class RNStateDetector implements StateDetector {
private applicationStateListener?: (state: ApplicationState) => void;
private networkStateListener?: (state: NetworkState) => void;
Expand Down
10 changes: 9 additions & 1 deletion packages/sdk/react-native/src/ReactNativeLDClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,16 @@ export default class ReactNativeLDClient extends LDClientImpl {
super.setConnectionMode(mode);
}

private encodeContext(context: LDContext) {
return base64UrlEncode(JSON.stringify(context), this.platform.encoding!);
}

override createStreamUriPath(context: LDContext) {
return `/meval/${base64UrlEncode(JSON.stringify(context), this.platform.encoding!)}`;
return `/meval/${this.encodeContext(context)}`;
}

override createPollUriPath(context: LDContext): string {
return `/msdk/evalx/contexts/${this.encodeContext(context)}`;
}

override async setConnectionMode(mode: ConnectionMode): Promise<void> {
Expand Down
18 changes: 18 additions & 0 deletions packages/sdk/react-native/src/platform/ConnectionManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ConnectionMode, LDLogger } from '@launchdarkly/js-client-sdk-common';

/**
* @internal
*/
export enum ApplicationState {
/// The application is in the foreground.
Foreground = 'foreground',
Expand All @@ -11,6 +14,9 @@ export enum ApplicationState {
Background = 'background',
}

/**
* @internal
*/
export enum NetworkState {
/// There is no network available for the SDK to use.
Unavailable = 'unavailable',
Expand All @@ -20,19 +26,28 @@ export enum NetworkState {
Available = 'available',
}

/**
* @internal
*/
export interface ConnectionDestination {
setNetworkAvailability(available: boolean): void;
setEventSendingEnabled(enabled: boolean, flush: boolean): void;
setConnectionMode(mode: ConnectionMode): Promise<void>;
}

/**
* @internal
*/
export interface StateDetector {
setApplicationStateListener(fn: (state: ApplicationState) => void): void;
setNetworkStateListener(fn: (state: NetworkState) => void): void;

stopListening(): void;
}

/**
* @internal
*/
export interface ConnectionManagerConfig {
/// The initial connection mode the SDK should use.
readonly initialConnectionMode: ConnectionMode;
Expand All @@ -51,6 +66,9 @@ export interface ConnectionManagerConfig {
readonly automaticBackgroundHandling: boolean;
}

/**
* @internal
*/
export class ConnectionManager {
private applicationState: ApplicationState = ApplicationState.Foreground;
private networkState: NetworkState = NetworkState.Available;
Expand Down
12 changes: 6 additions & 6 deletions packages/shared/sdk-client/src/LDClientImpl.storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const onChangePromise = () =>
});

// Common setup code for all tests
// 1. Sets up streamer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced all streamer with either updateProcessor or streaming.

// 1. Sets up streaming
// 2. Sets up the change listener
// 3. Runs identify
// 4. Get all flags
Expand All @@ -58,7 +58,7 @@ const identifyGetAllFlags = async (
}
jest.runAllTimers();

// if streamer errors, don't wait for 'change' because it will not be sent.
// if streaming errors, don't wait for 'change' because it will not be sent.
if (waitForChange && !shouldError) {
await changePromise;
}
Expand Down Expand Up @@ -91,8 +91,8 @@ describe('sdk-client storage', () => {
jest.resetAllMocks();
});

test('initialize from storage succeeds without streamer', async () => {
// make sure streamer errors
test('initialize from storage succeeds without streaming', async () => {
// make sure streaming errors
const allFlags = await identifyGetAllFlags(true, defaultPutResponse);

expect(basicPlatform.storage.get).toHaveBeenCalledWith('org:Testy Pizza');
Expand Down Expand Up @@ -185,7 +185,7 @@ describe('sdk-client storage', () => {
expect(emitter.emit).not.toHaveBeenCalled();
});

test('no storage, cold start from streamer', async () => {
test('no storage, cold start from streaming', async () => {
// fake previously cached flags even though there's no storage for this context
// @ts-ignore
ldc.flags = defaultPutResponse;
Expand All @@ -201,7 +201,7 @@ describe('sdk-client storage', () => {
JSON.stringify(defaultPutResponse),
);
expect(ldc.logger.debug).toHaveBeenCalledWith(
'OnIdentifyResolve no changes to emit from: streamer PUT.',
'OnIdentifyResolve no changes to emit from: stream PUT.',
);

// this is defaultPutResponse
Expand Down
6 changes: 3 additions & 3 deletions packages/shared/sdk-client/src/LDClientImpl.timeout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('sdk-client identify timeout', () => {
beforeEach(() => {
defaultPutResponse = clone<Flags>(mockResponseJson);

// simulate streamer error after a long timeout
// simulate streaming error after a long timeout
setupMockStreamingProcessor(true, defaultPutResponse, undefined, undefined, 30);

ldc = new LDClientImpl(testSdkKey, AutoEnvAttributes.Enabled, basicPlatform, {
Expand All @@ -50,14 +50,14 @@ describe('sdk-client identify timeout', () => {
jest.resetAllMocks();
});

// streamer is setup to error in beforeEach to cause a timeout
// streaming is setup to error in beforeEach to cause a timeout
test('rejects with default timeout of 5s', async () => {
jest.advanceTimersByTimeAsync(ldc.identifyTimeout * 1000).then();
await expect(ldc.identify(carContext)).rejects.toThrow(/identify timed out/);
expect(logger.error).toHaveBeenCalledWith(expect.stringMatching(/identify timed out/));
});

// streamer is setup to error in beforeEach to cause a timeout
// streaming is setup to error in beforeEach to cause a timeout
test('rejects with custom timeout', async () => {
const timeout = 15;
jest.advanceTimersByTimeAsync(timeout * 1000).then();
Expand Down
Loading